目录
2.2 java.lang.reflect.InvocationHandler 接口
1、概念
Java 的动态代理(Dynamic Proxy)是一种在运行时动态创建代理类和对象的技术,主要用于对方法调用进行拦截和增强。它基于接口实现,是 Java 反射机制的高级应用,广泛应用于 AOP(面向切面编程)、远程调用、事务管理等领域。
2、核心组件
2.1 java.lang.reflect.Proxy
类
-
提供静态方法创建动态代理类和实例。
-
核心方法:
static Object newProxyInstance(ClassLoader loader, // 类加载器(通常使用目标类的加载器)
Class<?>[] interfaces, // 代理类需实现的接口(目标接口)
InvocationHandler h) // 调用处理器(实现代理逻辑)
2.2 java.lang.reflect.InvocationHandler
接口
-
定义代理方法的调用逻辑。
-
核心方法:
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
proxy
:代理对象自身(慎用,避免递归调用)。
method
:被调用的目标方法(反射对象)。
args
:方法参数。
3、实现步骤
3.1 定义接口并实现
public interface UserService {
void addUser(String name);
String getUser(int id);
}
public class UserServiceImpl implements UserService {
@Override
public void addUser(String name) {
System.out.println("添加用户: " + name);
}
@Override
public String getUser(int id) {
return "用户" + id;
}
}
3.2 实现 InvocationHandler
public class LogHandler implements InvocationHandler {
private Object target; // 目标对象(被代理的实例)
public LogHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 前置增强
System.out.println("调用方法: " + method.getName() + " | 参数: " + Arrays.toString(args));
// 调用目标方法
Object result = method.invoke(target, args);
// 后置增强
System.out.println("方法返回: " + result);
return result;
}
}
3.3 创建动态代理对象
public class Main {
public static void main(String[] args) {
// 1. 创建目标对象
UserService realService = new UserServiceImpl();
// 2. 创建调用处理器(传入目标对象)
InvocationHandler handler = new LogHandler(realService);
// 3. 生成代理对象
UserService proxy = (UserService) Proxy.newProxyInstance(
realService.getClass().getClassLoader(), // 类加载器
realService.getClass().getInterfaces(), // 目标接口
handler // 调用处理器
);
// 4. 通过代理对象调用方法
proxy.addUser("张三"); // 自动触发 LogHandler.invoke()
System.out.println(proxy.getUser(100));
}
}
4、关键特性
-
基于接口:代理类必须实现目标接口,无法代理未实现接口的类(需结合 CGLIB 等工具代理类)。
-
运行时动态生成:代理类在运行时通过字节码生成(如
ProxyGenerator
),类名通常为$Proxy0
、$Proxy1
。 -
方法调用转发:所有方法调用均转发到
InvocationHandler.invoke()
,由开发者决定是否调用原始方法。 -
透明性:对使用者隐藏代理细节,调用方式与普通对象一致。
5、应用场景
-
AOP 编程
-
日志记录、性能监控、事务管理等横切关注点。
-
示例:Spring AOP 默认使用 JDK 动态代理(基于接口)。
-
-
远程方法调用(RPC)
-
动态代理封装网络通信(如 Dubbo、gRPC)。
-
-
延迟加载
-
例如 Hibernate 中代理关联对象,首次访问时才查询数据库。
-
-
访问控制
-
在方法调用前进行权限校验。
-
6、与CGLIB代理对比
特性 | JDK 动态代理 | CGLIB 代理 |
---|---|---|
代理对象要求 | 必须实现接口 | 可代理普通类(无需接口) |
性能 | 生成较快,调用稍慢(反射) | 生成较慢,调用较快(ASM) |
方法拦截 | 仅拦截接口方法 | 可拦截类中任意非 final 方法 |
依赖 | Java 原生支持 | 需引入第三方库 |
设计模式:代理模式
7、注意事项
-
避免递归调用:在
InvocationHandler.invoke()
中调用proxy.method()
会导致无限递归。 -
接口方法限制:无法代理
final
、static
或private
方法(JDK 代理基于接口)。 -
性能开销:反射调用比直接调用慢,高频场景需谨慎使用。