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. 总结
- 核心机制:通过反射动态生成代理类,拦截方法调用。
- 适用场景:需要无侵入式增强方法功能的场合。
- 性能权衡:灵活性与性能之间的取舍,需根据场景选择方案。
- 扩展应用:结合其他技术(如注解、反射工厂)可实现更复杂的功能增强。