一、序言
今天和大家聊聊 JDK 动态代理。
二、什么是 JDK 动态代理
在 Java 中,动态代理是一种强大的机制,允许在运行时创建代理类和对象,以拦截并处理对目标对象的方法调用。这种机制为实现各种横切关注点(cross-cutting concerns)提供了灵活且强大的手段,如日志记录、性能监控、事务管理等。
在 JDK 中,动态代理的实现依赖于 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler接口。通过 Proxy 类的 newProxyInstance 方法,我们可以动态地创建代理对象,并为其指定一个 InvocationHandler 对象,用于处理代理对象上的方法调用。
动态代理的核心在于 InvocationHandler 接口,它定义了一个方法 invoke,用于处理代理对象上的方法调用。通过实现 InvocationHandler 接口,我们可以在代理对象的方法调用前后执行额外的逻辑,如日志记录、权限检查等。这种方式使得我们能够将横切关注点与业务逻辑分离,提高了代码的可维护性和可扩展性。
总的来说,JDK 动态代理为 Java 开发者提供了一种优雅且灵活的方式来实现 AOP 和其他横切关注点,使得代码更加模块化、可重用和易于理解。
三、JDK 动态代理实践
现假设一个业务场景:我们需要在 UserService 中方法记录一些自定义的日志信息。
-
需要代理的类
// 接口 public interface UserService { User getUser(int userId); } // 实现类 public class UserServiceImpl implements UserService { @Override public User getUser(int userId) { // 具体业务逻辑... } } -
使用 JDK 代理完成对上述类中方法的日志信息代理
public class LogDynamicProxy implements InvocationHandler { // 需要代理的目标对象 private Object target; // 构造器 public LogDynamicProxy(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 定义代理之后的结果变量 Object result; // 执行被代理对象原始的方法 result = method.invoke(target, args); // 记录日志(自行定义记录日志的业务逻辑) System.out.println("记录日志..."); // 返回代理之后的结果 return result; } } -
使用代理对象
public void test() { // 实例化 UserService UserService userService = new UserServiceImpl(); // 获取 InvocationHandler InvocationHandler handler = new LogDynamicProxy(userService); // 获取 UserService 的代理对象 Object o = Proxy.newProxyInstance( userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), handler ); // 将 UserService 的代理对象转换成 UserService UserService proxy = (UserService) o; // 执行 UserService 代理之后的方法 User user = proxy.getUser(1); }
通过 JDK 动态代理,我们发现:记录日志的操作不需要写在 UserService 的实现类中(记录日志的代码和业务逻辑的代码实现了解耦)。
四、JDK 动态代理工作流程

由上面的示例我们可以分析出,JDK 代理的工作流程为:
- 需要被代理的对象将需要代理的操作(例如:记录日志)委托给 InvocationHandler
- Proxy 类根据需要被代理的对象和委托的 InvocationHandler 生成具体的代理实例
- 最后,执行代理实例中的代理方法
五、JDK 动态代理源码分析
5.1 InvocationHandler 接口
public interface InvocationHandler {
// proxy:代理对象本身,可以使用它来调用被代理对象的方法
// method:被代理的方法对象,你可以通过它获取方法的名称、参数等信息
// args:方法调用时传入的参数数组
// throws Throwable:这个方法声明了可能抛出 Throwable 类型的异常,表示在方法调用过程中可能会出现的异常
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
上述代码是 InvocationHandler 接口的源码,其内容只有一个 invoke() 方法。
5.2 Proxy#newProxyInstance()
// 这是一个静态方法,用于创建动态代理对象。它接收三个参数
// loader:一个 ClassLoader 对象,用于加载代理类
// interfaces:一个 Class 对象数组,表示被代理对象实现的接口
// h:一个 InvocationHandler 对象,用于处理代理对象的方法调用
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
// 检查传入的 InvocationHandler 对象是否为 null
// 如果是,则抛出 NullPointerException 异常
Objects.requireNonNull(h);
// 复制传入的接口数组,以防止对原始数组进行修改
final Class<?>[] intfs = interfaces.clone();
// 获取当前的安全管理器
final SecurityManager sm = System.getSecurityManager();
// 如果存在安全管理器,则检查对代理类的访问权限
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
// 获取代理类。这个方法会根据传入的类加载器和接口数组来查找或生成代理类
Class<?> cl = getProxyClass0(loader, intfs);
try {
// 如果存在安全管理器,则检查对代理类的创建权限
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
// 通过反射获取代理类的构造函数
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
// 如果代理类的构造函数不是公共的,则通过特权操作设置其可访问性
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
// 调用构造函数创建代理对象
// 传入 InvocationHandler 对象作为参数
// 返回新创建的代理对象
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
以上是 Proxy#newProxyInstance()方法的源码。我们可以分析出其核心流程为:

- 环境检查:在创建代理对象之前,会进行一系列的环境检查,包括权限检查等。
- 生成代理类:通过
getProxyClass0()方法生成代理类。该方法根据传入的类加载器和接口数组,查找或者生成代理类。 - 生成代理对象:根据生成的代理类来创建代理对象。代理对象实现了指定的接口,并将方法调用委托给指定的 InvocationHandler 处理器。
六、FAQ
6.1 JDK 动态代理为什么必须实现接口
public final class $Proxy extends Proxy implements UserService {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final User getUser(int var1) throws {
try {
return (User)super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
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 int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("com.foxconn.service.UserService").getMethod("getUser", Integer.TYPE);
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
上面的代码是 JDK 动态代理生成的代码。可以发现生成的代理类默认继承了 Proxy 类。 在 Java 中,一个类只能继承自一个父类,但是可以实现多个接口。这就意味着如果要创建一个动态代理类,只能通过接口去实现而不能使用类去继承。
1万+



被折叠的 条评论
为什么被折叠?



