如果
@Transactional
注解在公共方法上,它会起作用并开启一个事务。public class Bean {
public void doStuff() {
doPrivateStuff();
}
@Transactional
private void doPrivateStuff() {
}
}
...
Bean bean = (Bean)appContext.getBean("bean");
bean.doStuff();
@Transactional
注解在公共方法上,它会起作用并开启一个事务。public class Bean {
public void doStuff() {
doPrivateStuff();
}
@Transactional
private void doPrivateStuff() {
}
}
...
Bean bean = (Bean)appContext.getBean("bean");
bean.doStuff();
@Transactional
将不起作用。代理生成器将忽略它们。@Transactional
当使用代理时,您应该仅将@Transactional
注释应用于具有公共可见性的方法。如果您使用@Transactional
注释保护、私有或包可见方法,则不会引发错误,但是注释的方法不会展示配置的事务设置。如果需要注释非公共方法,请考虑使用AspectJ(请参见下文)。默认情况下,@Transactional
属性仅在从 applicationContext 获取的引用上调用带有注解的方法时才起作用。
public class Bean {
public void doStuff() {
doTransactionStuff();
}
@Transactional
public void doTransactionStuff() {
}
}
Bean bean = (Bean)appContext.getBean("bean");
bean.doTransactionStuff();
这不会:
Bean bean = (Bean)appContext.getBean("bean");
bean.doStuff();
注意:在代理模式下(默认情况下),只有通过代理传入的“外部”方法调用将被拦截。这意味着,即使被调用的方法标记有
@Transactional
,在运行时,目标对象内的一个方法调用另一个方法不会导致实际事务!如果您希望自调用也被包装在事务中,请考虑使用AspectJ模式(请参见下文)。在这种情况下,首先不会有代理;相反,目标类将被“编织”(即其字节码将被修改),以便在任何类型的方法上将
@Transactional
转换为运行时行为。
TransactionTemplate
。@Service
public class MyService {
@Autowired
private TransactionTemplate transactionTemplate;
private void process() {
transactionTemplate.executeWithoutResult(status -> processInTransaction());
}
private void processInTransaction(){
//...
}
}
是的,可以在私有方法上使用@Transactional,但正如其他人所提到的,这并不能直接奏效。您需要使用AspectJ。我花了一些时间才弄清楚如何让它起作用。我将分享我的结果。
我选择使用编译时织入而不是加载时织入,因为我认为这是一个更好的选择。此外,我正在使用Java 8,因此您可能需要调整一些参数。
首先,添加aspectjrt的依赖项。
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.8</version>
</dependency>
然后在Maven中添加AspectJ插件进行实际的字节码织入(这可能不是最小示例)。
<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构建进行测试以进行合理检查。
Spring Docs中解释说:
在代理模式下(默认情况下),只有通过代理传入的外部方法调用会被拦截。这意味着自调用实际上是指目标对象内的一个方法调用另一个目标对象的方法,即使所调用的方法标记有@Transactional,在运行时也不会导致实际事务。
如果您希望自调用也被包装成事务,请考虑使用AspectJ模式(请参见下表中的mode属性)。在这种情况下,首先不会有代理; 相反,目标类将被编织(即其字节码将被修改),以便在任何类型的方法上将@Transactional转化为运行时行为。
另一种方法是使用BeanSelfAware
BeanSelfAware
的引用?它似乎不是Spring框架的类。 - asgs与@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
是否重用现有的事务,但这段代码肯定会这样做。
@Transactional([^{](?!public))+ \{
来查找在我们的代码库中不会产生任何影响(因为它们位于私有、受保护或包私有方法上)的可能注释。当然,它不能找到对公共方法的“无代理自引用”调用 - 是否有插件或其他工具可以发现这些问题? - icyerasor