2015-12-10 67 views
2

Does Spring @Transactional attribute work on a private method?爲什麼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] 

這顯然是一個設計決策(爲什麼?),但我認爲這是相當可疑的,它靜靜地失敗,當它顯然是一個開發者的錯誤。

編輯 這顯然不是一個問題,使用接口(接口中唯一的公共方法)的時候,但作爲春季不一定需要一個接口來代理通過CGLIB對象,調用保護@Transactional方法將表現得像公共方法(即通過代理調用),除了設計它忽略了事務性。

+0

從理論上講,如果你在包內的類之間進行調用,我想你仍然可以使用包方法的代理AOP。主要問題是自我呼叫。你可能會考慮在Spring JIRA上提出這個問題。 – chrylis

回答

9

正因爲如此:

在代理模式(這是默認值),僅「外部」方法調用通過代理來將被截取。這意味着即使被調用的方法標記爲@Transactional,「自調用」(即目標對象內調用目標對象的某個其他方法的方法)也不會導致實際的事務處理。

而且這樣的:

由於Spring的AOP框架的基於代理的性質,保護的方法是通過定義不攔截,既不是JDK代理(其中,這是不適用),也不是CGLIB代理(這在技術上是可行的,但不適用於AOP目的)。因此,任何給定的切入點將僅與公共方法匹配!

春天的人可能想要保持與JDK代理的一致性。您不希望有不同的代理配置以及基於JDK和CGLIB的不同結果。

+1

自調用案例很明顯,但第二個引用可能是根本問題。但是,如果它「非默默地」失敗,那將是很好的,因爲'protected'方法中的@ Transactional將永遠是一個錯誤。 – Kayaman

+2

_Why_明確:與JDK代理一致。如果_無聲地_failing是一個好決定是有爭議的。 – atamanroman

1

受保護的方法不是公共合同的一部分。這就是爲什麼他們沒有代理。在大多數情況下,消費者看不到它們。

如果您a)通過IF連線並將其轉換爲具體實現(bad)或b)連線具體實現,並且使用者駐留在同一個包中,則可以調用受保護的方法。因爲只有b)有意義,所以我明白了Spring爲什麼不代理受保護的方法。這是一種罕見的情況,只能用於CGLIB,而不能用於JDK代理。

也許你想知道擴展bean的用例並調用super.myProtectedMethod():這些調用根本不代理,與訪問級別無關。

4

其他信息到其他答案。

這裏是the Spring blog一個例子圖片: enter image description here

正如你可以看到代理圍繞實現類(這裏的AccountServiceImpl)包裹和代理本身實現從AccountService接口唯一方法。 Interface只提供了公共方法(公共契約),所以代理不能被包裝在受保護的方法中,並提供它在公共方法中提供的事務行爲。

如果您使用AspectJ,則可以在同一服務中調用方法,但我不確定這是否也適用於protected方法,因爲我至今還沒有使用它。

+0

對不起,我沒有提到在這種情況下沒有界面。 CGLIB代理將被創建爲一個子類,所以'protected'方法可以正常訪問。 – Kayaman

相關問題