Java原生动态代理要求目标类必须实现接口,通过Proxy.newProxyInstance在运行时生成代理对象,结合InvocationHandler统一拦截方法调用并增强逻辑,但无法代理无接口的类或private/static/final方法。

Java原生动态代理是实现轻量级AOP最直接的方式,不依赖Spring等框架,核心在于利用java.lang.reflect.Proxy和InvocationHandler在运行时生成代理对象,对目标方法进行拦截与增强。关键前提是:目标类必须实现接口。
目标类需基于接口定义
动态代理要求被代理对象有明确的接口契约,这是JDK代理的硬性限制。例如定义一个用户服务接口:
// 接口定义
public interface UserService {<br>
void addUser(String name);<br>
void deleteUser(Long id);<br>
}
// 实现类(纯业务逻辑,不掺杂日志、统计等横切代码)
public class UserServiceImpl implements UserService {<br>
@Override<br>
public void addUser(String name) {<br>
System.out.println("保存用户:" + name);<br>
}<br>
@Override<br>
public void deleteUser(Long id) {<br>
System.out.println("删除ID为 " + id + " 的用户");<br>
}<br>
}
编写统一的切面处理器
所有增强逻辑(如日志、耗时统计、权限检查)都集中在InvocationHandler实现类中,避免重复编码:
- 构造时传入真实目标对象(如
new UserServiceImpl()) -
invoke()方法会在每次调用代理对象方法时触发 - 可在
method.invoke(target, args)前后插入任意逻辑 - 支持按方法名、注解或参数类型做差异化处理
public class LoggingInvocationHandler implements InvocationHandler {<br>
private final Object target;<br><br>
public LoggingInvocationHandler(Object target) {<br>
this.target = target;<br>
}<br><br>
@Override<br>
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {<br>
System.out.println("[前置] 正在执行 " + method.getName());<br>
long start = System.nanoTime();<br><br>
Object result = method.invoke(target, args);<br><br>
long cost = (System.nanoTime() - start) / 1_000_000;<br>
System.out.println("[后置] " + method.getName() + " 执行完毕,耗时 " + cost + "ms");<br>
return result;<br>
}<br>
}
通过Proxy.newProxyInstance创建代理实例
这是生成代理对象的唯一入口,需提供三要素:
立即学习“Java免费学习笔记(深入)”;
- 类加载器:通常用目标对象的
getClass().getClassLoader() - 接口数组:
target.getClass().getInterfaces(),确保代理能转型为原接口 - 处理器实例:即上面写的
LoggingInvocationHandler
UserService service = new UserServiceImpl();<br>
InvocationHandler handler = new LoggingInvocationHandler(service);<br>
UserService proxy = (UserService) Proxy.newProxyInstance(<br>
service.getClass().getClassLoader(),<br>
service.getClass().getInterfaces(),<br>
handler<br>
);<br>
proxy.addUser("张三"); // 控制台输出日志+耗时
注意事项与局限性
JDK原生动态代理虽简洁,但有明确边界:
- 只能代理接口,无法代理没有接口的普通类(此时需CGLIB)
- 代理对象是接口类型,不能强转为实现类类型
- 无法拦截
private、static或final方法 - 所有被代理接口方法都会经过
invoke(),需自行判断是否需要增强



















