为什么Spring的@Transactional不受保护方法的影响?

时间:2015-12-10 09:24:22

标签: java spring spring-transactions

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

  

使用代理时,应该应用@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]

拨打"相同的" protected方法(通过非接口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]

这显然是一个设计决定(为什么?),但我认为它显然是有问题的,当它明显是开发人员错误时,它会无声地失败。

修改 当使用接口(仅接口中的公共方法)时,这显然不是问题,但是由于Spring不一定需要通过CGLIB代理对象的接口,因此调用受保护的@Transactional方法将表现得像公共方法(即通过代理调用),除了 by design 之外,它忽略了事务性。

3 个答案:

答案 0 :(得分:12)

因此:

  

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

而且:

  

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

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

答案 1 :(得分:4)

其他答案的其他信息。

以下是来自the Spring blog的示例图片: enter image description here

正如您所看到的,代理程序包含在实现类(此处为AccountServiceImpl)中,而代理本身仅实现AccountService接口中的方法。 Interface仅提供公共方法(公共合同),因此代理不能包含在受保护的方法中,并提供它在公共方法上提供的事务行为。

如果您使用AspectJ,则可以在同一服务中调用方法,但我不确定这是否也适用于protected方法,因为我还没有使用它现在

答案 2 :(得分:1)

受保护的方法不属于公共合同。这就是他们没有被代理的原因。在大多数情况下,消费者看不到它们。

如果你a)通过IF连接并转发到具体实现(坏)或b)连接具体实现并且消费者驻留在同一个包中,你可以调用受保护的方法。由于只有b)有意义,我可以看出为什么Spring不代理受保护的方法。这是一个罕见的极端情况,只适用于CGLIB,而不适用于JDK代理。

也许您想知道扩展bean并调用super.myProtectedMethod()的用例:这些调用根本没有代理,与访问级别无关。