Spring @Transactional属性是否在私有方法上工作?

如果我在Spring bean的私有方法上有一个@Transactional -annotation,注释是否有效?

如果@Transactional注释在公共方法上,它将工作并打开一个事务。

 public class Bean { public void doStuff() { doPrivateStuff(); } @Transactional private void doPrivateStuff() { } } ... Bean bean = (Bean)appContext.getBean("bean"); bean.doStuff(); 

你的问题的答案是否定的 – 如果用于注释私有方法,@ @Transactional将不起作用。 代理生成器将忽略它们。

这在“ Spring手册”第10.5.6章中有logging:

方法可见性和@Transactional

在使用代理时,您应该将@Transactional注释仅应用于具有公开可见性的方法。 如果使用@Transactional注释标注protected,private或package-visible方法,则不会引发错误,但注释的方法不会显示已configuration的事务设置。 如果您需要注释非公共方法,请考虑使用AspectJ(请参见下文)。

问题不是私有的或公共的,问题是:它是如何被调用的,以及您使用的是哪个AOP实现!

如果使用(默认)Spring Proxy AOP,则Spring提供的所有AOPfunction(如@Transational )将只在呼叫通过代理时才被考虑。 – 如果从另一个 bean调用带注释的方法,通常就是这种情况。

这有两个含义:

  • 因为不能从另一个bean调用私有方法(exception是reflection),所以他们的@Transactional注解不被考虑。
  • 如果该方法是公共的,但是它是从同一个bean调用的,那么它也不会被考虑(这个语句只有在使用(默认)Spring Proxy AOP的时候才是正确的)。

@见Spring参考:第9.6章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

注意:在代理模式下(这是默认模式),只有通过代理进入的“外部”方法调用才会被拦截。 这意味着即使被调用的方法标记为@Transactional ,“自调用”(即调用目标对象的其他方法的目标对象内的方法)也不会导致实际的事务处理。

如果您希望自我调用也包含在事务中,请考虑使用AspectJ模式(请参见下文)。 在这种情况下,首先不会有代理; 相反,目标类将被编织(即其字节码将被修改),以便将@Transactional转换为任何types的方法的运行时行为。

是的,可以在私有方法上使用@Transactional,但正如其他人所说的那样,这将不能用于开箱即用。 你需要使用AspectJ。 我花了一些时间来弄清楚如何使它工作。 我会分享我的结果。

我select使用编译时编织,而不是加载时编织,因为我认为这是一个更好的select。 另外,我正在使用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> 

最后添加到你的configuration类

 @EnableTransactionManagement(mode = AdviceMode.ASPECTJ) 

现在你应该可以在私有方法上使用@Transactional了。

对此方法的一个警告:您将需要configuration您的IDE,以了解AspectJ,否则,如果您通过Eclipse运行应用程序,例如它可能无法正常工作。 确保你testing一个直接的Maven构build作为一个完整性检查。

请参阅此文档:

在代理模式下(这是默认模式),只拦截通过代理进入的外部方法调用。 这意味着即使被调用的方法被标记为@Transactional,自调用实际上是调用目标对象的另一个方法的目标对象内的方法也不会导致实际的事务。

如果您期望自我调用也被事务打包,请考虑使用AspectJ模式(请参阅下表中的模式属性)。 在这种情况下,首先不会有代理; 相反,目标类将被编织(即,其字节代码将被修改),以便将@Transactional转换为任何types的方法的运行时行为。

– – – – – – – – – – – – – – 过度

所以, 一种方式是用户BeanSelfAware

答案是不。 请参阅Spring Reference:使用@Transactional :

@Transactional注解可以放在接口定义,接口上的方法,类定义或者类的公共方法之前