JDK 动态代理动态织入事务详解

发布时间:2026/10/2 6:54:59
JDK 动态代理动态织入事务详解 JDK 动态代理动态织入事务详解一、目标手动实现事务代理Spring 的Transactional底层就是通过动态代理织入事务的。我们手动实现一个简化版彻底理解这个过程。需求Service 方法执行前开启事务执行成功提交事务抛出异常回滚事务。调用 Service 方法 → 代理拦截 → 开启事务 → 执行目标方法 → 成功 → 提交 → 异常 → 回滚 → 抛出异常二、完整代码实现1. 事务管理器模拟一个事务管理器负责开启、提交、回滚事务。publicclassTransactionManager{// 模拟 ThreadLocal 保存当前线程的事务状态privatestaticfinalThreadLocalBooleantransactionStatusnewThreadLocal();publicvoidbegin(){System.out.println(【事务】开启事务);transactionStatus.set(true);}publicvoidcommit(){System.out.println(【事务】提交事务);transactionStatus.remove();}publicvoidrollback(){System.out.println(【事务】回滚事务);transactionStatus.remove();}publicbooleanisActive(){returnBoolean.TRUE.equals(transactionStatus.get());}}2. 抽象主题publicinterfaceUserService{voidsave(Useruser);UserfindById(Longid);voidbatchSave(ListUserusers);}3. 真实主题publicclassUserServiceImplimplementsUserService{Overridepublicvoidsave(Useruser){System.out.println(保存用户user.getName());// 模拟数据库操作}OverridepublicUserfindById(Longid){System.out.println(查询用户id);returnnewUser(id,张三);}OverridepublicvoidbatchSave(ListUserusers){for(Useruser:users){System.out.println(批量保存user.getName());}// 模拟异常if(users.size()5){thrownewRuntimeException(批量保存失败);}}}4. 事务 InvocationHandler核心逻辑在invoke()方法中方法执行前开启事务成功提交异常回滚。publicclassTransactionInvocationHandlerimplementsInvocationHandler{// 目标对象privatefinalObjecttarget;// 事务管理器privatefinalTransactionManagertransactionManager;publicTransactionInvocationHandler(Objecttarget,TransactionManagertransactionManager){this.targettarget;this.transactionManagertransactionManager;}OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{// 1. 判断方法是否需要事务if(!needTransaction(method)){returnmethod.invoke(target,args);}// 2. 开启事务transactionManager.begin();Objectresultnull;try{// 3. 执行目标方法resultmethod.invoke(target,args);// 4. 方法正常返回提交事务transactionManager.commit();returnresult;}catch(Exceptione){// 5. 方法抛出异常回滚事务transactionManager.rollback();// 6. 把原始异常抛出去throwe.getCause()!null?e.getCause():e;}}/** * 判断方法是否需要事务 * 查询方法不需要事务增删改方法需要 */privatebooleanneedTransaction(Methodmethod){StringmethodNamemethod.getName();returnmethodName.startsWith(save)||methodName.startsWith(update)||methodName.startsWith(delete)||methodName.startsWith(batch);}}5. 客户端调用publicclassClient{publicstaticvoidmain(String[]args){// 1. 创建真实对象UserServicetargetnewUserServiceImpl();// 2. 创建事务管理器TransactionManagertxManagernewTransactionManager();// 3. 创建代理对象UserServiceproxy(UserService)Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),newTransactionInvocationHandler(target,txManager));// 4. 通过代理调用System.out.println( 正常保存 );proxy.save(newUser(1L,张三));System.out.println(\n 正常查询无事务);proxy.findById(1L);System.out.println(\n 批量保存异常 );try{proxy.batchSave(Arrays.asList(newUser(1L,A),newUser(2L,B),newUser(3L,C),newUser(4L,D),newUser(5L,E),newUser(6L,F)));}catch(Exceptione){System.out.println(捕获异常e.getMessage());}}}6. 输出结果 正常保存 【事务】开启事务 保存用户张三 【事务】提交事务 正常查询无事务 查询用户1 批量保存异常 【事务】开启事务 批量保存A 批量保存B 批量保存C 批量保存D 批量保存E 批量保存F 【事务】回滚事务 捕获异常批量保存失败三、代理类的结构Proxy.newProxyInstance()生成的代理类大致如下publicfinalclass$Proxy0extendsProxyimplementsUserService{privatestaticMethodm0;// saveprivatestaticMethodm1;// findByIdprivatestaticMethodm2;// batchSavepublic$Proxy0(InvocationHandlerh){super(h);}Overridepublicfinalvoidsave(Useruser){try{super.h.invoke(this,m0,newObject[]{user});}catch(Throwablee){thrownewUndeclaredThrowableException(e);}}OverridepublicfinalUserfindById(Longid){try{return(User)super.h.invoke(this,m1,newObject[]{id});}catch(Throwablee){thrownewUndeclaredThrowableException(e);}}OverridepublicfinalvoidbatchSave(ListUserusers){try{super.h.invoke(this,m2,newObject[]{users});}catch(Throwablee){thrownewUndeclaredThrowableException(e);}}}每个方法都把调用转发到InvocationHandler.invoke()事务逻辑就在这里织入。四、关键细节1. 异常处理method.invoke()抛出的异常会被包装成InvocationTargetException需要取出原始异常catch(Exceptione){transactionManager.rollback();// e 是 InvocationTargetException需要 getCause()throwe.getCause()!null?e.getCause():e;}2. 事务传播上面的实现是简化版每次调用都开启新事务。真实场景中需要处理事务传播当前没有事务时开启新事务。当前已有事务时加入当前事务REQUIRED。当前已有事务时挂起当前事务开启新事务REQUIRES_NEW。简化版传播处理OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{if(!needTransaction(method)){returnmethod.invoke(target,args);}// 如果当前已有事务直接加入if(transactionManager.isActive()){returnmethod.invoke(target,args);}// 没有事务开启新事务transactionManager.begin();try{Objectresultmethod.invoke(target,args);transactionManager.commit();returnresult;}catch(Exceptione){transactionManager.rollback();throwe.getCause()!null?e.getCause():e;}}3. 只读事务查询方法可以标记为只读优化性能privatebooleanisReadOnly(Methodmethod){Stringnamemethod.getName();returnname.startsWith(find)||name.startsWith(get)||name.startsWith(query)||name.startsWith(select);}4. 事务超时记录事务开始时间超时后自动回滚longstartSystem.currentTimeMillis();try{Objectresultmethod.invoke(target,args);longcostSystem.currentTimeMillis()-start;if(costtimeout){transactionManager.rollback();thrownewTransactionTimeoutException(事务超时);}transactionManager.commit();returnresult;}catch(Exceptione){transactionManager.rollback();throwe.getCause()!null?e.getCause():e;}五、内部调用失效问题这是动态代理最经典的坑。同一个类中方法 A 调用方法 BB 的事务不会生效。publicclassUserServiceImplimplementsUserService{Overridepublicvoidsave(Useruser){// 直接调用 this.batchSave()不经过代理batchSave(Arrays.asList(user,user));}OverridepublicvoidbatchSave(ListUserusers){// 事务不生效}}原因save()内部调用batchSave()时this是原始对象不是代理对象。只有外部通过代理调用才会触发InvocationHandler.invoke()。解决方案一注入自身代理ServicepublicclassUserServiceImplimplementsUserService{AutowiredprivateUserServiceself;// 注入代理对象Overridepublicvoidsave(Useruser){self.batchSave(Arrays.asList(user,user));// 通过代理调用}OverridepublicvoidbatchSave(ListUserusers){// 事务生效}}解决方案二使用 AopContextServicepublicclassUserServiceImplimplementsUserService{Overridepublicvoidsave(Useruser){((UserService)AopContext.currentProxy()).batchSave(Arrays.asList(user,user));}}需要在配置类上添加EnableAspectJAutoProxy(exposeProxy true)。解决方案三拆分类把batchSave移到另一个 Bean 中通过注入调用。六、JDK 动态代理的局限局限说明解决方案必须实现接口目标类没有接口时无法使用用 CGLIB只能代理接口方法实现类中额外的方法无法代理用 CGLIB性能开销反射调用比直接调用慢用 CGLIB 或缓存 Method内部调用失效this 调用不经过代理注入自身代理或 AopContext构造方法不执行代理类不调用目标类构造方法无影响目标对象已创建七、Spring 中事务代理的完整链路Spring 的Transactional底层由TransactionInterceptor实现它本质上就是一个MethodInterceptorCGLIB或配合InvocationHandlerJDK。调用 Transactional 方法 → 代理对象拦截 → TransactionInterceptor.invoke() → 获取 Transactional 属性传播行为、隔离级别、超时、只读 → 创建/加入事务 → 执行目标方法 → 成功 → commit → 异常 → rollback根据 rollbackFor 判断核心类类作用TransactionInterceptor事务拦截器织入事务逻辑PlatformTransactionManager事务管理器开启/提交/回滚TransactionAttributeSource解析Transactional注解属性TransactionInfo保存当前事务状态八、总结维度核心要点核心机制Proxy.newProxyInstance()InvocationHandler织入逻辑在invoke()中前后插入事务开启、提交、回滚事务管理器负责 begin、commit、rollback异常处理回滚后抛出原始异常内部调用不经过代理事务失效Spring 对应TransactionInterceptorPlatformTransactionManager代理选择有接口用 JDK无接口用 CGLIBJDK 动态代理织入事务的本质是在InvocationHandler.invoke()中围绕method.invoke(target, args)包裹事务的开启、提交和回滚逻辑。Spring 的Transactional就是把这段逻辑封装在了TransactionInterceptor中通过动态代理自动应用到所有需要事务的方法上。理解这个手动实现就能理解 Spring 事务代理的底层原理也能解释为什么内部调用会导致事务失效。