Java动态代理通过Proxy和InvocationHandler实现反射拦截:运行时为接口生成代理类,所有方法调用统一交由invoke()处理,利用method.invoke()反射执行目标方法,并在前后插入日志、权限等横切逻辑。

Java 中利用 JDK 动态代理结合反射实现方法拦截,核心是通过 java.lang.reflect.Proxy 创建代理对象,并用 InvocationHandler 拦截所有接口方法调用,在其中使用反射获取方法元信息、参数、注解等,进而实现日志、权限、事务等横切逻辑。
一、动态代理的前提:必须基于接口
JDK 动态代理只能为实现了接口的类生成代理。它在运行时生成一个实现了相同接口的代理类(如 $Proxy0),而不是修改目标类本身。
所以你需要:
- 定义一个接口(例如
UserService) - 提供一个实现类(例如
UserServiceImpl) - 不直接使用实现类,而是通过代理对象调用其接口方法
二、编写 InvocationHandler 实现拦截逻辑
这是拦截的核心。你需要重写 invoke 方法,在其中访问被调用的方法、参数、返回值,甚至可修改行为:
立即学习“Java免费学习笔记(深入)”;
<font color="#888">// 示例:带日志和参数打印的通用处理器</font>
public class LoggingInvocationHandler implements InvocationHandler {
private final Object target; // 被代理的真实对象
<pre class='brush:java;toolbar:false;'>public LoggingInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("【开始调用】" + method.getDeclaringClass().getSimpleName()
+ "." + method.getName());
// 利用反射获取方法上的注解(比如自定义 @NeedAuth)
if (method.isAnnotationPresent(NeedAuth.class)) {
System.out.println("→ 检测到 @NeedAuth,执行鉴权逻辑...");
// 这里可抛出异常或跳过执行
}
// 打印参数类型与值(反射获取参数名需 Java 8+ -parameters 编译选项)
Parameter[] parameters = method.getParameters();
for (int i = 0; i < args.length; i++) {
System.out.printf(" 参数 %s(%s) = %s%n",
parameters[i].getName(),
parameters[i].getType().getSimpleName(),
args[i]);
}
long start = System.currentTimeMillis();
Object result = method.invoke(target, args); // 反射调用原方法
long cost = System.currentTimeMillis() - start;
System.out.println("【执行完成】耗时 " + cost + "ms,返回值:" + result);
return result;
}}
三、创建代理实例
使用 Proxy.newProxyInstance 创建代理对象。注意三个参数:
-
ClassLoader:一般用目标对象的类加载器(
target.getClass().getClassLoader()) -
Class>[] interfaces:目标对象实现的所有接口(可用
target.getClass().getInterfaces()) - InvocationHandler:上面写的处理器实例
<font color="#888">// 使用示例</font>
UserService service = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
service.getClass().getClassLoader(),
service.getClass().getInterfaces(),
new LoggingInvocationHandler(service)
);
<p>proxy.getUser(123L); // 此时会触发 invoke 方法四、进阶技巧:结合反射做更灵活的拦截
你可以在 invoke 中进一步利用反射能力:
- 通过
method.getReturnType()判断是否需要包装返回值(如统一封装成Result<t></t>) - 用
method.getAnnotation(Class<t>)</t>提取自定义注解配置(如超时时间、缓存 Key 表达式) - 调用
method.invoke(...)前后插入 try-catch,实现异常统一处理或降级 - 配合
MethodHandles.Lookup(Java 7+)实现更高效的反射调用(但 JDK 代理中通常仍用Method.invoke)
不复杂但容易忽略:动态代理不会拦截类中非接口定义的方法(包括 toString()、hashCode() 等,除非接口显式声明),也不会拦截私有方法或静态方法——这些需用 CGLIB 或字节码工具。


















