簡體   English   中英

為什么Spring的@Transactional無法在受保護的方法上工作?

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

Spring @Transactional屬性是否適用於私有方法?

使用代理時,應僅將@Transactional注釋應用於具有公共可見性的方法。 如果使用@Transactional注釋對受保護的,私有的或程序包可見的方法進行注釋,則不會引發任何錯誤,但是帶注釋的方法不會顯示已配置的事務設置。

我可以想到排除privatepackage-private方法的充分理由,但是為什么protected方法不具有事務性呢? 以下堆棧跟蹤顯示了公共方法(通過接口代理調用)的正確行為:

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方法的行為將類似於公共方法(即通過代理調用),但根據設計它會忽略事務性。

因為這:

在代理模式(默認)下,將僅攔截通過代理傳入的“外部”方法調用。 這意味着即使調用的方法標記有@Transactional,“自調用”(即目標對象內的方法調用目標對象的其他方法)也不會在運行時導致實際事務!

和這個:

由於Spring的AOP框架基於代理的性質,根據定義,無論是JDK代理(不適用)還是CGLIB代理(在技術上可行但不建議用於AOP的目的),都不會攔截受保護的方法。 結果,任何給定的切入點將僅與公共方法匹配!

Spring的家伙可能希望與JDK代理保持一致。 您不希望基於JDK和CGLIB具有不同的代理配置和不同的結果。

其他答案的其他信息。

這是Spring博客的示例圖片: 在此處輸入圖片說明

如您所見,代理服務器包裹在實現類(此處為AccountServiceImpl )周圍,代理服務器本身僅實現AccountService接口中的方法。 Interface僅提供公共方法(公共合同),因此代理不能圍繞受保護的方法包裝並提供其在公共方法上提供的事務行為。

如果您要使用AspectJ ,則可以在同一服務中調用方法,但是我不確定這是否也適用於protected方法,因為到目前為止我還沒有使用過它。

受保護的方法不屬於公共合同的一部分。 這就是為什么它們沒有被代理。 在大多數情況下,它們對消費者是不可見的。

如果您a)通過IF連線並轉換為具體實現(錯誤),或者b)連線具體實現且使用者位於同一包中,則可以調用受保護的方法。 正如只有b)有意義,我可以理解為什么Spring不代理受保護的方法。 這是一個罕見的極端情況,僅適用於CGLIB,不適用於JDK代理。

也許您想知道擴展bean並調用super.myProtectedMethod()的用例:完全不代理那些調用,而與訪問級別無關。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM