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

2022-09-04 06:39:46

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

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

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

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]

当调用“相同”的受保护方法(通过非接口 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


答案 1

因此:

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

还有这个:

由于Spring的AOP框架基于代理的性质,根据定义,受保护的方法不会被拦截,无论是JDK代理(这不适用)还是CGLIB代理(这在技术上是可行的,但不建议用于AOP目的)。因此,任何给定的切入点将仅与公共方法匹配!

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


答案 2

其他答案的其他信息。

这是来自Spring博客的示例图片:enter image description here

如您所见,代理被包装在实现类(此处为 )周围,并且代理本身仅实现接口中的方法。An 仅提供公共方法(公共协定),因此代理不能包装在受保护的方法周围并提供它在公共方法上提供的事务行为。AccountServiceImplAccountServiceInterface

如果您愿意使用,可以在同一服务中调用方法,但我不确定这是否也适用于方法,因为我直到现在才使用它。AspectJprotected


推荐