JDK Proxy 使用步骤
Proxy#newProxyInstance() 方法用于创建 JDK Proxy,JDK Proxy 只能基于接口创建代理。
ClassLoader loader:代理类的类加载器。loader – the class loader to define the proxy class.
Class<?>[] interfaces:代理类需要实现的接口。interfaces – the list of interfaces for the proxy class to implement.
InvocationHandler h:h – the invocation handler to dispatch method invocations to. 在 InvocationHandler#invoke() 方法中编写代理实现逻辑。
JDK Proxy 的使用注意事项见方法上的 JavaDoc。
public class Proxy implements java.io.Serializable {
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) {
调用 Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), invocationHandler) 生成 userService 的代理对象,并将其强转为 UserService 类型。
public static void main(String[] args) {
System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
UserService userService = new UserServiceImpl();
InvocationHandlerImpl invocationHandler = new InvocationHandlerImpl(userService);
UserService userServiceProxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(), invocationHandler);
User user = userService.getUserById(1L);
System.out.println(user);
user = userServiceProxy.getUserById(2L);
System.out.println(user);
}
InvocationHandlerImpl 实现了 InvocationHandler 接口,在 invoke() 方法中编写代理逻辑,并通过反射调用 method.invoke(target, args) 方法。在 invoke() 方法中,你可以做任何想做的事情,你甚至可以不调用 target 类的方法。比如 MyBatis 中只需要定义 Mapper 接口, 而不需要定义任何 Mapper 接口的实现,自然也就不需要调用 target 类的方法。
@RequiredArgsConstructor
public class InvocationHandlerImpl implements InvocationHandler {
private final Object target;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("preparing to execute method");
// 如果使用 proxy 执行 invoke() 方法,会导致无限递归
// Object retVal = method.invoke(proxy, args);
Object retVal = method.invoke(target, args);
System.out.println("execute method finished");
return retVal;
}
}
UserServiceImpl 类:
public class UserServiceImpl implements UserService {
@Override
public User getUserById(Long id) {
System.out.println("querying user from db");
return User.of(id, "oneby" + id, new Date());
}
}
程序输出结果:userServiceProxy 执行前后输出了 invoke() 中的 sout 语句。
querying user from db
User(id=1, username=oneby1, createTime=Fri Apr 21 05:35:14 UTC 2023)
preparing to execute method
querying user from db
execute method finished
User(id=2, username=oneby2, createTime=Fri Apr 21 05:35:14 UTC 2023)
错误示范:千万不要写成 Object retVal = method.invoke(proxy, args),proxy 就是代理对象自己,自己调用自己的方法,会导致无限递归。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("preparing to execute method");
// 如果使用 proxy 执行 invoke() 方法,会导致无限递归
Object retVal = method.invoke(proxy, args);
// Object retVal = method.invoke(target, args);
System.out.println("execute method finished");
return retVal;
}
程序输出结果:无限递归,但为啥会输出 NoClassDefFoundError,不是 StackOverflowError 呢?
// 超级多的 preparing to execute method 输出
preparing to execute method
preparing to execute method
Exception in thread "main" java.lang.NoClassDefFoundError: Could not initialize class java.lang.reflect.UndeclaredThrowableException
at jdk.proxy1/jdk.proxy1.$Proxy0.getUserById(Unknown Source)
at com.oneby.spring.aop.proxy.jdk.JdkProxyApplication.main(JdkProxyApplication.java:21)
InvocationHandlerImpl#invoke() 方法的 proxy 参数是代理对象。
使用 JDK Proxy,UserServiceImpl 类的 this 就是 UserServiceImpl 实例,不是代理对象,等下一节讲 CGLib Proxy 的时候,this 可能会毁你三观。
在生成代理对象的代码之前, 加上这句 System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true"),可以在工程的 jdk 目录下生成 JDK Proxy 对应的 Class 文件。
public final class $Proxy0 extends Proxy implements UserService {
private static final Method m0;
private static final Method m1;
private static final Method m2;
private static final Method m3;
public $Proxy0(InvocationHandler param1) {
super(var1);
}
public final int hashCode() {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final boolean equals(Object var1) {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final User getUserById(Long var1) {
try {
return (User)super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.oneby.spring.aop.proxy.jdk.UserService").getMethod("getUserById", Class.forName("java.lang.Long"));
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
private static Lookup proxyClassLookup(Lookup var0) throws IllegalAccessException {
if (var0.lookupClass() == Proxy.class && var0.hasFullPrivilegeAccess()) {
return MethodHandles.lookup();
} else {
throw new IllegalAccessException(var0.toString());
}
}
}
$Proxy0 是 UserServiceImpl 的代理类,类中定义了四个静态字段,分别是 m0、m1、m2、m3。
public final class $Proxy0 extends Proxy implements UserService {
private static final Method m0;
private static final Method m1;
private static final Method m2;
private static final Method m3;
这四个静态字段分别对应四个方法,分别是:Object#hashCode()、Object#equals()、Object#toString()、UserService#getUserById()。
static {
try {
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.oneby.spring.aop.proxy.jdk.UserService").getMethod("getUserById", Class.forName("java.lang.Long"));
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
执行 m0、m1、m2、m3 方法时,会执行 super.h.invoke() 方法,所以 Object#hashCode()、Object#equals()、Object#toString()、UserService#getUserById() 方法都会被代理。
JDK Proxy 只能基于接口创建代理,但是官方大哥们考虑到了 Object 类中定义的 Object#hashCode()、Object#equals()、Object#toString() 方法也有可能需要被代理,也默认创建了这三个方法的代理,所以在使用 JDK Proxy 代理的时候可能需要特殊处理这三个方法。
JDK Proxy 的执行原理与效率:InvocationHandler#invoke() 方法的 prototype 为 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable,在 invoke() 方法中我们只能使用参数中的 Method method 使用反射来执行 target 对象中的方法(如果需要的话),所以 JDK Proxy 的效率总体上不如 CGLib Proxy,至于为什么,下一节讲了知道了。
public final int hashCode() {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final boolean equals(Object var1) {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final User getUserById(Long var1) {
try {
return (User)super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}