为什么Spring的@Transactional不能用于受保护的方法?

Kay*_*man 4 java spring spring-transactions

来自Spring @Transactional属性是否适用于私有方法?

使用代理时,应仅将@Transactional注释应用于具有公共可见性的方法.如果使用@Transactional注释对带保护的,私有的或包可见的方法进行注释,则不会引发错误,但带注释的方法不会显示已配置的事务设置.

我可以想出排除privatepackage-private方法的充分理由,但为什么protected方法不会在事务上表现?以下stacktrace显示公共方法的正确行为(通过接口代理调用):

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]
Run Code Online (Sandbox Code Playgroud)

当调用"相同"的受保护方法(通过非接口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]
Run Code Online (Sandbox Code Playgroud)

这显然是一个设计决定(为什么?),但我认为它显然是一个开发人员错误,它无声地失败.

编辑 当使用接口(接口中只有公共方法)时,这显然不是问题,但由于Spring不一定需要通过CGLIB代理对象的接口,因此调用受保护的@Transactional方法将表现得像公共方法(即通过代理),除了按设计它忽略了事务性.

Lak*_*ula 14

因为这:

在代理模式(默认设置)下,只会拦截通过代理进入的"外部"方法调用.这意味着'自调用',即目标对象中调用目标对象的其他方法的方法,即使被调用的方法用@Transactional标记,也不会在运行时导致实际的事务!

还有这个:

由于Spring的AOP框架基于代理的特性,受保护的方法根据定义不会被拦截,既不用于JDK代理(这不适用),也不用于CGLIB代理(这在技术上可行,但不建议用于AOP).因此,任何给定的切入点都只能与公共方法匹配!

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

  • 自我调用的情况很清楚,但第二个引用可能是根本问题。然而,如果它“非静默地”失败会很好,因为 `protected` 方法上的 `@Transactional` 始终是一个错误。 (2认同)
  • _Why_很清楚:与JDK代理的一致性.如果_failing silently_是一个好的决定是值得商榷的. (2认同)

kam*_*mwo 5

其他答案的其他信息。

这是Spring博客的示例图片: 在此处输入图片说明

如您所见,代理服务器包裹在实现类(这里是AccountServiceImpl)周围,代理服务器本身仅实现AccountService接口中的方法。An Interface仅提供公共方法(公共合同),因此代理不能围绕受保护的方法包装并提供其在公共方法上提供的交易行为。

如果要使用AspectJ,可以在同一服务中调用方法,但是我不确定这是否也适用于protected方法,因为到目前为止我还没有使用过它。