在Spring bean中启动新的事务
在Spring bean中启动新的事务
我们有:
@Transactional(propagation = Propagation.REQUIRED) public class MyClass implements MyInterface { ...
MyInterface有一个方法:go()
。
当go()方法执行时,我们会启动一个新的事务,在方法完成后提交或回滚 - 这很好。
现在假设在go()方法中,我们调用了MyClass中一个带有@Transactional(propagation = Propagation.REQUIRES_NEW
的私有方法。似乎Spring“忽略”了REQUIRES_NEW注解,并没有启动一个新的事务。我认为这是因为Spring AOP在接口级别(MyInterface)上操作,并不拦截对MyClass方法的任何调用。这个理解正确吗?
有没有办法在go()事务内启动一个新的事务?唯一的方法是调用另一个已配置为REQUIRES_NEW的Spring管理的bean吗?
更新:补充说明,当客户端执行go()
时,是通过对接口的引用而不是类来执行的:
@Autowired MyInterface impl; impl.go();
在Spring bean中启动新事务的原因是为了实现事务行为。要在同一个bean中调用"REQUIRES_NEW"事务,可以通过创建一个"self"引用来实现。但在Spring中,这并不是直接可行的,需要使用注解进行注入。
下面是实现的代码示例:
@Service("someService") public class ServiceImpl implements Service { @Autowired @Qualifier("someService") private Service selfReference; public void firstMethod() { selfReference.secondMethod(); } @Transactional(propagation = Propagation.REQUIRES_NEW) public void secondMethod() { // 在新的事务中执行操作 } }
在`firstMethod`中调用`selfReference.secondMethod()`会调用代理对象而不是`this`,这样就可以实现"REQUIRES_NEW"事务的工作。
在Spring bean中启动新事务的问题:
问题原因:Spring AOP的工作方式决定了只能在公共方法上起作用。即使
在MyClass的公共方法上,Spring也不会识别它,除非该方法在接口中定义。
解决方法:可以使用TransactionTemplate以编程方式启动新事务。具体步骤如下:
1. 创建TransactionTemplate对象,并传入事务管理器(TransactionManager)作为参数。
2. 设置事务传播行为为PROPAGATION_REQUIRES_NEW,表示启动一个新事务。
3. 使用execute方法执行需要在事务中执行的代码块。
代码示例:
TransactionTemplate txTemplate = new TransactionTemplate(txManager); txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); txTemplate.execute(status -> { // do stuff });
补充说明:如果MyClass实现了一个接口,Spring只会使用该接口生成事务代理,并忽略所有公共的、非接口方法。如果MyClass没有实现接口,则所有公共方法都会被使用。
需要注意的是,无论是接口还是类,方法需要通过代理来执行,且必须是一个注入的依赖项。
通过使用TransactionTemplate以编程方式启动新事务,可以解决在Spring bean中启动新事务的问题。
Spring bean中启动新事务的问题通常是由于以下原因导致的:
- Spring只会拦截通过代理调用的方法,因此在同一个类中调用方法不会启动新的事务。
- 使用@Transactional
注解时,只有公共方法才会被拦截,保护、私有或包可见的方法不会被拦截。
- 在使用代理模式时,默认情况下只有通过代理调用的方法才会被拦截。
为了解决这个问题,可以采取以下方法:
- 使用@Transactional
注解时,只对公共方法使用该注解。
- 使用aspectj
模式进行事务设置,从而在类中进行代码织入,而不是在运行时创建代理。
- 在类中获取Spring代理,并调用该代理的方法,而不是直接调用this
。
下面是一个示例代码:
(propagation = Propagation.REQUIRED) public class SomeService { private ApplicationContext applicationContext; private SomeService getSpringProxy() { return applicationContext.getBean(this.getClass()); } private void doSomeAndThenMore() { // 替代 // this.doSometingPublicly(); // 使用以下方式在事务中运行 getSpringProxy().doSometingPublicly(); } public void doSometingPublicly() { // 在此执行一些需要事务的操作 } }
更多详细信息请参考Spring参考文档中的事务声明式注解部分。