通过反射调用@Transactional方法时Spring未创建新事务的问题排查
你猜的没错,反射直接调用原始对象的方法确实会让Spring忽略@Transactional注解——因为Spring的事务功能是靠动态代理实现的,只有通过代理对象调用方法时,事务拦截器才会介入,解析注解并处理事务逻辑。而你现在用handleMethod.invoke(handler)调用的是原始对象的方法,完全绕开了Spring的代理层,自然不会创建新事务。
下面给你几个可行的方案,按需选择:
方案1:确保调用的是代理对象的方法
如果你的handler是Spring容器管理的Bean,那不要自己实例化或返回原始对象,必须从Spring容器中获取代理后的实例(比如用ApplicationContext.getBean()或者依赖注入)。拿到代理对象后,再用反射调用它的方法,这样就会触发事务拦截器。
举个例子,如果autowireHandler是你自己写的方法,检查它是不是返回了原始对象,改成从Spring容器获取:
// 替换成从ApplicationContext获取代理对象 Object handler = applicationContext.getBean(handlerClass);
方案2:封装到Spring管理的事务方法中
把反射调用的逻辑包装到一个被Spring管理的Bean方法里,给这个方法加上@Transactional(propagation = Propagation.REQUIRES_NEW),然后调用这个Bean的方法,间接触发事务:
@Component public class TransactionalMethodInvoker { @Transactional(propagation = Propagation.REQUIRES_NEW) public void invokeWithNewTransaction(Object target, Method method) throws Exception { method.invoke(target); } }
然后修改你的调用代码:
// 注入TransactionalMethodInvoker @Autowired private TransactionalMethodInvoker methodInvoker; // ... 其他代码 runnable = () -> { LOGGER.info("executing method {} on {}",handleMethod,handler); try { methodInvoker.invokeWithNewTransaction(handler, handleMethod); } catch (Exception e) { // 处理异常 LOGGER.error("Method invocation failed", e); throw new RuntimeException(e); } }; runnable.run();
这个方案的核心是让调用经过Spring代理的方法,从而触发事务注解逻辑。
方案3:手动用TransactionTemplate管理事务
如果不想依赖代理,也可以手动创建新事务,用TransactionTemplate来控制事务传播行为:
// 注入Spring的事务管理器 @Autowired private PlatformTransactionManager transactionManager; // ... 其他代码 runnable = () -> { LOGGER.info("executing method {} on {}",handleMethod,handler); TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); // 设置为REQUIRES_NEW,强制创建新事务 transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); transactionTemplate.execute(status -> { try { handleMethod.invoke(handler); return null; } catch (Exception e) { // 标记事务回滚 status.setRollbackOnly(); throw new RuntimeException("Method execution failed", e); } }); }; runnable.run();
这种方式更灵活,完全手动控制事务生命周期,不需要依赖代理对象。
修改后可以看日志,如果看到类似下面的内容,说明新事务创建成功了:
DEBUG o.springframework.orm.jpa.JpaTransactionManager - Creating new transaction with name [...]: PROPAGATION_REQUIRES_NEW
内容的提问来源于stack exchange,提问作者The Shoe Shiner

