Java的动态代理
以下是Java动态代理的逐步解释:
Java动态代理详解
1. 动态代理的核心组件
java.lang.reflect.Proxy
:生成代理对象的工具类。java.lang.reflect.InvocationHandler
:代理对象的方法调用处理器接口。
2. 实现步骤
步骤1:自定义接口
java
代码解读
复制代码
public interface UserService {
void addUser(String username);
void deleteUser(String username);
}
步骤2:实现自定接口
java
代码解读
复制代码
public class UserServiceImpl implements UserService {
@Override
public void addUser(String username) {
System.out.println("添加用户: " + username);
}
@Override
public void deleteUser(String username) {
System.out.println("删除用户: " + username);
}
}
步骤3:实现InvocationHandler
java
代码解读
复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class LoggingHandler implements InvocationHandler {
private final Object target; // 被代理的对象
public LoggingHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 方法执行前逻辑
System.out.println("调用方法前: " + method.getName());
// 执行目标方法
Object result = method.invoke(target, args);
// 方法执行后逻辑
System.out.println("调用方法后: " + method.getName());
return result;
}
}
步骤4:生成代理对象
java
代码解读
复制代码
import java.lang.reflect.Proxy;
public class DynamicProxyDemo {
public static void main(String[] args) {
// 创建目标对象
UserService realService = new UserServiceImpl();
// 创建InvocationHandler实例
InvocationHandler handler = new LoggingHandler(realService);
// 生成代理对象
UserService proxyService = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(), // 类加载器
new Class[]{UserService.class}, // 代理的接口数组
handler // InvocationHandler实例
);
// 通过代理对象调用方法
proxyService.addUser("Alice");
proxyService.deleteUser("Bob");
}
}
3. 输出结果
makefile
代码解读
复制代码
调用方法前: addUser
添加用户: Alice
调用方法后: addUser
调用方法前: deleteUser
删除用户: Bob
调用方法后: deleteUser
4. 动态代理的底层原理
- 代理类生成:
Proxy.newProxyInstance()
在运行时动态生成代理类字节码。 - 代理类结构:生成的代理类继承
Proxy
并实现指定接口。 - 方法调用流程:
- 客户端调用代理对象方法(如
addUser()
)。 - 代理类将调用转发到
InvocationHandler.invoke()
。 invoke()
方法执行前置逻辑 → 调用目标方法 → 执行后置逻辑。
5. 动态代理的限制与解决方案
限制 | 说明 | 解决方案 |
只能代理接口 | 无法直接代理没有接口的类 | 使用CGLIB库(如Spring AOP默认行为) |
性能开销 | 反射调用比直接调用慢约5-20倍 | 对性能敏感场景使用AspectJ编译时织入 |
无法代理final方法 | final方法无法被重写 | 避免代理final方法或使用CGLIB |
6. 动态代理的典型应用场景
- AOP实现:日志、事务、权限校验等横切关注点。
- 远程方法调用(RPC):如Dubbo、gRPC的客户端代理。
- 延迟加载:例如Hibernate的延迟初始化代理。
- Mock测试:动态生成测试替身(Test Double)。
7. 动态代理与CGLIB对比
特性 | JDK动态代理 | CGLIB |
代理方式 | 基于接口 | 基于类继承 |
性能 | 较慢(反射调用) | 较快(方法调用无反射) |
依赖 | 无需第三方库 | 需要引入CGLIB库 |
方法限制 | 无法代理无接口的类 | 无法代理final方法或类 |
8. 高级技巧
查看生成的代理类字节码
java
代码解读
复制代码
// 在启动JVM时添加以下参数
System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
处理Object方法(如toString)
java
代码解读
复制代码
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getDeclaringClass() == Object.class) {
// 直接调用Object方法,不进行代理增强
return method.invoke(target, args);
}
// 其他方法的代理逻辑
// ...
}
动态代理实现RPC框架伪代码
java
代码解读
复制代码
public class RpcProxy implements InvocationHandler {
private final String host;
private final int port;
public RpcProxy(String host, int port) {
this.host = host;
this.port = port;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
// 1. 序列化请求参数
byte[] requestData = serialize(method, args);
// 2. 发送网络请求
byte[] responseData = sendRequest(host, port, requestData);
// 3. 反序列化响应结果
return deserialize(responseData, method.getReturnType());
}
}
9. 总结
- 核心机制:通过反射动态生成代理类,拦截方法调用。
- 适用场景:需要无侵入式增强方法功能的场合。
- 性能权衡:灵活性与性能之间的取舍,需根据场景选择方案。
- 扩展应用:结合其他技术(如注解、反射工厂)可实现更复杂的功能增强。