简体   繁体   English

为什么Spring的@Transactional无法在受保护的方法上工作?

[英]Why doesn't Spring's @Transactional work on protected methods?

From Does Spring @Transactional attribute work on a private method? Spring @Transactional属性是否适用于私有方法?

When using proxies, you should apply the @Transactional annotation only to methods with public visibility. 使用代理时,应仅将@Transactional注释应用于具有公共可见性的方法。 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. 如果使用@Transactional注释对受保护的,私有的或程序包可见的方法进行注释,则不会引发任何错误,但是带注释的方法不会显示已配置的事务设置。

I can think of good reasons to exclude private and package-private methods, but why won't protected methods behave transactionally? 我可以想到排除privatepackage-private方法的充分理由,但是为什么protected方法不具有事务性呢? The following stacktrace displays the correct behaviour for a public method (called through an interface proxy): 以下堆栈跟踪显示了公共方法(通过接口代理调用)的正确行为:

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]

When calling an "identical" protected method (through a non-interface CGLIB proxy), we get the following: 当调用“相同的”受保护方法(通过非接口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]

This is apparently a design decision (why?), but I consider it rather questionable that it fails silently, when it's clearly a developer error. 这显然是一个设计决定(为什么?),但我认为这很可疑,因为它显然是开发人员错误,但它会在无提示的情况下失败。

Edit This is obviously not an issue when using interfaces (only public methods in interfaces), but as Spring doesn't necessarily need an interface to proxy an object through CGLIB, calling a protected @Transactional method will behave just like a public method (ie called through a proxy), except that by design it ignores the transactionality. 编辑使用接口(仅是接口中的公共方法)时,这显然不是问题,但是由于Spring不一定需要接口来通过CGLIB代理对象,因此调用受保护的@Transactional方法的行为将类似于公共方法(即通过代理调用),但根据设计它会忽略事务性。

Because of this: 因为这:

In proxy mode (which is the default), only 'external' method calls coming in through the proxy will be intercepted. 在代理模式(默认)下,将仅拦截通过代理传入的“外部”方法调用。 This means that 'self-invocation', ie 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! 这意味着即使调用的方法标记有@Transactional,“自调用”(即目标对象内的方法调用目标对象的其他方法)也不会在运行时导致实际事务!

And this: 和这个:

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). 由于Spring的AOP框架基于代理的性质,根据定义,无论是JDK代理(不适用)还是CGLIB代理(在技术上可行但不建议用于AOP的目的),都不会拦截受保护的方法。 As a consequence, any given pointcut will be matched against public methods only! 结果,任何给定的切入点将仅与公共方法匹配!

The Spring guys would probably want to keep consistency with JDK proxies. Spring的家伙可能希望与JDK代理保持一致。 You wouldn't want to have different proxy configuration and different results based on JDK versus CGLIB. 您不希望基于JDK和CGLIB具有不同的代理配置和不同的结果。

Additional info to the other answers. 其他答案的其他信息。

Here is an example picture from the Spring blog : 这是Spring博客的示例图片: 在此处输入图片说明

As you can see the proxy is wrapped around the implementing class (here the AccountServiceImpl ) and the proxy itself implements only methods from the AccountService interface. 如您所见,代理服务器包裹在实现类(此处为AccountServiceImpl )周围,代理服务器本身仅实现AccountService接口中的方法。 An Interface provides only public methods (public contract), so a proxy can not be wrapped around the protected method and provide the transactional behavior, which it provides on public methods. Interface仅提供公共方法(公共合同),因此代理不能围绕受保护的方法包装并提供其在公共方法上提供的事务行为。

Calling methods in the same service is possible, if you would use AspectJ , but I'm not sure if this also counts for protected methods, because I haven`t used it until now. 如果您要使用AspectJ ,则可以在同一服务中调用方法,但是我不确定这是否也适用于protected方法,因为到目前为止我还没有使用过它。

Protected methods are not part of the public contract. 受保护的方法不属于公共合同的一部分。 That's why they are not proxied. 这就是为什么它们没有被代理。 They are not visible to the consumer in most cases. 在大多数情况下,它们对消费者是不可见的。

You could call protected methods if you a) wire by IF and cast down to the concrete implementation (bad) or b) wire the concrete implementation and the consumer resides in the same package. 如果您a)通过IF连线并转换为具体实现(错误),或者b)连线具体实现且使用者位于同一包中,则可以调用受保护的方法。 As only b) makes sense, I can see why Spring does not proxy protected methods. 正如只有b)有意义,我可以理解为什么Spring不代理受保护的方法。 It's a rare corner case and would only work with CGLIB, not JDK proxies. 这是一个罕见的极端情况,仅适用于CGLIB,不适用于JDK代理。

Maybe you wonder about the use case of extending a bean and calling super.myProtectedMethod() : those calls are not proxied at all, independent of access level. 也许您想知道扩展bean并调用super.myProtectedMethod()的用例:完全不代理那些调用,而与访问级别无关。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM