JDK动态代理通过Proxy.newProxyInstance()基于接口运行时生成代理类实现AOP,需传入ClassLoader、接口数组和InvocationHandler;仅支持接口代理,不支持类、static/final/private方法,代理对象只能强转为接口类型。

Java 中通过 Proxy.newProxyInstance() 创建动态代理,是实现轻量级 AOP(面向切面编程)的核心机制之一。它不修改原始类字节码,也不依赖第三方框架(如 Spring AOP 的 CGLIB),而是基于接口在运行时生成代理类,拦截方法调用并织入横切逻辑(如日志、权限、事务等)。
前提:只能代理接口,不能代理具体类
Proxy.newProxyInstance() 要求目标对象必须实现至少一个接口。JDK 动态代理底层基于反射和 java.lang.reflect.Proxy 类,生成的代理类继承自 Proxy 并实现指定接口——这是 JVM 的限制,无法绕过。
- 如果你只有类没有接口,需先提取接口,或改用 CGLIB(但那是另一套机制)
- 代理对象类型是接口类型,不是原实现类类型,调用时需按接口引用操作
三要素:ClassLoader、接口数组、InvocationHandler
调用 Proxy.newProxyInstance() 必须传入三个参数:
-
ClassLoader:用于加载代理类,通常用目标对象的类加载器(
target.getClass().getClassLoader()) -
Class[] interfaces:要代理的接口列表,如
new Class[]{Service.class} -
InvocationHandler:真正执行拦截逻辑的处理器,所有方法调用最终都转给它的
invoke()方法
示例代码结构:
立即学习“Java免费学习笔记(深入)”;
Service service = new ServiceImpl();
Service proxy = (Service) Proxy.newProxyInstance(
service.getClass().getClassLoader(),
service.getClass().getInterfaces(),
new LoggingInvocationHandler(service)
);
proxy.doSomething(); // 触发 invoke()
核心:编写 InvocationHandler 实现横切逻辑
InvocationHandler 是 AOP 的“切面”载体。在 invoke() 中,你可以:
- 在目标方法执行前插入逻辑(如记录开始时间、校验权限)
- 通过
method.invoke(target, args)调用原始方法(注意处理异常) - 在方法返回后或抛出异常时执行收尾(如打印耗时、回滚事务、记录结果)
一个典型日志增强的 InvocationHandler:
class LoggingInvocationHandler implements InvocationHandler {
private final Object target;
<pre class='brush:java;toolbar:false;'>LoggingInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("【前置】调用 " + method.getName());
long start = System.currentTimeMillis();
try {
Object result = method.invoke(target, args);
long cost = System.currentTimeMillis() - start;
System.out.println("【后置】" + method.getName() + " 执行完成,耗时 " + cost + "ms");
return result;
} catch (Exception e) {
System.out.println("【异常】" + method.getName() + " 抛出 " + e.getClass().getSimpleName());
throw e;
}
}}
注意事项与常见陷阱
- 不能代理 static/final/private 方法:这些方法无法被接口声明,自然不会被代理拦截
-
代理对象无法强转为实现类类型:只能转为接口类型,否则抛
ClassCastException - 同一个 InvocationHandler 实例可复用,但要注意其中是否持有非线程安全的状态(如共享字段)
- 性能开销存在但可控:每次调用多一层反射,对高频方法建议压测评估


















