如果我对Spring bean中的私有方法有一个@Transactional注释,那么注释是否有效?
如果@Transactional
注释是在公共方法上,它可以工作并打开一个事务。
public class Bean {
public void doStuff() {
doPrivateStuff();
}
@Transactional
private void doPrivateStuff() {
}
}
...
Bean bean = (Bean)appContext.getBean("bean");
bean.doStuff();
答案 0 :(得分:205)
您的问题的答案是否定的 - 如果用于注释私有方法,@Transactional
将无效。代理生成器将忽略它们。
Spring Manual chapter 10.5.6中记录了这一点:
方法可见性和
@Transactional
使用代理时,您应该申请 仅
@Transactional
注释 具有公众可见度的方法。如果 你做注释保护,私人或 包装可见的方法用@Transactional
注释,没有错误 被提出,但注释方法 没有展示配置 交易设置。考虑一下 如果需要,可以使用AspectJ(见下文) 注释非公开方法。
答案 1 :(得分:132)
该问题不是私人的或公开的,问题是:如何调用它以及您使用哪种AOP实现!
如果您使用(默认)Spring Proxy AOP,那么只有当呼叫通过代理时,才会考虑Spring提供的所有AOP功能(如@Transational
)。 - 如果从另一个 bean调用带注释的方法,通常就是这种情况。
这有两个含义:
@Transactional
注释。@See Spring Reference: Chapter 9.6 9.6 Proxying mechanisms
恕我直言,你应该使用aspectJ模式,而不是Spring Proxies,这将克服这个问题。 AspectJ Transactional Aspects甚至编织成私有方法(检查Spring 3.0)。
答案 2 :(得分:28)
默认情况下,@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();
Spring Reference: Using @Transactional
注意:在代理模式(默认设置)下,只会拦截通过代理进入的“外部”方法调用。这意味着'自调用',即目标对象中调用目标对象的其他方法的方法,即使被调用的方法标有
@Transactional
,也不会在运行时导致实际的事务! / p>如果您希望自我调用也包含在事务中,请考虑使用AspectJ模式(见下文)。在这种情况下,首先不会有代理;相反,目标类将被“编织”(即其字节代码将被修改),以便将
@Transactional
转换为任何类型方法的运行时行为。
答案 3 :(得分:10)
是的,可以在私有方法上使用@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构建进行测试,作为完整性检查。
答案 4 :(得分:3)
答案是否定的。请参阅Spring Reference: Using @Transactional :
@Transactional
注释可以放在接口定义,接口上的方法,类定义或类上的 public 方法之前
答案 5 :(得分:3)
Spring Docs解释说
在代理模式(默认设置)下,只有外部方法调用 通过代理进入是截获的。这意味着 自调用,实际上是目标对象调用中的一个方法 目标对象的另一种方法,不会导致实际的 即使调用的方法被标记,运行时的事务也是如此 @Transactional。
考虑使用AspectJ模式(参见下表中的mode属性) 如果你希望自我调用包含在事务中 好。在这种情况下,首先不会有代理; 相反,目标类将被编织(即,它的字节代码将 被修改)以便将@Transactional变为运行时行为 任何一种方法。
另一种方式是用户 BeanSelfAware
答案 6 :(得分:3)
如果您需要在事务中包装私有方法,并且不想使用Aspectj,则可以使用TransactionTemplate。
@Service
public class MyService {
@Autowired
private TransactionTemplate transactionTemplate;
private void process(){
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
processInTransaction();
}
});
}
private void processInTransaction(){
//...
}
}
答案 7 :(得分:0)
与@loonis suggested使用TransactionTemplate的方式相同,可以使用此帮助程序组件(Kotlin):
1
用法:
@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")
}
}
是否重用现有事务,但是此代码肯定可以。