如果我在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();
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
问题不是私有的或公共的,问题是:如何调用它以及您使用哪个AOP实现!
如果使用(默认)Spring Proxy AOP,那么只有在调用通过代理时才会考虑Spring提供的所有AOP功能(如@Transactional)。—如果从另一个bean调用带注释的方法,通常会出现这种情况。
这有两个含义:
因为不能从另一个bean调用私有方法(例外是反射),所以不会考虑它们的@Transactional Annotation。
如果方法是公共的,但它是从同一个bean调用的,那么它也不会被考虑在内(这句话只在(默认)使用Spring Proxy AOP的情况下是正确的)。
@参见Spring Reference: Chapter 9.6代理机制
恕我直言,您应该使用aspectJ模式,而不是Spring代理,这将克服这个问题。AspectJ事务方面甚至被编织到私有方法中(Spring 3.0检查)。
默认情况下,@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引用:使用@Transactional
Note: In proxy mode (which is the default), only 'external' method calls coming in through the proxy will be intercepted. This means that 'self-invocation', i.e. a method within the target object calling some other method of the target object, won't lead to an actual transaction at runtime even if the invoked method is marked with @Transactional!
Consider the use of AspectJ mode (see below) if you expect self-invocations to be wrapped with transactions as well. In this case, there won't be a proxy in the first place; instead, the target class will be 'weaved' (i.e. its byte code will be modified) in order to turn @Transactional into runtime behavior on any kind of method.
与@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