JAVA 动态代理:从原理剖析到实战应用
JAVA 动态代理:从原理剖析到实战应用
1.1 本章学习目标与重点
💡 掌握动态代理的核心概念与分类,理解动态代理在 Java 开发中的核心价值。
💡 熟练掌握 JDK 动态代理的实现流程与核心 API,能够独立编写 JDK 动态代理代码。
💡 了解 CGLIB 动态代理的实现原理与适用场景,对比 JDK 动态代理与 CGLIB 动态代理的差异。
💡 结合实际业务场景,掌握动态代理在 AOP 编程、权限控制、日志记录等场景中的实战应用。
⚠️ 本章重点是 JDK 动态代理的核心实现 和 动态代理在 AOP 中的实战应用,这是 Java 高级开发与框架设计的必备技能。
1.2 动态代理的核心概念与价值
1.2.1 什么是动态代理
💡 动态代理 是 Java 设计模式中代理模式的一种高级实现,它允许程序在 运行时 动态生成目标类的代理对象,而无需在编译期手动编写代理类的代码。
在传统的静态代理模式中,我们需要为每个目标类手动编写对应的代理类,代理类与目标类实现相同的接口,这种方式存在代码冗余、扩展性差的问题。而动态代理则通过反射机制,在运行时动态创建代理类,实现了代理逻辑的复用,大幅提升了代码的灵活性和扩展性。
动态代理的核心思想是 “代理对象包裹目标对象,增强目标对象的方法功能”,代理对象可以在目标方法执行前后添加额外的逻辑,如日志记录、权限校验、事务管理等,而不需要修改目标对象的代码。
1.2.2 动态代理的核心价值
动态代理是 Java 高级开发中不可或缺的技术,其核心价值主要体现在以下几个方面:
- 解耦核心业务与非核心业务:将日志、事务、权限等通用功能从核心业务代码中抽离,实现 关注点分离。
- 提升代码复用性:代理逻辑可以复用在多个目标类上,避免重复编写相同的增强代码。
- 增强代码扩展性:新增增强功能时,只需修改代理逻辑,无需修改目标类代码,符合 开闭原则。
- 框架开发的核心基石:是 Spring AOP、MyBatis 等主流框架的底层实现技术,支撑了框架的核心功能。
1.2.3 动态代理的分类
根据实现方式的不同,Java 中的动态代理主要分为两类:
| 动态代理类型 | 底层实现 | 核心要求 | 典型应用 |
|---|---|---|---|
| JDK 动态代理 | Java 反射机制 | 目标类必须实现一个或多个接口 | Spring AOP(默认)、自定义框架 |
| CGLIB 动态代理 | 字节码增强技术(ASM 框架) | 目标类可以不实现接口,不能是 final 类 | Spring AOP(目标类无接口时)、Hibernate |
✅ 核心结论:动态代理的本质是 运行时生成代理类,增强目标方法功能,它是实现 AOP 编程的核心技术。
1.3 代理模式基础:静态代理
在学习动态代理之前,我们需要先掌握静态代理的实现方式,理解代理模式的核心思想,为动态代理的学习打下基础。
1.3.1 静态代理的核心角色
静态代理模式包含三个核心角色:
- 抽象角色:通常是一个接口,定义了目标对象和代理对象的共同方法。
- 目标角色:实现抽象角色的类,是代理对象所代理的真实对象,包含核心业务逻辑。
- 代理角色:实现抽象角色的类,包含对目标对象的引用,在目标方法执行前后添加增强逻辑。
1.3.2 静态代理的代码实现
/** * 静态代理示例:用户服务接口(抽象角色) */publicinterfaceUserService{voidaddUser(String username);voiddeleteUser(String userId);}/** * 目标角色:用户服务实现类 */publicclassUserServiceImplimplementsUserService{@OverridepublicvoidaddUser(String username){System.out.println("核心业务:添加用户 "+ username);}@OverridepublicvoiddeleteUser(String userId){System.out.println("核心业务:删除用户 "+ userId);}}/** * 代理角色:用户服务代理类 */publicclassUserServiceProxyimplementsUserService{// 维护目标对象的引用privatefinalUserService target;publicUserServiceProxy(UserService target){this.target = target;}@OverridepublicvoidaddUser(String username){// 前置增强:日志记录System.out.println("[日志] 执行 addUser 方法,参数:"+ username);// 执行目标方法 target.addUser(username);// 后置增强:事务提交System.out.println("[事务] 添加用户成功,事务提交");}@OverridepublicvoiddeleteUser(String userId){// 前置增强:日志记录System.out.println("[日志] 执行 deleteUser 方法,参数:"+ userId);// 执行目标方法 target.deleteUser(userId);// 后置增强:事务提交System.out.println("[事务] 删除用户成功,事务提交");}}/** * 测试类 */publicclassStaticProxyTest{publicstaticvoidmain(String[] args){// 创建目标对象UserService target =newUserServiceImpl();// 创建代理对象UserService proxy =newUserServiceProxy(target);// 通过代理对象调用方法 proxy.addUser("张三");System.out.println("---------------------------"); proxy.deleteUser("1001");}}1.3.3 静态代理的优缺点
优点:
- 实现简单,易于理解和上手。
- 不修改目标类代码,实现了核心业务与增强逻辑的分离。
缺点:
- 代码冗余:每个目标类都需要编写对应的代理类,当目标类较多时,代理类数量会急剧增加。
- 扩展性差:新增增强功能时,需要修改所有代理类的代码。
- 耦合度高:代理类与目标类实现相同的接口,接口变更时,代理类和目标类都需要修改。
⚠️ 注意事项:静态代理的缺点正是动态代理需要解决的问题,动态代理通过运行时生成代理类,完美解决了静态代理的代码冗余和扩展性问题。
1.4 JDK 动态代理核心实现
JDK 动态代理是 Java 官方提供的动态代理实现,底层基于 反射机制 和 java.lang.reflect 包下的核心 API 实现。
1.4.1 JDK 动态代理的核心 API
JDK 动态代理的核心 API 包含两个关键类/接口:
InvocationHandler接口:这是调用处理器接口,代理对象的所有方法调用都会转发到该接口的invoke方法,我们需要实现该接口来编写增强逻辑。Proxy类:这是代理类的生成器,通过其静态方法newProxyInstance可以在运行时动态生成代理对象。
核心方法详解
InvocationHandler.invoke(Object proxy, Method method, Object[] args)proxy:生成的代理对象本身method:被调用的目标方法对象args:目标方法的参数数组- 作用:编写增强逻辑,并通过反射调用目标方法
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)loader:目标类的类加载器interfaces:目标类实现的接口数组h:调用处理器对象- 作用:生成并返回动态代理对象
1.4.2 JDK 动态代理的实现步骤
💡 JDK 动态代理的实现可以分为以下四个核心步骤:
① 定义业务接口和目标类
② 实现 InvocationHandler 接口,编写增强逻辑
③ 通过 Proxy.newProxyInstance 生成代理对象
④ 通过代理对象调用目标方法
1.4.3 JDK 动态代理的代码实现
importjava.lang.reflect.InvocationHandler;importjava.lang.reflect.Method;importjava.lang.reflect.Proxy;/** * 步骤1:定义业务接口和目标类(复用静态代理的 UserService 和 UserServiceImpl) */publicinterfaceUserService{voidaddUser(String username);voiddeleteUser(String userId);}publicclassUserServiceImplimplementsUserService{@OverridepublicvoidaddUser(String username){System.out.println("核心业务:添加用户 "+ username);}@OverridepublicvoiddeleteUser(String userId){System.out.println("核心业务:删除用户 "+ userId);}}/** * 步骤2:实现 InvocationHandler 接口,编写增强逻辑 */publicclassLogTransactionInvocationHandlerimplementsInvocationHandler{// 维护目标对象的引用privatefinalObject target;publicLogTransactionInvocationHandler(Object target){this.target = target;}@OverridepublicObjectinvoke(Object proxy,Method method,Object[] args)throwsThrowable{Object result =null;try{// 前置增强:日志记录System.out.println("[日志] 执行方法:"+ method.getName()+",参数:"+(args ==null?"无": args[0]));// 反射调用目标方法 result = method.invoke(target, args);// 后置增强:事务提交System.out.println("[事务] 方法执行成功,事务提交");}catch(Exception e){// 异常增强:事务回滚System.out.println("[事务] 方法执行异常,事务回滚,异常信息:"+ e.getMessage());throw e;}return result;}}/** * 步骤3和4:生成代理对象并测试 */publicclassJdkDynamicProxyTest{publicstaticvoidmain(String[] args){// 1. 创建目标对象UserService target =newUserServiceImpl();// 2. 创建调用处理器对象InvocationHandler handler =newLogTransactionInvocationHandler(target);// 3. 生成动态代理对象UserService proxy =(UserService)Proxy.newProxyInstance( target.getClass().getClassLoader(),// 目标类的类加载器 target.getClass().getInterfaces(),// 目标类实现的接口 handler // 调用处理器);// 4. 通过代理对象调用方法System.out.println("===== 正常调用 ====="); proxy.addUser("李四");System.out.println("---------------------------"); proxy.deleteUser("1002");System.out.println("\n===== 异常调用 =====");// 模拟方法执行异常try{ proxy.addUser(null);}catch(Exception e){// 捕获异常}}}1.4.4 运行结果与分析
===== 正常调用 ===== [日志] 执行方法:addUser,参数:李四 核心业务:添加用户 李四 [事务] 方法执行成功,事务提交 --------------------------- [日志] 执行方法:deleteUser,参数:1002 核心业务:删除用户 1002 [事务] 方法执行成功,事务提交 ===== 异常调用 ===== [日志] 执行方法:addUser,参数:null 核心业务:添加用户 null [事务] 方法执行异常,事务回滚,异常信息:null ✅ 核心结论:JDK 动态代理通过 InvocationHandler 统一处理所有目标方法的增强逻辑,实现了代理逻辑的复用,解决了静态代理的代码冗余问题。
1.4.5 JDK 动态代理的注意事项
⚠️ 1. 目标类必须实现接口:JDK 动态代理是基于接口实现的,如果目标类没有实现任何接口,Proxy.newProxyInstance 方法会抛出 IllegalArgumentException 异常。
⚠️ 2. 代理对象是接口的实现类:通过 JDK 动态代理生成的代理对象,是目标类所实现接口的子类,不能将代理对象强制转换为目标类类型。
⚠️ 3. 增强逻辑统一化:所有目标方法的增强逻辑都写在 invoke 方法中,如果需要对不同方法进行不同增强,可以通过 method.getName() 进行判断。
1.5 CGLIB 动态代理核心实现
CGLIB(Code Generation Library)是一个基于字节码增强技术的动态代理框架,它弥补了 JDK 动态代理的不足,支持对没有实现接口的类进行代理。
1.5.1 CGLIB 动态代理的核心原理
CGLIB 动态代理的底层基于 ASM 字节码操作框架,它的核心原理是:
- 在运行时动态生成目标类的 子类,并重写目标类的非 final 方法。
- 在子类中嵌入增强逻辑,实现对目标方法的增强。
- 通过创建子类的实例,作为目标类的代理对象。
1.5.2 CGLIB 动态代理的核心 API
CGLIB 动态代理的核心 API 包含两个关键类:
MethodInterceptor接口:方法拦截器接口,类似于 JDK 动态代理的InvocationHandler,代理对象的方法调用会转发到该接口的intercept方法。Enhancer类:增强器类,用于生成目标类的子类(代理对象)。
1.5.3 CGLIB 动态代理的实现步骤
💡 CGLIB 动态代理的实现步骤如下:
① 引入 CGLIB 依赖
② 定义目标类(可以不实现接口)
③ 实现 MethodInterceptor 接口,编写增强逻辑
④ 通过 Enhancer 生成代理对象
⑤ 通过代理对象调用目标方法
1.5.4 CGLIB 动态代理的代码实现
步骤1:引入 CGLIB 依赖(Maven)
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version></dependency>步骤2:编写目标类(无接口)
/** * 目标类:订单服务类(无实现任何接口) */publicclassOrderService{publicvoidcreateOrder(String orderId){System.out.println("核心业务:创建订单 "+ orderId);}publicvoidcancelOrder(String orderId){System.out.println("核心业务:取消订单 "+ orderId);}}步骤3:实现 MethodInterceptor 接口
importnet.sf.cglib.proxy.MethodInterceptor;importnet.sf.cglib.proxy.MethodProxy;importjava.lang.reflect.Method;/** * CGLIB 方法拦截器:编写增强逻辑 */publicclassLogMethodInterceptorimplementsMethodInterceptor{/** * @param obj 代理对象(目标类的子类实例) * @param method 目标方法对象 * @param args 目标方法参数 * @param proxy 方法代理对象,用于调用目标方法 */@OverridepublicObjectintercept(Object obj,Method method,Object[] args,MethodProxy proxy)throwsThrowable{Object result =null;try{// 前置增强:日志记录System.out.println("[CGLIB 日志] 执行方法:"+ method.getName()+",参数:"+(args ==null?"无": args[0]));// 调用目标方法(通过 MethodProxy 调用,性能更高) result = proxy.invokeSuper(obj, args);// 后置增强:事务提交System.out.println("[CGLIB 事务] 方法执行成功,事务提交");}catch(Exception e){// 异常增强:事务回滚System.out.println("[CGLIB 事务] 方法执行异常,事务回滚,异常信息:"+ e.getMessage());throw e;}return result;}}步骤4:生成代理对象并测试
importnet.sf.cglib.proxy.Enhancer;/** * CGLIB 动态代理测试类 */publicclassCglibDynamicProxyTest{publicstaticvoidmain(String[] args){// 1. 创建增强器对象Enhancer enhancer =newEnhancer();// 2. 设置目标类的字节码(父类) enhancer.setSuperclass(OrderService.class);// 3. 设置方法拦截器 enhancer.setCallback(newLogMethodInterceptor());// 4. 生成代理对象(目标类的子类实例)OrderService proxy =(OrderService) enhancer.create();// 5. 通过代理对象调用方法System.out.println("===== 正常调用 ====="); proxy.createOrder("ORDER_20260129");System.out.println("---------------------------"); proxy.cancelOrder("ORDER_20260129");System.out.println("\n===== 异常调用 =====");try{ proxy.createOrder(null);}catch(Exception e){// 捕获异常}}}1.5.5 运行结果与分析
===== 正常调用 ===== [CGLIB 日志] 执行方法:createOrder,参数:ORDER_20260129 核心业务:创建订单 ORDER_20260129 [CGLIB 事务] 方法执行成功,事务提交 --------------------------- [CGLIB 日志] 执行方法:cancelOrder,参数:ORDER_20260129 核心业务:取消订单 ORDER_20260129 [CGLIB 事务] 方法执行成功,事务提交 ===== 异常调用 ===== [CGLIB 日志] 执行方法:createOrder,参数:null 核心业务:创建订单 null [CGLIB 事务] 方法执行异常,事务回滚,异常信息:null 1.5.6 CGLIB 动态代理的注意事项
⚠️ 1. 目标类不能是 final 类:CGLIB 通过生成子类实现代理,如果目标类是 final 类,无法生成子类,会抛出 IllegalArgumentException 异常。
⚠️ 2. 目标方法不能是 final 方法:final 方法不能被子类重写,因此无法被 CGLIB 增强。
⚠️ 3. 性能优于 JDK 动态代理:CGLIB 基于字节码操作,直接生成字节码,性能比基于反射的 JDK 动态代理更高。
1.6 JDK 动态代理 vs CGLIB 动态代理
为了帮助大家更好地选择合适的动态代理技术,我们对 JDK 动态代理和 CGLIB 动态代理进行全面对比:
| 对比维度 | JDK 动态代理 | CGLIB 动态代理 |
|---|---|---|
| 底层实现 | Java 反射机制 | ASM 字节码增强技术 |
| 目标类要求 | 必须实现接口 | 可以不实现接口,不能是 final 类 |
| 代理对象生成方式 | 实现目标类的接口 | 生成目标类的子类 |
| 性能 | 较低(反射调用) | 较高(直接操作字节码) |
| 核心 API | InvocationHandler、Proxy | MethodInterceptor、Enhancer |
| 适用场景 | 目标类实现接口的情况 | 目标类未实现接口的情况 |
| Spring AOP 默认选择 | 目标类有接口时 | 目标类无接口时 |
✅ 核心结论:在实际开发中,优先使用 JDK 动态代理(符合面向接口编程思想),当目标类没有实现接口时,再使用 CGLIB 动态代理。
1.7 动态代理的实战应用场景
动态代理在 Java 开发中应用广泛,尤其是在框架开发和业务系统设计中,以下是几个典型的实战应用场景。
1.7.1 场景1:基于动态代理的 AOP 日志框架
💡 核心需求:实现一个轻量级的 AOP 日志框架,自动记录所有业务方法的调用日志,包括方法名、参数、执行时间等信息。
代码实现
importjava.lang.reflect.InvocationHandler;importjava.lang.reflect.Method;importjava.lang.reflect.Proxy;importjava.util.Arrays;/** * AOP 日志框架:JDK 动态代理实现 */// 日志注解:标记需要记录日志的方法public@interfaceLoggable{}// 通用日志调用处理器publicclassLogInvocationHandlerimplementsInvocationHandler{privatefinalObject target;publicLogInvocationHandler(Object target){this.target = target;}@OverridepublicObjectinvoke(Object proxy,Method method,Object[] args)throwsThrowable{// 判断方法是否标记了 @Loggable 注解if(method.isAnnotationPresent(Loggable.class)){long startTime =System.currentTimeMillis();String methodName = method.getName();String params =Arrays.toString(args);// 前置日志System.out.println("[AOP 日志] 方法 "+ methodName +" 开始执行,参数:"+ params);// 执行目标方法Object result = method.invoke(target, args);// 后置日志long endTime =System.currentTimeMillis();System.out.println("[AOP 日志] 方法 "+ methodName +" 执行完毕,耗时:"+(endTime - startTime)+"ms,返回值:"+ result);return result;}else{// 未标记注解的方法,直接执行return method.invoke(target, args);}}// 生成代理对象的工具方法@SuppressWarnings("unchecked")publicstatic<T>TcreateProxy(T target){return(T)Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(),newLogInvocationHandler(target));}}// 业务接口publicinterfaceProductService{@LoggableStringgetProductById(String productId);StringgetProductName(String productId);}// 业务实现类publicclassProductServiceImplimplementsProductService{@OverridepublicStringgetProductById(String productId){// 模拟业务逻辑耗时try{Thread.sleep(100);}catch(InterruptedException e){ e.printStackTrace();}return"产品ID:"+ productId +",名称:Java 编程思想";}@OverridepublicStringgetProductName(String productId){return"Java 编程思想";}}// 测试类publicclassAopLogTest{publicstaticvoidmain(String[] args){ProductService target =newProductServiceImpl();ProductService proxy =LogInvocationHandler.createProxy(target);// 标记 @Loggable 的方法,会记录日志 proxy.getProductById("P1001");// 未标记 @Loggable 的方法,不会记录日志System.out.println("---------------------------"); proxy.getProductName("P1001");}}运行结果
[AOP 日志] 方法 getProductById 开始执行,参数:[P1001] [AOP 日志] 方法 getProductById 执行完毕,耗时:101ms,返回值:产品ID:P1001,名称:Java 编程思想 --------------------------- 1.7.2 场景2:基于动态代理的权限校验框架
💡 核心需求:实现一个权限校验框架,在方法执行前自动校验用户的权限,无权限则抛出异常。
代码实现
importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;importjava.lang.reflect.InvocationHandler;importjava.lang.reflect.Method;importjava.lang.reflect.Proxy;importjava.util.HashSet;importjava.util.Set;/** * 权限校验注解:标记方法所需的权限 */@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public@interfaceRequiresPermission{String[]value();}/** * 用户上下文:存储当前登录用户的权限 */publicclassUserContext{privatestaticfinalThreadLocal<Set<String>> userPermissions =newThreadLocal<>();publicstaticvoidsetUserPermissions(String... permissions){Set<String> permissionSet =newHashSet<>();for(String permission : permissions){ permissionSet.add(permission);} userPermissions.set(permissionSet);}publicstaticSet<String>getUserPermissions(){return userPermissions.get();}publicstaticvoidclear(){ userPermissions.remove();}}/** * 权限校验调用处理器 */publicclassPermissionInvocationHandlerimplementsInvocationHandler{privatefinalObject target;publicPermissionInvocationHandler(Object target){this.target = target;}@OverridepublicObjectinvoke(Object proxy,Method method,Object[] args)throwsThrowable{// 判断方法是否需要权限校验if(method.isAnnotationPresent(RequiresPermission.class)){RequiresPermission annotation = method.getAnnotation(RequiresPermission.class);String[] requiredPermissions = annotation.value();Set<String> userPermissions =UserContext.getUserPermissions();// 校验权限boolean hasPermission =false;for(String permission : requiredPermissions){if(userPermissions.contains(permission)){ hasPermission =true;break;}}if(!hasPermission){thrownewSecurityException("权限不足,所需权限:"+String.join(",", requiredPermissions));}}// 执行目标方法return method.invoke(target, args);}// 生成代理对象的工具方法@SuppressWarnings("unchecked")publicstatic<T>TcreateProxy(T target){return(T)Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(),newPermissionInvocationHandler(target));}}// 业务接口publicinterfaceAdminService{@RequiresPermission({"admin:user:add"})voidaddUser(String username);@RequiresPermission({"admin:user:delete"})voiddeleteUser(String userId);}// 业务实现类publicclassAdminServiceImplimplementsAdminService{@OverridepublicvoidaddUser(String username){System.out.println("添加用户成功:"+ username);}@OverridepublicvoiddeleteUser(String userId){System.out.println("删除用户成功:"+ userId);}}// 测试类publicclassPermissionTest{publicstaticvoidmain(String[] args){AdminService target =newAdminServiceImpl();AdminService proxy =PermissionInvocationHandler.createProxy(target);// 测试1:用户拥有 admin:user:add 权限System.out.println("===== 有权限调用 =====");UserContext.setUserPermissions("admin:user:add"); proxy.addUser("王五");// 测试2:用户没有 admin:user:delete 权限System.out.println("\n===== 无权限调用 =====");try{ proxy.deleteUser("1003");}catch(Exception e){System.out.println(e.getMessage());}UserContext.clear();}}运行结果
===== 有权限调用 ===== 添加用户成功:王五 ===== 无权限调用 ===== 权限不足,所需权限:admin:user:delete 1.7.3 场景3:Spring AOP 中的动态代理应用
💡 Spring AOP 是动态代理的典型应用,它底层同时支持 JDK 动态代理和 CGLIB 动态代理:
- 当目标类实现接口时,Spring AOP 默认使用 JDK 动态代理。
- 当目标类没有实现接口时,Spring AOP 自动切换为 CGLIB 动态代理。
- 开发者可以通过配置
proxy-target-class="true",强制 Spring AOP 使用 CGLIB 动态代理。
Spring AOP 通过动态代理,实现了横切关注点的统一管理,如日志、事务、权限等功能,大幅提升了代码的复用性和扩展性。
1.8 动态代理的优缺点与最佳实践
1.8.1 动态代理的优点
- 解耦核心业务与增强逻辑:将通用功能从核心业务中抽离,实现关注点分离。
- 代码复用性高:一套增强逻辑可以复用在多个目标类上,避免重复编码。
- 扩展性强:新增增强功能时,只需修改代理逻辑,无需修改目标类代码。
- 符合开闭原则:对扩展开放,对修改关闭,是优秀的设计模式实践。
1.8.2 动态代理的缺点
- 性能损耗:JDK 动态代理基于反射,存在一定的性能损耗;CGLIB 性能较高,但也比直接调用目标方法慢。
- 调试困难:代理对象是运行时生成的,调试时难以跟踪代码执行流程。
- 技术门槛高:动态代理涉及反射、字节码操作等高级技术,对开发者的技术水平要求较高。
1.8.3 动态代理的最佳实践
- 优先使用 JDK 动态代理:符合面向接口编程思想,代码更具规范性和可维护性。
- 合理选择增强时机:对于性能敏感的场景,尽量减少动态代理的使用,或选择 CGLIB 动态代理。
- 结合注解使用:通过注解标记需要增强的方法,实现增强逻辑的精准控制。
- 使用成熟框架:优先使用 Spring AOP 等成熟框架,避免重复造轮子,提升开发效率。
- 做好异常处理:在增强逻辑中处理目标方法的异常,避免异常扩散导致程序崩溃。
1.9 实战案例:基于动态代理的事务管理框架
1.9.1 需求分析
💡 实现一个轻量级的事务管理框架,要求:
- 通过注解标记需要事务管理的方法。
- 支持事务的自动提交和回滚。
- 事务管理逻辑与核心业务逻辑解耦。
- 支持 JDK 动态代理和 CGLIB 动态代理。
1.9.2 代码实现
importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;importjava.lang.reflect.InvocationHandler;importjava.lang.reflect.Method;importjava.lang.reflect.Proxy;importnet.sf.cglib.proxy.Enhancer;importnet.sf.cglib.proxy.MethodInterceptor;importnet.sf.cglib.proxy.MethodProxy;/** * 事务注解:标记需要事务管理的方法 */@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public@interfaceTransactional{}/** * 事务管理器:模拟事务的提交和回滚 */publicclassTransactionManager{publicvoidbeginTransaction(){System.out.println("[事务] 开启事务");}publicvoidcommit(){System.out.println("[事务] 提交事务");}publicvoidrollback(){System.out.println("[事务] 回滚事务");}}/** * JDK 动态代理事务处理器 */publicclassJdkTransactionInvocationHandlerimplementsInvocationHandler{privatefinalObject target;privatefinalTransactionManager transactionManager =newTransactionManager();publicJdkTransactionInvocationHandler(Object target){this.target = target;}@OverridepublicObjectinvoke(Object proxy,Method method,Object[] args)throwsThrowable{Object result =null;// 判断方法是否需要事务管理if(method.isAnnotationPresent(Transactional.class)){ transactionManager.beginTransaction();try{ result = method.invoke(target, args); transactionManager.commit();}catch(Exception e){ transactionManager.rollback();throw e;}}else{ result = method.invoke(target, args);}return result;}@SuppressWarnings("unchecked")publicstatic<T>TcreateJdkProxy(T target){return(T)Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(),newJdkTransactionInvocationHandler(target));}}/** * CGLIB 动态代理事务拦截器 */publicclassCglibTransactionInterceptorimplementsMethodInterceptor{privatefinalObject target;privatefinalTransactionManager transactionManager =newTransactionManager();publicCglibTransactionInterceptor(Object target){this.target = target;}@OverridepublicObjectintercept(Object obj,Method method,Object[] args,MethodProxy proxy)throwsThrowable{Object result =null;if(method.isAnnotationPresent(Transactional.class)){ transactionManager.beginTransaction();try{ result = proxy.invokeSuper(obj, args); transactionManager.commit();}catch(Exception e){ transactionManager.rollback();throw e;}}else{ result = proxy.invokeSuper(obj, args);}return result;}@SuppressWarnings("unchecked")publicstatic<T>TcreateCglibProxy(T target){Enhancer enhancer =newEnhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setCallback(newCglibTransactionInterceptor(target));return(T) enhancer.create();}}/** * 业务接口 */publicinterfaceAccountService{@Transactionalvoidtransfer(String fromAccount,String toAccount,double amount);}/** * 业务实现类 */publicclassAccountServiceImplimplementsAccountService{@Overridepublicvoidtransfer(String fromAccount,String toAccount,double amount){System.out.println("核心业务:从账户 "+ fromAccount +" 转账 "+ amount +" 到账户 "+ toAccount);// 模拟转账异常if(amount <0){thrownewIllegalArgumentException("转账金额不能为负数");}}}/** * 测试类 */publicclassTransactionTest{publicstaticvoidmain(String[] args){// JDK 动态代理测试System.out.println("===== JDK 动态代理事务测试 =====");AccountService jdkProxy =JdkTransactionInvocationHandler.createJdkProxy(newAccountServiceImpl());// 正常转账 jdkProxy.transfer("A1001","A1002",1000.0);System.out.println("---------------------------");// 异常转账try{ jdkProxy.transfer("A1001","A1002",-500.0);}catch(Exception e){System.out.println("转账失败:"+ e.getMessage());}// CGLIB 动态代理测试(无接口类)System.out.println("\n===== CGLIB 动态代理事务测试 =====");OrderService cglibProxy =CglibTransactionInterceptor.createCglibProxy(newOrderService()); cglibProxy.createOrder("ORDER_20260129");}}1.9.3 运行结果
===== JDK 动态代理事务测试 ===== [事务] 开启事务 核心业务:从账户 A1001 转账 1000.0 到账户 A1002 [事务] 提交事务 --------------------------- [事务] 开启事务 核心业务:从账户 A1001 转账 -500.0 到账户 A1002 [事务] 回滚事务 转账失败:转账金额不能为负数 ===== CGLIB 动态代理事务测试 ===== [CGLIB 日志] 执行方法:createOrder,参数:ORDER_20260129 核心业务:创建订单 ORDER_20260129 [CGLIB 事务] 方法执行成功,事务提交 1.9.4 案例总结
✅ 这个事务管理框架综合运用了 JDK 动态代理 和 CGLIB 动态代理 技术,核心亮点如下:
- 通过
@Transactional注解标记需要事务管理的方法,配置简单。 - 事务管理逻辑与核心业务逻辑完全解耦,符合关注点分离原则。
- 支持两种动态代理方式,适配不同的目标类场景。
- 实现了事务的自动提交和回滚,保证了数据的一致性。
1.10 本章总结
- 动态代理是代理模式的高级实现,分为 JDK 动态代理 和 CGLIB 动态代理 两类。
- JDK 动态代理基于反射机制,要求目标类实现接口;CGLIB 动态代理基于字节码增强,支持无接口类。
- 动态代理的核心是 运行时生成代理对象,增强目标方法功能,是实现 AOP 编程的核心技术。
- 动态代理广泛应用于日志记录、权限校验、事务管理等场景,是 Spring AOP 等框架的底层实现。
- 动态代理的最佳实践是“优先使用 JDK 动态代理,结合注解精准控制,使用成熟框架提升效率”。
- 动态代理的本质是解耦核心业务与增强逻辑,提升代码的复用性和扩展性,符合开闭原则。