java - 为什么 Spring 的 @Transactional 不适用于 protected 方法?

标签 java spring spring-transactions

来自Does Spring @Transactional attribute work on a private method?

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.

我可以想到排除 privatepackage-private 方法的充分理由,但为什么 protected 方法不能以事务方式运行?以下堆栈跟踪显示公共(public)方法(通过接口(interface)代理调用)的正确行为:

at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_51]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:302) ~[spring-aop-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) ~[spring-aop-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) ~[spring-tx-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281) ~[spring-tx-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) ~[spring-tx-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207) ~[spring-aop-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at com.sun.proxy.$Proxy145.improveType(Unknown Source) ~[na:na]

当调用“相同”的 protected 方法(通过非接口(interface) CGLIB 代理)时,我们得到以下结果:

at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_51]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:302) ~[spring-aop-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) ~[spring-aop-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:720) ~[spring-aop-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:653) ~[spring-aop-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at my.company.webservices.facade.EntityFacade$$EnhancerBySpringCGLIB$$fd77735b.findEntity(<generated>) ~[spring-core-4.2.1.RELEASE.jar:na]

这显然是一个设计决策(为什么?),但我认为当它显然是开发人员错误时,它会默默地失败,这是相当值得怀疑的。

编辑 当使用接口(interface)(仅接口(interface)中的公共(public)方法)时,这显然不是问题,但由于 Spring 不一定需要接口(interface)来通过 CGLIB 代理对象,因此调用 protected @Transactional 方法的行为只是就像公共(public)方法(即通过代理调用)一样,除了根据设计它忽略了事务性。

最佳答案

因为这个:

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!

还有这个:

Due to the proxy-based nature of Spring's AOP framework, protected methods are by definition not intercepted, neither for JDK proxies (where this isn't applicable) nor for CGLIB proxies (where this is technically possible but not recommendable for AOP purposes). As a consequence, any given pointcut will be matched against public methods only!

Spring 人员可能希望与 JDK 代理保持一致。您不希望基于 JDK 与 CGLIB 拥有不同的代理配置和不同的结果。

关于java - 为什么 Spring 的 @Transactional 不适用于 protected 方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57723100/

相关文章:

java - 具有多个数据源的 Spring Boot @Transactional 属性

java - Spring Data JPA 中的更新和事务

spring - 如何使用 Spring + DBUnit + JUnit 配置多个事务管理器

java - 如何在 Java 中实现没有资源泄漏警告的 Stream<E>

java - Java 中 TreeSet 部分 View 的 size() 的复杂性是多少

java - 通过 syslog 发送 log4j2 堆栈跟踪

java - 使用 PageFactory 时处理 StaleElementReferenceException

java - Spring Batch(Spring Boot - Java配置)多线程jdbc编写器

java - 如何摆脱 Spring Boot 中的 JpaRepository 接口(interface)

java - 如果事件是从另一个监听器发布的,则忽略 TransactionalEventListener