--- title: "14-动态代理" created: 2025-11-26 tags: - 博客 aliases: - 动态代理 --- # 动态代理 > **问:什么是动态代理?JDK 和 CGLIB 有什么区别?** > 动态代理就是一种“运行时帮你生成代理对象”的机制,它的最大特点是:**你不需要提前写好代理类,而是在程序运行的时候,框架帮你“临时织入”增强逻辑。** > > 在不动原来业务代码的前提下,它可以帮你在方法执行的前后插入各种通用逻辑,比如日志打印、权限校验、事务控制等,这在 AOP、RPC、监控等场景中非常常见。 > > Java 有两种动态代理方式: > > - **JDK 动态代理**:专门用来代理“接口”,它通过 `Proxy` 和 `InvocationHandler` 实现,是基于反射的; > - **CGLIB 动态代理**:用来代理“类”,不需要接口,是通过继承 + 动态生成字节码的方式实现的。 > > 简单理解就是: > 👉 有接口就用 JDK,没接口就用 CGLIB。 > > 在 Spring 中: > > - 传统 Spring 默认用的是 **JDK 动态代理**; > - 从 Spring Boot 2.x 开始,**默认改用 CGLIB**,因为它兼容性更强,避免因为类没有接口而出错。 ## 第一部分:问题的提出 ### 一、现实中的需求:横切关注点 想象一个场景: ```java // 传统方式:业务代码和增强逻辑混在一起 public class UserServiceImpl implements UserService { @Override public void save(User user) { // 日志打印 System.out.println("保存用户前:" + user); // 权限检查 if (!hasPermission()) { throw new SecurityException("无权限"); } // 事务开始 beginTransaction(); try { // 真正的业务逻辑 userRepository.save(user); // 事务提交 commitTransaction(); } catch (Exception e) { // 事务回滚 rollbackTransaction(); throw e; } // 日志打印 System.out.println("保存用户后"); } @Override public void delete(int id) { // 又要重复写一遍日志、权限、事务的代码... System.out.println("删除用户前:" + id); if (!hasPermission()) { throw new SecurityException("无权限"); } beginTransaction(); try { userRepository.delete(id); commitTransaction(); } catch (Exception e) { rollbackTransaction(); throw e; } System.out.println("删除用户后"); } } ``` 这种写法有什么问题? 1. **代码重复** - 日志、权限、事务代码在每个方法中重复 2. **职责混乱** - 业务逻辑和增强逻辑混在一起 3. **难以维护** - 修改增强逻辑需要改动所有方法 4. **难以测试** - 无法单独测试业务逻辑 ### 二、理想的解决方案 我们希望能这样写: ```java // 理想方式:业务代码只关注业务逻辑 public class UserServiceImpl implements UserService { @Override public void save(User user) { // 只写业务逻辑,增强逻辑由框架自动处理 userRepository.save(user); } @Override public void delete(int id) { // 只写业务逻辑 userRepository.delete(id); } } // 框架自动为我们处理:日志、权限、事务等 UserService userService = createProxy(new UserServiceImpl()); userService.save(user); // 自动执行:日志 → 权限检查 → 事务 → 业务逻辑 → 事务提交 → 日志 ``` **这就是动态代理要解决的问题:在不改变原始代码的前提下,为方法自动织入增强逻辑。** --- ## 第二部分:解决方案的演进 ### 三、静态代理:手工方案 在动态代理出现之前,我们可以手工写代理类: ```java // 1. 定义接口 public interface UserService { void save(User user); void delete(int id); } // 2. 实现接口 public class UserServiceImpl implements UserService { @Override public void save(User user) { userRepository.save(user); } @Override public void delete(int id) { userRepository.delete(id); } } // 3. 手工写代理类 public class UserServiceProxy implements UserService { private UserService target; public UserServiceProxy(UserService target) { this.target = target; } @Override public void save(User user) { // 前置增强 System.out.println("保存用户前"); checkPermission(); beginTransaction(); try { // 调用原方法 target.save(user); // 后置增强 commitTransaction(); } catch (Exception e) { rollbackTransaction(); throw e; } System.out.println("保存用户后"); } @Override public void delete(int id) { // 又要写一遍代理逻辑... System.out.println("删除用户前"); checkPermission(); beginTransaction(); try { target.delete(id); commitTransaction(); } catch (Exception e) { rollbackTransaction(); throw e; } System.out.println("删除用户后"); } } // 4. 使用代理 UserService userService = new UserServiceProxy(new UserServiceImpl()); userService.save(user); ``` **静态代理的问题:** 1. 需要手工为每个接口写代理类 2. 如果有100个接口,就要写100个代理类 3. 代理逻辑重复,难以维护 ### 四、动态代理:自动方案 **动态代理就是让框架自动生成代理类,而不需要我们手工写。** ```java // 动态代理:框架自动生成代理类 UserService target = new UserServiceImpl(); UserService proxy = createDynamicProxy(target, (method, args) -> { // 前置增强 System.out.println("方法调用前:" + method.getName()); checkPermission(); beginTransaction(); try { // 调用原方法 Object result = method.invoke(target, args); // 后置增强 commitTransaction(); return result; } catch (Exception e) { rollbackTransaction(); throw e; } }); // 所有方法都自动应用了增强逻辑 proxy.save(user); proxy.delete(1); ``` **动态代理的优势:** 1. 无需手工写代理类 2. 一套增强逻辑适用于所有方法 3. 易于维护和扩展 --- ## 第三部分:动态代理的两种实现 ### 五、JDK动态代理:基于接口 #### 5.1 设计思路 ``` ┌─────────────────────────────────────────┐ │ JDK动态代理的设计思路 │ ├─────────────────────────────────────────┤ │ │ │ 1. 前提条件 │ │ └─ 目标类必须实现接口 │ │ │ │ 2. 核心机制 │ │ ├─ Proxy:代理工厂 │ │ ├─ InvocationHandler:方法拦截器 │ │ └─ 反射:调用原方法 │ │ │ │ 3. 工作流程 │ │ ├─ 调用代理对象的方法 │ │ ├─ 转发到InvocationHandler.invoke() │ │ ├─ 通过反射调用原方法 │ │ └─ 返回结果 │ │ │ └─────────────────────────────────────────┘ ``` #### 5.2 核心API ```java // Proxy.newProxyInstance() 的三个参数 Proxy.newProxyInstance( ClassLoader loader, // 1. 类加载器 Class[] interfaces, // 2. 要实现的接口 InvocationHandler handler // 3. 方法拦截器 ); ``` #### 5.3 完整实现 ```java // 1. 定义接口 public interface UserService { void save(User user); String getName(int id); } // 2. 实现接口 public class UserServiceImpl implements UserService { @Override public void save(User user) { System.out.println("真正的保存逻辑:" + user); } @Override public String getName(int id) { System.out.println("真正的查询逻辑:" + id); return "Tom"; } } // 3. 实现InvocationHandler(方法拦截器) public class MyInvocationHandler implements InvocationHandler { private Object target; // 被代理的目标对象 public MyInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // proxy:代理对象本身(通常不用) // method:被调用的方法 // args:方法参数 // 前置增强 System.out.println("========== 方法调用前 =========="); System.out.println("方法名:" + method.getName()); System.out.println("参数:" + Arrays.toString(args)); // 调用原方法(通过反射) Object result = null; try { result = method.invoke(target, args); System.out.println("返回值:" + result); } catch (Exception e) { System.out.println("异常:" + e.getMessage()); throw e; } // 后置增强 System.out.println("========== 方法调用后 =========="); return result; } } // 4. 创建代理对象 public class JDKProxyDemo { public static void main(String[] args) { // 创建目标对象 UserService target = new UserServiceImpl(); // 创建代理对象 UserService proxy = (UserService) Proxy.newProxyInstance( target.getClass().getClassLoader(), // 类加载器 target.getClass().getInterfaces(), // 要实现的接口 new MyInvocationHandler(target) // 方法拦截器 ); // 调用代理对象的方法 proxy.save(new User("Tom")); String name = proxy.getName(1); } } ``` **执行流程:** ``` proxy.save(user) ↓ InvocationHandler.invoke(proxy, method, args) ↓ 前置增强(日志、权限检查等) ↓ method.invoke(target, args) // 反射调用原方法 ↓ 后置增强(事务提交等) ↓ 返回结果 ``` #### 5.4 JDK动态代理的原理 ```java // JDK动态代理的本质:运行时生成一个代理类 // 生成的代理类大概是这样的: public class $Proxy0 implements UserService { private InvocationHandler h; public $Proxy0(InvocationHandler h) { this.h = h; } @Override public void save(User user) { try { // 获取save方法的Method对象 Method method = UserService.class.getMethod("save", User.class); // 调用InvocationHandler的invoke方法 h.invoke(this, method, new Object[]{user}); } catch (Throwable e) { throw new UndeclaredThrowableException(e); } } @Override public String getName(int id) { try { Method method = UserService.class.getMethod("getName", int.class); return (String) h.invoke(this, method, new Object[]{id}); } catch (Throwable e) { throw new UndeclaredThrowableException(e); } } } ``` **关键点:** 1. JDK动态代理在运行时生成一个实现了接口的代理类 2. 代理类的每个方法都会调用InvocationHandler的invoke() 3. 所有的增强逻辑都在invoke()中实现 --- ### 六、CGLIB动态代理:基于继承 #### 6.1 设计思路 ``` ┌─────────────────────────────────────────┐ │ CGLIB动态代理的设计思路 │ ├─────────────────────────────────────────┤ │ │ │ 1. 前提条件 │ │ └─ 目标类可以没有接口 │ │ │ │ 2. 核心机制 │ │ ├─ Enhancer:代理工厂 │ │ ├─ MethodInterceptor:方法拦截器 │ │ └─ 字节码增强:生成子类 │ │ │ │ 3. 工作流程 │ │ ├─ 创建目标类的子类 │ │ ├─ 重写目标方法 │ │ ├─ 在子类方法中插入拦截逻辑 │ │ └─ 调用super.method()执行原方法 │ │ │ └─────────────────────────────────────────┘ ``` #### 6.2 核心API ```java // Enhancer的基本用法 Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(targetClass); // 设置父类 enhancer.setCallback(methodInterceptor); // 设置拦截器 Object proxy = enhancer.create(); // 创建代理对象 ``` #### 6.3 完整实现 ```java // 1. 目标类(注意:没有接口) public class UserService { public void save(User user) { System.out.println("真正的保存逻辑:" + user); } public String getName(int id) { System.out.println("真正的查询逻辑:" + id); return "Tom"; } } // 2. 实现MethodInterceptor(方法拦截器) public class MyMethodInterceptor implements MethodInterceptor { private Object target; // 被代理的目标对象 public MyMethodInterceptor(Object target) { this.target = target; } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { // obj:代理对象 // method:被调用的方法 // args:方法参数 // proxy:方法代理,用于调用原方法 // 前置增强 System.out.println("========== 方法调用前 =========="); System.out.println("方法名:" + method.getName()); System.out.println("参数:" + Arrays.toString(args)); // 调用原方法(两种方式) Object result = null; try { // 方式1:使用MethodProxy(推荐,性能更好) result = proxy.invokeSuper(obj, args); // 方式2:使用反射(不推荐) // result = method.invoke(target, args); System.out.println("返回值:" + result); } catch (Exception e) { System.out.println("异常:" + e.getMessage()); throw e; } // 后置增强 System.out.println("========== 方法调用后 =========="); return result; } } // 3. 创建代理对象 public class CGLIBProxyDemo { public static void main(String[] args) { // 创建目标对象 UserService target = new UserService(); // 创建Enhancer Enhancer enhancer = new Enhancer(); // 设置父类(要代理的类) enhancer.setSuperclass(UserService.class); // 设置拦截器 enhancer.setCallback(new MyMethodInterceptor(target)); // 创建代理对象 UserService proxy = (UserService) enhancer.create(); // 调用代理对象的方法 proxy.save(new User("Tom")); String name = proxy.getName(1); } } ``` **执行流程:** ``` proxy.save(user) ↓ MethodInterceptor.intercept(obj, method, args, proxy) ↓ 前置增强(日志、权限检查等) ↓ proxy.invokeSuper(obj, args) // 调用父类方法 ↓ 后置增强(事务提交等) ↓ 返回结果 ``` #### 6.4 CGLIB动态代理的原理 ```java // CGLIB动态代理的本质:运行时生成一个继承目标类的子类 // 生成的代理类大概是这样的: public class UserService$$EnhancerByCGLIB$$12345 extends UserService { private MethodInterceptor methodInterceptor; @Override public void save(User user) { // 获取方法代理 MethodProxy proxy = MethodProxy.find( UserService.class, new Class[]{User.class} ); // 调用拦截器 methodInterceptor.intercept( this, UserService.class.getMethod("save", User.class), new Object[]{user}, proxy ); } @Override public String getName(int id) { MethodProxy proxy = MethodProxy.find( UserService.class, new Class[]{int.class} ); return (String) methodInterceptor.intercept( this, UserService.class.getMethod("getName", int.class), new Object[]{id}, proxy ); } } ``` **关键点:** 1. CGLIB动态代理在运行时生成一个继承目标类的子类 2. 子类重写了目标类的所有方法 3. 每个方法都会调用MethodInterceptor的intercept() 4. 通过proxy.invokeSuper()调用父类(原始)方法 --- ## 第四部分:JDK vs CGLIB对比 ### 七、两者的核心区别 #### 7.1 对比表格 | 维度 | JDK动态代理 | CGLIB动态代理 | | --- | --- | --- | | **依赖接口** | ✅ 必须实现接口 | ❌ 不需要接口 | | **实现方式** | 反射 + 接口实现 | 字节码增强 + 继承 | | **代理对象** | 实现接口的代理类 | 继承目标类的子类 | | **性能(调用)** | JDK8后接近 | 稍快(避免反射) | | **性能(创建)** | 快 | 慢(字节码生成) | | **final限制** | 无影响 | ❌ 无法代理final方法 | | **原生支持** | ✅ JDK内置 | ❌ 需要引入CGLIB | | **Spring默认** | Spring Framework | SpringBoot 2.x+ | #### 7.2 选择标准 ``` ┌─────────────────────────────────────────┐ │ 如何选择JDK还是CGLIB? │ ├─────────────────────────────────────────┤ │ │ │ 有接口? │ │ ├─ 是 → 优先选择JDK │ │ │ ├─ 原生支持 │ │ │ ├─ 无需额外依赖 │ │ │ └─ 性能相当 │ │ │ │ │ └─ 否 → 必须选择CGLIB │ │ ├─ 支持具体类 │ │ ├─ 通用性强 │ │ └─ 避免报错 │ │ │ │ Spring框架选择: │ │ ├─ Spring Framework:JDK(默认) │ │ ├─ SpringBoot 2.x+:CGLIB(默认) │ │ └─ 可通过配置修改 │ │ │ └─────────────────────────────────────────┘ ``` #### 7.3 为什么SpringBoot改用CGLIB? ```java // 问题场景:使用JDK代理的坑 // 情况1:有接口,JDK代理正常 public interface UserService { void save(User user); } public class UserServiceImpl implements UserService { @Override public void save(User user) { } } // ✅ 可以使用JDK代理 UserService proxy = (UserService) Proxy.newProxyInstance(...); // 情况2:没有接口,JDK代理报错 public class UserService { // 没有实现接口 public void save(User user) { } } // ❌ JDK代理报错:java.lang.ClassCastException // UserService proxy = (UserService) Proxy.newProxyInstance(...); // 因为代理对象没有实现UserService接口 // ✅ CGLIB代理正常工作 Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(UserService.class); UserService proxy = (UserService) enhancer.create(); ``` **SpringBoot改用CGLIB的原因:** 1. 通用性更强 - 支持有接口和无接口的类 2. 避免ClassCastException - 不会因为缺接口而报错 3. 易于维护 - 用户无需关心是否有接口 4. 性能相当 - JDK8后性能差异已缩小 --- ## 第五部分:实际应用 ### 八、Spring AOP中的动态代理 ```java // 1. 定义切面 @Aspect @Component public class LoggingAspect { @Before("execution(* com.example.service.*.*(..))") public void beforeMethod(JoinPoint joinPoint) { System.out.println("方法调用前:" + joinPoint.getSignature()); } @After("execution(* com.example.service.*.*(..))") public void afterMethod(JoinPoint joinPoint) { System.out.println("方法调用后:" + joinPoint.getSignature()); } } // 2. 定义业务类(可以没有接口) @Service public class UserService { public void save(User user) { System.out.println("保存用户:" + user); } } // 3. Spring自动创建代理 @Autowired private UserService userService; // 实际上是代理对象 // 4. 调用代理对象 userService.save(user); // 自动执行:前置通知 → 业务逻辑 → 后置通知 ``` **Spring AOP的工作流程:** ``` 1. 扫描@Aspect注解的切面类 2. 解析@Before、@After等通知 3. 为匹配的Bean创建代理对象 4. 调用代理对象时,自动织入增强逻辑 ``` ### 九、RPC框架中的动态代理 ```java // RPC框架使用动态代理模拟本地调用 // 1. 定义远程服务接口 public interface UserServiceRPC { User getUserById(int id); } // 2. 创建RPC代理 public class RPCProxyFactory { public static T createProxy(Class serviceInterface, String serverAddress) { return (T) Proxy.newProxyInstance( serviceInterface.getClassLoader(), new Class[]{serviceInterface}, (proxy, method, args) -> { // 1. 序列化参数 byte[] requestData = serialize(method.getName(), args); // 2. 发送网络请求到远程服务器 byte[] responseData = sendNetworkRequest(serverAddress, requestData); // 3. 反序列化返回值 return deserialize(responseData); } ); } } // 3. 使用RPC代理 UserServiceRPC userService = RPCProxyFactory.createProxy( UserServiceRPC.class, "192.168.1.100:8080" ); // 看起来像本地调用,实际上是远程调用 User user = userService.getUserById(1); ``` --- ## 第六部分:性能与最佳实践 ### 十、性能对比 ```java public class ProxyPerformanceDemo { public static void main(String[] args) { int iterations = 1_000_000; // ========== 直接调用 ========== UserService target = new UserServiceImpl(); long directStart = System.nanoTime(); for (int i = 0; i < iterations; i++) { target.save(new User()); } long directTime = System.nanoTime() - directStart; // ========== JDK代理 ========== UserService jdkProxy = (UserService) Proxy.newProxyInstance( UserService.class.getClassLoader(), new Class[]{UserService.class}, new MyInvocationHandler(target) ); long jdkStart = System.nanoTime(); for (int i = 0; i < iterations; i++) { jdkProxy.save(new User()); } long jdkTime = System.nanoTime() - jdkStart; // ========== CGLIB代理 ========== Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(UserServiceImpl.class); enhancer.setCallback(new MyMethodInterceptor(target)); UserService cglibProxy = (UserService) enhancer.create(); long cglibStart = System.nanoTime(); for (int i = 0; i < iterations; i++) { cglibProxy.save(new User()); } long cglibTime = System.nanoTime() - cglibStart; // 输出结果 System.out.println("直接调用:" + directTime + "ns"); System.out.println("JDK代理:" + jdkTime + "ns"); System.out.println("CGLIB代理:" + cglibTime + "ns"); } } ``` **典型结果(JDK8+):** ``` 直接调用:10ms JDK代理:15ms(1.5倍) CGLIB代理:12ms(1.2倍) ``` **性能特点:** - JDK代理:调用快,创建快 - CGLIB代理:调用稍快,创建慢(字节码生成) - 总体差异不大,不是选择的主要因素 ### 十一、最佳实践 ```java // ✅ 推荐做法 // 1. 优先使用接口 public interface UserService { void save(User user); } public class UserServiceImpl implements UserService { @Override public void save(User user) { // 业务逻辑 } } // 2. 使用Spring AOP而不是手工代理 @Aspect @Component public class LoggingAspect { @Before("execution(* com.example.service.*.*(..))") public void beforeMethod(JoinPoint joinPoint) { System.out.println("方法调用前"); } } // 3. 缓存代理对象 public class ProxyCache { private static final Map, Object> cache = new ConcurrentHashMap<>(); public static T getProxy(Class serviceClass) { return (T) cache.computeIfAbsent(serviceClass, key -> { // 创建代理对象 return createProxy(serviceClass); }); } } // ❌ 避免做法 // 1. 不要频繁创建代理对象 for (int i = 0; i < 1000; i++) { UserService proxy = Proxy.newProxyInstance(...); // 每次都创建 } // 2. 不要在代理中做重操作 public Object invoke(Object proxy, Method method, Object[] args) { // ❌ 不要在这里做数据库查询等重操作 userRepository.queryAll(); return method.invoke(target, args); } // 3. 不要混用JDK和CGLIB // 同一个应用中,尽量统一使用一种代理方式 ``` --- ## 第七部分:总结 ### 十二、动态代理的本质 **动态代理是在运行时自动生成代理类的机制,它让我们能够在不修改原始代码的前提下,为方法自动织入增强逻辑。** ``` ┌──────────────────────────────────────────┐ │ 动态代理的核心价值 │ ├──────────────────────────────────────────┤ │ │ │ 1. 解耦 │ │ ├─ 业务逻辑和增强逻辑分离 │ │ ├─ 易于维护和扩展 │ │ └─ 符合单一职责原则 │ │ │ │ 2. 通用性 │ │ ├─ 一套增强逻辑适用所有方法 │ │ ├─ 无需为每个类写代理 │ │ └─ 支持动态添加功能 │ │ │ │ 3. 灵活性 │ │ ├─ 运行时动态决定是否代理 │ │ ├─ 支持多个增强逻辑组合 │ │ └─ 易于扩展新功能 │ │ │ │ 4. 框架能力 │ │ ├─ 实现AOP(面向切面编程) │ │ ├─ 实现事务管理 │ │ ├─ 实现权限控制 │ │ └─ 实现RPC框架 │ │ │ └──────────────────────────────────────────┘ ``` #### 12.1 动态代理的工作原理总结 ``` ┌─────────────────────────────────────────────────────┐ │ 动态代理的完整工作流程 │ ├─────────────────────────────────────────────────────┤ │ │ │ 第1步:定义目标对象 │ │ ├─ 目标类(可以有接口或没有) │ │ └─ 包含真正的业务逻辑 │ │ │ │ 第2步:定义增强逻辑 │ │ ├─ JDK:实现InvocationHandler │ │ └─ CGLIB:实现MethodInterceptor │ │ │ │ 第3步:创建代理对象 │ │ ├─ JDK:Proxy.newProxyInstance() │ │ └─ CGLIB:Enhancer.create() │ │ │ │ 第4步:调用代理对象 │ │ ├─ 代理对象拦截方法调用 │ │ ├─ 执行前置增强 │ │ ├─ 调用原方法 │ │ ├─ 执行后置增强 │ │ └─ 返回结果 │ │ │ └─────────────────────────────────────────────────────┘ ``` #### 12.2 JDK vs CGLIB的本质区别 ``` ┌──────────────────────────────────────────┐ │ JDK和CGLIB的本质区别 │ ├──────────────────────────────────────────┤ │ │ │ JDK动态代理 │ │ ├─ 原理:实现接口 │ │ ├─ 结构:代理类 implements 接口 │ │ ├─ 调用:通过接口多态 │ │ └─ 反射:每次调用都需要反射 │ │ │ │ CGLIB动态代理 │ │ ├─ 原理:继承父类 │ │ ├─ 结构:代理类 extends 目标类 │ │ ├─ 调用:通过继承多态 │ │ └─ 反射:使用MethodProxy避免反射 │ │ │ │ 关键差异 │ │ ├─ 依赖:JDK需要接口,CGLIB不需要 │ │ ├─ 性能:CGLIB调用快(避免反射) │ │ ├─ 创建:CGLIB创建慢(字节码生成) │ │ └─ 限制:CGLIB无法代理final方法 │ │ │ └──────────────────────────────────────────┘ ``` ### 十三、反射与动态代理的关系 #### 13.1 反射是基础 ```java // 动态代理依赖反射机制 // JDK动态代理:显式使用反射 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // method是反射获取的Method对象 return method.invoke(target, args); // 通过反射调用原方法 } // CGLIB动态代理:隐式使用反射 public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) { // proxy.invokeSuper()内部也使用反射机制 return proxy.invokeSuper(obj, args); } // 动态代理 = 反射 + 代理模式 ``` #### 13.2 动态代理是反射的应用 ``` ┌──────────────────────────────────────────┐ │ 反射与动态代理的层次关系 │ ├──────────────────────────────────────────┤ │ │ │ 第1层:反射机制(基础) │ │ ├─ Class对象 │ │ ├─ Method、Field、Constructor │ │ └─ 运行时获取和操作类信息 │ │ │ │ 第2层:动态代理(应用) │ │ ├─ 利用反射实现方法拦截 │ │ ├─ 在运行时生成代理类 │ │ └─ 实现横切关注点 │ │ │ │ 第3层:AOP框架(高层) │ │ ├─ Spring AOP │ │ ├─ 自动创建代理 │ │ └─ 用户无感知反射和代理 │ │ │ └──────────────────────────────────────────┘ ``` --- ### 十四、动态代理的实际应用场景 #### 14.1 Spring AOP(面向切面编程) ```java // 场景:为所有Service方法添加日志和事务 @Aspect @Component public class TransactionAspect { @Before("execution(* com.example.service.*.*(..))") public void beginTransaction() { System.out.println("开始事务"); } @After("execution(* com.example.service.*.*(..))") public void commitTransaction() { System.out.println("提交事务"); } @AfterThrowing("execution(* com.example.service.*.*(..))") public void rollbackTransaction() { System.out.println("回滚事务"); } } // Spring自动为所有Service创建代理 // 用户无需关心代理的存在 ``` #### 14.2 权限控制 ```java // 场景:为某些方法添加权限检查 public class PermissionInterceptor implements InvocationHandler { private Object target; @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 检查权限 if (!hasPermission(method)) { throw new SecurityException("无权限访问:" + method.getName()); } // 调用原方法 return method.invoke(target, args); } } // 使用 UserService userService = (UserService) Proxy.newProxyInstance( UserService.class.getClassLoader(), new Class[]{UserService.class}, new PermissionInterceptor(new UserServiceImpl()) ); userService.delete(1); // 自动检查权限 ``` #### 14.3 缓存 ```java // 场景:为查询方法添加缓存 public class CacheInterceptor implements MethodInterceptor { private Map cache = new ConcurrentHashMap<>(); @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { // 生成缓存key String cacheKey = method.getName() + Arrays.toString(args); // 检查缓存 if (cache.containsKey(cacheKey)) { System.out.println("从缓存返回:" + cacheKey); return cache.get(cacheKey); } // 调用原方法 Object result = proxy.invokeSuper(obj, args); // 存入缓存 cache.put(cacheKey, result); return result; } } // 使用 Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(UserService.class); enhancer.setCallback(new CacheInterceptor()); UserService userService = (UserService) enhancer.create(); userService.getUserById(1); // 第一次调用,执行查询 userService.getUserById(1); // 第二次调用,从缓存返回 ``` #### 14.4 性能监控 ```java // 场景:监控方法执行时间 public class PerformanceInterceptor implements InvocationHandler { private Object target; @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long startTime = System.currentTimeMillis(); try { return method.invoke(target, args); } finally { long endTime = System.currentTimeMillis(); long duration = endTime - startTime; System.out.println("方法:" + method.getName() + ",耗时:" + duration + "ms"); // 如果超过阈值,发送告警 if (duration > 1000) { sendAlert("方法执行过慢:" + method.getName()); } } } } ``` #### 14.5 RPC框架 ```java // 场景:实现远程方法调用 public class RpcInvocationHandler implements InvocationHandler { private String serverAddress; public RpcInvocationHandler(String serverAddress) { this.serverAddress = serverAddress; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 1. 序列化请求 RpcRequest request = new RpcRequest( method.getDeclaringClass().getName(), method.getName(), method.getParameterTypes(), args ); // 2. 发送网络请求 RpcResponse response = sendRequest(serverAddress, request); // 3. 反序列化返回值 return response.getResult(); } } // 使用 UserServiceRPC userService = (UserServiceRPC) Proxy.newProxyInstance( UserServiceRPC.class.getClassLoader(), new Class[]{UserServiceRPC.class}, new RpcInvocationHandler("192.168.1.100:8080") ); // 看起来像本地调用,实际上是远程调用 User user = userService.getUserById(1); ``` --- ### 十五、动态代理的常见问题 #### Q1: 为什么JDK代理需要接口,CGLIB不需要? **答:** - JDK代理通过实现接口来创建代理类 - 代理类需要实现目标接口的所有方法 - 如果没有接口,就无法创建代理类 - CGLIB通过继承来创建代理类 - 代理类继承目标类,可以重写所有方法 - 不需要接口,支持具体类 #### Q2: 为什么CGLIB无法代理final方法? **答:** - CGLIB通过继承实现代理 - final方法无法被重写 - 所以CGLIB无法拦截final方法的调用 ```java public class UserService { public final void save(User user) { // final方法 // CGLIB无法代理这个方法 } } ``` #### Q3: 动态代理会影响性能吗? **答:** - 代理对象的创建有开销(特别是CGLIB) - 方法调用有额外开销(反射或字节码调用) - 但在实际应用中,这个开销通常可以接受 **优化建议:** 1. 缓存代理对象,避免重复创建 2. 避免在代理中做重操作 3. 对性能敏感的代码,考虑使用MethodHandle #### Q4: 如何选择JDK还是CGLIB? **答:** ``` 有接口? ├─ 是 → JDK(原生支持,无需依赖) └─ 否 → CGLIB(支持具体类) Spring框架? ├─ Spring Framework → JDK(默认) └─ SpringBoot 2.x+ → CGLIB(默认) 可以通过配置修改默认选择 ``` #### Q5: 代理对象和原对象是同一个吗? **答:** ```java UserService target = new UserServiceImpl(); UserService proxy = createProxy(target); System.out.println(target == proxy); // false,不是同一个对象 System.out.println(target instanceof UserService); // true System.out.println(proxy instanceof UserService); // true ``` - 代理对象和原对象不是同一个 - 但代理对象实现了相同的接口或继承了相同的类 - 从外部看,它们是可以互换的 --- ### 十六、动态代理的最佳实践 #### 16.1 推荐做法 ```java // 1. 优先使用框架提供的代理机制 @Aspect @Component public class LoggingAspect { @Before("execution(* com.example.service.*.*(..))") public void beforeMethod(JoinPoint joinPoint) { System.out.println("方法调用前"); } } // 2. 缓存代理对象 public class ProxyFactory { private static final Map, Object> proxyCache = new ConcurrentHashMap<>(); public static T getProxy(Class targetClass, T target) { return (T) proxyCache.computeIfAbsent(targetClass, key -> { return Proxy.newProxyInstance( targetClass.getClassLoader(), new Class[]{targetClass}, new MyInvocationHandler(target) ); }); } } // 3. 避免在代理中做重操作 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // ✅ 轻操作:日志、权限检查 System.out.println("方法调用"); // ❌ 重操作:数据库查询、网络请求 // userRepository.queryAll(); return method.invoke(target, args); } // 4. 使用接口编程 public interface UserService { void save(User user); } public class UserServiceImpl implements UserService { @Override public void save(User user) { // 业务逻辑 } } // 5. 异常处理 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { return method.invoke(target, args); } catch (InvocationTargetException e) { // 处理目标方法抛出的异常 throw e.getTargetException(); } catch (IllegalAccessException e) { // 处理访问异常 throw new RuntimeException(e); } } ``` #### 16.2 避免做法 ```java // ❌ 不要频繁创建代理对象 for (int i = 0; i < 1000; i++) { UserService proxy = Proxy.newProxyInstance(...); // 每次都创建 } // ❌ 不要在代理中做复杂业务逻辑 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // ❌ 不要在这里写业务逻辑 if (method.getName().equals("save")) { // 复杂的业务逻辑 } return method.invoke(target, args); } // ❌ 不要忽视异常处理 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // ❌ 没有异常处理 return method.invoke(target, args); } // ❌ 不要混用多种代理方式 // 同一个应用中,尽量统一使用一种代理方式 ``` --- ### 十七、动态代理的演进 #### 17.1 现代Java的替代方案 ```java // 1. MethodHandle(Java 7+) MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodHandle mh = lookup.findVirtual( UserService.class, "save", MethodType.methodType(void.class, User.class) ); mh.invoke(userService, user); // 优势:性能接近直接调用,JVM可以优化 // 2. 字节码增强(ASM、Javassist、CGLIB) // Spring使用CGLIB进行字节码增强 // 编译期生成代码,避免运行时反射成本 // 3. 编译期注解处理(APT) // Lombok、Dagger等在编译期生成代码 // 避免运行时反射开销 ``` #### 17.2 Java 19+ 虚拟线程与代理 ```java // Java 19引入虚拟线程,改变了异步编程方式 // 动态代理在虚拟线程中的应用 @Aspect @Component public class VirtualThreadAspect { @Around("execution(* com.example.service.*.*(..))") public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable { // 在虚拟线程中执行 return ForkJoinTask.adapt(() -> { try { return joinPoint.proceed(); } catch (Throwable e) { throw new RuntimeException(e); } }).invoke(); } } ``` --- ### 十八、关键要点回顾 #### 18.1 动态代理的核心概念 | 概念 | 说明 | 用途 | | --- | --- | --- | | **代理模式** | 为对象提供代理以控制访问 | 增强功能 | | **JDK代理** | 基于接口的代理 | 有接口的类 | | **CGLIB代理** | 基于继承的代理 | 无接口的类 | | **InvocationHandler** | JDK代理的拦截器 | 拦截方法调用 | | **MethodInterceptor** | CGLIB代理的拦截器 | 拦截方法调用 | | **AOP** | 面向切面编程 | 横切关注点 | #### 18.2 动态代理的三个层次 ``` 第1层:基础机制 ├─ JDK动态代理(反射 + 接口) └─ CGLIB动态代理(字节码 + 继承) 第2层:应用框架 ├─ Spring AOP ├─ 权限控制 ├─ 缓存 └─ 性能监控 第3层:高级特性 ├─ 事务管理 ├─ RPC框架 ├─ 日志系统 └─ 安全框架 ``` #### 18.3 动态代理的权衡 ``` ┌────────────────────────────────────┐ │ 动态代理的权衡分析 │ ├────────────────────────────────────┤ │ │ │ 优点 │ │ ✓ 解耦业务和增强逻辑 │ │ ✓ 通用性强 │ │ ✓ 易于维护和扩展 │ │ ✓ 支持动态添加功能 │ │ │ │ 缺点 │ │ ✗ 增加代码复杂度 │ │ ✗ 性能开销(通常可接受) │ │ ✗ 调试困难(代理隐藏细节) │ │ ✗ 需要理解代理机制 │ │ │ │ 结论 │ │ → 框架层使用,业务层通过框架使用 │ │ → 理解原理,谨慎使用 │ │ → 优先使用Spring AOP等框架 │ │ │ └────────────────────────────────────┘ ``` --- ### 十九、记忆口诀 ``` 动态代理运行时,自动生成代理类 不改原始业务代码,横切关注点织入 JDK基于接口实现,CGLIB继承目标类 有接口用JDK,无接口用CGLIB Spring默认JDK,Boot改用CGLIB InvocationHandler和MethodInterceptor 反射是基础,代理是应用 AOP是框架,用户无感知 缓存代理对象,避免重复创建 权限日志事务,缓存性能监控 RPC框架安全,动态代理无处不在 理解原理很重要,框架使用最关键 ``` --- ### 二十、相关链接 - 01-Class对象与反射机制 - 动态代理的基础 - [[7-Blog/后端与微服务/13-注解原理|注解原理]] - 注解与代理的结合 - Spring AOP(面向切面编程) - 实际应用 - 设计模式-代理模式 - 理论基础(代理模式理论即本篇一~三章,体系总览见 设计模式体系) ---