如果我在Spring bean中的私有方法上有一个@Transactional -annotation,那么这个annotation有任何效果吗?
如果@Transactional注释位于公共方法上,则它将工作并打开事务。
public class Bean {
public void doStuff() {
doPrivateStuff();
}
@Transactional
private void doPrivateStuff() {
}
}
...
Bean bean = (Bean)appContext.getBean("bean");
bean.doStuff();
与@loonis建议使用TransactionTemplate的方式相同,可以使用这个帮助组件(Kotlin):
@Component
class TransactionalUtils {
/**
* Execute any [block] of code (even private methods)
* as if it was effectively [Transactional]
*/
@Transactional
fun <R> executeAsTransactional(block: () -> R): R {
return block()
}
}
用法:
@Service
class SomeService(private val transactionalUtils: TransactionalUtils) {
fun foo() {
transactionalUtils.executeAsTransactional { transactionalFoo() }
}
private fun transactionalFoo() {
println("This method is executed within transaction")
}
}
不知道TransactionTemplate是否重用现有事务,但这段代码肯定会重用。
与@loonis建议使用TransactionTemplate的方式相同,可以使用这个帮助组件(Kotlin):
@Component
class TransactionalUtils {
/**
* Execute any [block] of code (even private methods)
* as if it was effectively [Transactional]
*/
@Transactional
fun <R> executeAsTransactional(block: () -> R): R {
return block()
}
}
用法:
@Service
class SomeService(private val transactionalUtils: TransactionalUtils) {
fun foo() {
transactionalUtils.executeAsTransactional { transactionalFoo() }
}
private fun transactionalFoo() {
println("This method is executed within transaction")
}
}
不知道TransactionTemplate是否重用现有事务,但这段代码肯定会重用。
Spring文档解释了这一点
In proxy mode (which is the default), only external method calls
coming in through the proxy are intercepted. This means that
self-invocation, in effect, a method within the target object calling
another method of the target object, will not lead to an actual
transaction at runtime even if the invoked method is marked with
@Transactional.
Consider the use of AspectJ mode (see mode attribute in table below)
if you expect self-invocations to be wrapped with transactions as
well. In this case, there will not be a proxy in the first place;
instead, the target class will be weaved (that is, its byte code will
be modified) in order to turn @Transactional into runtime behavior on
any kind of method.
另一种方法是使用BeanSelfAware
是的,可以在私有方法上使用@Transactional,但正如其他人提到的那样,这不能开箱即用。您需要使用AspectJ。我花了一些时间才弄清楚如何让它工作。我将分享我的成果。
我选择使用编译时编织而不是加载时编织,因为我认为这是一个整体上更好的选择。另外,我使用的是Java 8,所以你可能需要调整一些参数。
首先,添加aspectjrt的依赖项。
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.8</version>
</dependency>
然后添加AspectJ插件来在Maven中进行实际的字节码编织(这可能不是一个最小的示例)。
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.8</version>
<configuration>
<complianceLevel>1.8</complianceLevel>
<source>1.8</source>
<target>1.8</target>
<aspectLibraries>
<aspectLibrary>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</aspectLibrary>
</aspectLibraries>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
最后将其添加到配置类中
@EnableTransactionManagement(mode = AdviceMode.ASPECTJ)
现在您应该能够在私有方法上使用@Transactional。
这种方法需要注意的一点是:您需要将IDE配置为能够识别AspectJ,否则如果您通过Eclipse(例如)运行应用程序,它可能无法工作。确保对直接的Maven构建进行测试,作为完整性检查。