Java Spring@Transactional属性在私有方法上工作吗?
如果我在Springbean中的私有方法上有一个-annotation,那么这个annotation会有任何效果吗 如果Java Spring@Transactional属性在私有方法上工作吗?,java,spring,transactions,annotations,Java,Spring,Transactions,Annotations,如果我在Springbean中的私有方法上有一个-annotation,那么这个annotation会有任何效果吗 如果@Transactional注释位于公共方法上,它将工作并打开一个事务 public class Bean { public void doStuff() { doPrivateStuff(); } @Transactional private void doPrivateStuff() { } } ... Bean bean = (Bean
@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
注释可以放在接口定义、接口上的方法、类定义或类上的公共方法之前
默认情况下,
@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
转换为任何类型方法的运行时行为
春季医生解释说 在代理模式(默认)下,只有外部方法调用 通过代理进入被拦截。这意味着 自调用,实际上是目标对象中调用 目标对象的另一种方法,不会导致实际的 即使调用的方法标记为 @事务性的 考虑使用AspectJ模式(参见下表中的模式属性) 如果您希望将自调用与事务打包为 好。在这种情况下,首先不会有代理人; 相反,将编织目标类(即,其字节码将 要将@Transactional转换为上的运行时行为 任何一种方法
另一种方法是用户BeanSelfAware问题不是私有的或公共的,问题是:如何调用它以及使用哪种AOP实现 如果使用(默认)Spring代理AOP,则只有在调用通过代理时才会考虑Spring提供的所有AOP功能(如
@Transactional
)如果从另一个bean调用带注释的方法,则通常会出现这种情况
这有两个含义:
- 因为私有方法不能从另一个bean调用(反射是例外),所以不考虑它们的
注释@Transactional
- 如果该方法是公共的,但它是从同一个bean调用的,那么也不会考虑它(只有在使用(默认)Spring代理AOP时,该语句才是正确的)
我想您应该使用aspectJ模式,而不是Spring代理,这将克服这个问题。AspectJ事务方面甚至被编织到私有方法中(选中Spring 3.0)。是的,可以在私有方法上使用@Transactional,但正如其他人所提到的,这并不是现成的。您需要使用AspectJ。我花了一些时间想办法让它工作。我将分享我的结果 我选择使用编译时编织而不是加载时编织,因为我认为这是一个更好的选择。另外,我正在使用Java8,因此您可能需要调整一些参数 首先,添加aspectjrt的依赖项
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.8</version>
</dependency>
现在,您应该能够在私有方法上使用@Transactional
这种方法有一个警告:您需要将IDE配置为了解AspectJ,否则,如果您通过Eclipse运行应用程序,例如,它可能无法工作。确保对直接Maven构建进行测试,作为一种健全性检查。如果需要在事务中封装私有方法,并且不想使用aspectj,可以使用 与使用相同的方法可以使用此帮助器组件(Kotlin):
不知道
TransactionTemplate
是否重用现有事务,但此代码肯定会这样做。您确定吗?如果代理样式是Cglib呢?你是说bean=newbean();?不。如果我使用new Bean()创建Bean,那么至少在不使用Aspect-J的情况下,注释将无法工作。谢谢!这解释了我观察到的奇怪行为。这种内部方法调用限制非常违反直觉……这两点不一定都是正确的。第一个是不正确的-可以反射地调用私有方法,但代理发现逻辑选择不这样做。第二点仅适用于基于接口的JDK代理,但不适用于基于CGLIB子类的代理。@skaffman:1-我的陈述更精确,2。但是默认代理是基于接口的,不是吗?这取决于目标是否使用接口。如果没有,就使用CGLIB。你能告诉我CGLIB不能不使用方面的原因或参考资料吗
<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)
@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(){
//...
}
}
@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")
}
}