Java Spring @Transactional 属性是否适用于私有方法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4396284/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Does Spring @Transactional attribute work on a private method?
提问by Juha Syrj?l?
If I have a @Transactional-annotation on a private method in a Spring bean, does the annotation have any effect?
如果我在 Spring bean 中的私有方法上有@Transactional注释,该注释有什么作用吗?
If the @Transactional
annotation is on a public method, it works and open a transaction.
如果@Transactional
注释在公共方法上,它会起作用并打开一个事务。
public class Bean {
public void doStuff() {
doPrivateStuff();
}
@Transactional
private void doPrivateStuff() {
}
}
...
Bean bean = (Bean)appContext.getBean("bean");
bean.doStuff();
采纳答案by Ralph
The Question is not private or public, the question is: How is it invoked and which AOP implementation you use!
问题不是私有的或公开的,问题是:它是如何调用的以及您使用的是哪种 AOP 实现!
If you use (default) Spring Proxy AOP, then all AOP functionality provided by Spring (like @Transational
) will only be taken into account if the call goes through the proxy. -- This is normally the case if the annotated method is invoked from anotherbean.
如果您使用(默认)Spring Proxy AOP,那么@Transational
只有在调用通过代理时才会考虑Spring 提供的所有 AOP 功能(如)。-- 如果注释的方法是从另一个bean调用的,则通常是这种情况。
This has two implications:
这有两个含义:
- Because private methods must not be invoked from another bean (the exception is reflection), their
@Transactional
Annotation is not taken into account. - If the method is public, but it is invoked from the same bean, it will not be taken into account either (this statement is only correct if (default) Spring Proxy AOP is used).
- 因为不能从另一个 bean 调用私有方法(反射是例外),
@Transactional
所以不考虑它们的Annotation。 - 如果该方法是公共的,但它是从同一个 bean 调用的,则也不会考虑它(此语句仅在使用(默认)Spring Proxy AOP 时才正确)。
@See Spring Reference: Chapter 9.6 9.6 Proxying mechanisms
@See Spring 参考:第 9.6 章 9.6 代理机制
IMHO you should use the aspectJ mode, instead of the Spring Proxies, that will overcome the problem. And the AspectJ Transactional Aspects are woven even into private methods (checked for Spring 3.0).
恕我直言,您应该使用 aspectJ 模式,而不是 Spring Proxies,这将克服这个问题。并且 AspectJ 事务方面甚至被编织到私有方法中(检查 Spring 3.0)。
回答by skaffman
The answer your question is no - @Transactional
will have no effect if used to annotate private methods. The proxy generator will ignore them.
您的问题的答案是否定的 -@Transactional
如果用于注释私有方法将无效。代理生成器将忽略它们。
This is documented in Spring Manual chapter 10.5.6:
这在Spring 手册第 10.5.6 章中有记录:
Method visibility and
@Transactional
When using proxies, you should apply the
@Transactional
annotation only to methods with public visibility. If you do annotate protected, private or package-visible methods with the@Transactional
annotation, no error is raised, but the annotated method does not exhibit the configured transactional settings. Consider the use of AspectJ (see below) if you need to annotate non-public methods.
方法可见性和
@Transactional
使用代理时,您应该仅将
@Transactional
注释应用于具有公共可见性的方法。如果您使用注释对受保护的、私有的或包可见的方法进行@Transactional
注释,则不会引发错误,但带注释的方法不会显示配置的事务设置。如果您需要注释非公共方法,请考虑使用 AspectJ(见下文)。
回答by ivy
The answer is no. Please see Spring Reference: Using @Transactional:
答案是不。请参阅Spring 参考:使用 @Transactional:
The
@Transactional
annotation may be placed before an interface definition, a method on an interface, a class definition, or a publicmethod on a class
所述
@Transactional
注释可以应用于接口定义被放置,一个方法的接口,类定义,或在公共的一类方法
回答by Juha Syrj?l?
By default the @Transactional
attribute works only when calling an annotated method on a reference obtained from applicationContext.
默认情况下,该@Transactional
属性仅在对从 applicationContext 获得的引用调用带注释的方法时才起作用。
public class Bean {
public void doStuff() {
doTransactionStuff();
}
@Transactional
public void doTransactionStuff() {
}
}
This will open a transaction:
这将打开一个交易:
Bean bean = (Bean)appContext.getBean("bean");
bean.doTransactionStuff();
This will not:
这不会:
Bean bean = (Bean)appContext.getBean("bean");
bean.doStuff();
Spring Reference: Using @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.
注意:在代理模式下(默认),只有通过代理进入的“外部”方法调用才会被拦截。这意味着“自调用”,即目标对象中的方法调用目标对象的其他方法,即使被调用的方法被标记为
@Transactional
!也不会在运行时导致实际事务。如果您希望自调用也与事务一起包装,请考虑使用 AspectJ 模式(见下文)。在这种情况下,首先不会有代理;相反,目标类将被“编织”(即其字节码将被修改),以便
@Transactional
在任何类型的方法上转换为运行时行为。
回答by user536161
Spring Docs explain that
Spring Docs 解释说
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.
在代理模式下(默认),只有通过代理进入的外部方法调用才会被拦截。这意味着自调用实际上是目标对象中的一个方法调用目标对象的另一个方法,即使被调用的方法用@Transactional 标记,也不会在运行时导致实际事务。
如果您希望自调用也与事务一起包装,请考虑使用 AspectJ 模式(请参阅下表中的模式属性)。在这种情况下,首先不会有代理;相反,目标类将被编织(即,其字节码将被修改),以便将 @Transactional 转换为任何类型方法的运行时行为。
Another way is user BeanSelfAware
另一种方式是用户BeanSelfAware
回答by James Watkins
Yes, it is possible to use @Transactional on private methods, but as others have mentioned this won't work out of the box. You need to use AspectJ. It took me some time to figure out how to get it working. I will share my results.
是的,可以在私有方法上使用 @Transactional,但正如其他人提到的那样,这不会开箱即用。您需要使用 AspectJ。我花了一些时间来弄清楚如何让它工作。我会分享我的结果。
I chose to use compile-time weaving instead of load-time weaving because I think it's an overall better option. Also, I'm using Java 8 so you may need to adjust some parameters.
我选择使用编译时编织而不是加载时编织,因为我认为这是一个整体更好的选择。另外,我使用的是 Java 8,因此您可能需要调整一些参数。
First, add the dependency for aspectjrt.
首先,添加对 aspectjrt 的依赖。
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.8</version>
</dependency>
Then add the AspectJ plugin to do the actual bytecode weaving in Maven (this may not be a minimal example).
然后添加 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>
Finally add this to your config class
最后将此添加到您的配置类
@EnableTransactionManagement(mode = AdviceMode.ASPECTJ)
Now you should be able to use @Transactional on private methods.
现在您应该可以在私有方法上使用 @Transactional。
One caveat to this approach: You will need to configure your IDE to be aware of AspectJ otherwise if you run the app via Eclipse for example it may not work. Make sure you test against a direct Maven build as a sanity check.
这种方法的一个警告:您需要配置您的 IDE 以了解 AspectJ,否则如果您通过 Eclipse 运行应用程序,例如它可能无法工作。确保针对直接 Maven 构建进行测试作为健全性检查。
回答by loonis
If you need to wrap a private method inside a transaction and don't want to use aspectj, you can use TransactionTemplate.
如果您需要在事务中包装私有方法并且不想使用 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(){
//...
}
}
回答by Lu55
Same way as @loonis suggestedto use TransactionTemplateone may use this helper component (Kotlin):
与@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()
}
}
Usage:
用法:
@Service
class SomeService(private val transactionalUtils: TransactionalUtils) {
fun foo() {
transactionalUtils.executeAsTransactional { transactionalFoo() }
}
private fun transactionalFoo() {
println("This method is executed within transaction")
}
}
Don't know whether TransactionTemplate
reuse existing transaction or not but this code definitely do.
不知道是否TransactionTemplate
重用现有的事务,但这段代码肯定可以。