1. 程式人生 > >Spring代理同一個方法呼叫同一個物件中的兄弟方法的問題

Spring代理同一個方法呼叫同一個物件中的兄弟方法的問題

service有A、B兩個方法,A方法無AOP,B方法有AOP。在A方法中呼叫了B方法。

  • 問題1:B方法AOP會不會生效?為什麼?
  • 問題2:如果不會,怎麼解決?

涉及的知識點:事務傳播特性,巢狀事務

同一個方法呼叫同一個物件中的兄弟方法,基於JDK介面形式的動態代理,是不會生效的。
但是,基於cglib形式的動態代理,那就不一定了。

Spring的配置可以讓你去選擇使用哪種動態代理。

JDK動態代理基於介面,要生成代理類,是通過實現了一個介面生成的,但是代理類實現了相通的介面,他的實現從何而來,必須從一個目標物件target身上來,target作為方法呼叫的基礎,然後在target前後左右做攔截。對於spring aop來說,我們平時加上@transactional註解的service類,實際上,就是target的類,我們在service方法內部呼叫另外一個兄弟service方法,實際上,就是target內部的呼叫,並沒有走外層的攔截。

但是,cglib形式的動態代理,是基於繼承的,代理類是繼承一個目標類而來,這樣,代理類還需要一個target嗎?其實是不需要的, 因為在代理類的例項中,已經包含了一個父類物件super,我們只需要覆蓋父類的方法,在super的前後左右做攔截,就可以完成動態代理。

但是,cglib繼承模式中,也可以持有一個target屬性,而忽略super,只是說,我們多了一個選擇,可以不選擇target,使用super,也可以使用一個target,而不使用super。

對於spring aop中的cglib形式動態代理來說,我們只是在service類上加了transactional註解,根本就不存在任何的target,因此,在spring aop中,cglib形式下的動態代理就是基於super的攔截。

好了,既然是基於super的攔截,cglib也是採用繼承覆蓋父類方法實現的,那麼,在service類中,呼叫一個兄弟方法的時候,出現了一個很奇特的現象:那就是,你在service類中呼叫一個兄弟方法,就一定是呼叫super物件中的另一個兄弟方法嗎?

因為子類已經覆蓋了父類的方法,你在呼叫一個兄弟方法的時候,實際上,因為多型的存在,呼叫的是你的子類(代理類)的覆蓋後的方法,而子類(代理類)的方法,是已經實現了攔截的方法。所以,這個時候,@transactional註解,是會生效的。