springaop实现原理_spring AOP的实现原理
基于代理(Proxy)的AOP實現
首先,這是一種基于代理(Proxy)的實現方式。下面這張圖很好地表達了這層關系:
這張圖反映了參與到AOP過程中的幾個關鍵組件(以@Before Advice為例):
它們之間的調用先后次序反映在上圖的序號中:
為了理解清楚這張圖的意思和代理在中間扮演的角色,不妨看看下面的代碼:
@Componentpublic class SampleBean { public void advicedMethod() { } public void invokeAdvicedMethod() { advicedMethod(); }}@Aspect@Componentpublic class SampleAspect { @Before("execution(void advicedMethod())") public void logException() { System.out.println("Aspect被調用了"); }}sampleBean.invokeAdvicedMethod(); // 會打印出 "Aspect被調用了" 嗎?SampleBean扮演的就是目標方法所在Bean的角色,而SampleAspect扮演的則是Advice的角色。很顯然,被AOP修飾過的方法是advicedMethod(),而非invokeAdvicedMethod()。然而,invokeAdvicedMethod()方法在內部調用了advicedMethod()。那么會打印出來Advice中的輸出嗎?
答案是不會。
如果想不通為什么會這樣,不妨再去仔細看看上面的示意圖。
這是在使用Spring AOP的時候可能會遇到的一個問題。類似這種間接調用不會觸發Advice的原因在于調用發生在目標方法所在Bean的內部,和外面的代理對象可是沒有半毛錢的關系哦。我們可以把這個代理想象成一個中介,只有它知道Advice的存在,調用者Bean和目標方法所在Bean知道彼此的存在,但是對于代理或者是Advice卻是一無所知的。因此,沒有通過代理的調用是絕無可能觸發Advice的邏輯的。如下圖所示:
Spring AOP的兩種實現方式
Spring AOP有兩種實現方式:
- 基于接口的動態代理(Dynamic Proxy)
- 基于子類化的CGLIB代理
我們在使用Spring AOP的時候,一般是不需要選擇具體的實現方式的。Spring AOP能根據上下文環境幫助我們選擇一種合適的。那么是不是每次都能夠這么”智能”地選擇出來呢?也不盡然,下面的例子就反映了這個問題:
@Componentpublic class SampleBean implements SampleInterface { public void advicedMethod() { } public void invokeAdvicedMethod() { advicedMethod(); }}public interface SampleInterface {}在上述代碼中,我們為原來的Bean實現了一個新的接口SampleInterface,這個接口中并沒有定義任何方法。這個時候,再次運行相關測試代碼的時候就會出現異常(摘錄了部分異常信息):
org.springframework.beans.factory.BeanCreationException: Error ceating bean with name 'com.destiny1020.SampleBeanTest': Injection of autowired dependencies failedCaused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.destiny1020.SampleBean] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency.也就是說在Test類中對于Bean的Autowiring失敗了,原因是創建SampleBeanTest Bean的時候發生了異常。那么為什么會出現創建Bean的異常呢?從異常信息來看并不明顯,實際上這個問題的根源在于Spring AOP在創建代理的時候出現了問題。
這個問題的根源可以在這里得到一些線索:
Spring AOP Reference - AOP Proxies
文檔中是這樣描述的(每段后加上了翻譯):
Spring AOP defaults to using standard JDK dynamic proxies for AOP proxies. This enables any interface (or set of interfaces) to be proxied.Spring AOP默認使用標準的JDK動態代理來實現AOP代理。這能使任何借口(或者一組接口)被代理。Spring AOP can also use CGLIB proxies. This is necessary to proxy classes rather than interfaces. CGLIB is used by default if a business object does not implement an interface. As it is good practice to program to interfaces rather than classes; business classes normally will implement one or more business interfaces. It is possible to force the use of CGLIB, in those (hopefully rare) cases where you need to advise a method that is not declared on an interface, or where you need to pass a proxied object to a method as a concrete type.Spring AOP也使用CGLIB代理。對于代理classes而非接口這是必要的。如果一個業務對象沒有實現任何接口,那么默認會使用CGLIB。由于面向接口而非面向classes編程是一個良好的實踐;業務對象通常都會實現一個或者多個業務接口。強制使用CGLIB也是可能的(希望這種情況很少),此時你需要advise的方法沒有被定義在接口中,或者你需要向方法中傳入一個具體的對象作為代理對象。因此,上面異常的原因在于:
強制使用CGLIB也是可能的(希望這種情況很少),此時你需要advise的方法沒有被定義在接口中。我們需要advise的方法是SampleBean中的advicedMethod方法。而在添加接口后,這個方法并沒有被定義在該接口中。所以正如文檔所言,我們需要強制使用CGLIB來避免這個問題。
強制使用CGLIB很簡單:
@Configuration@EnableAspectJAutoProxy(proxyTargetClass = true)@ComponentScan(basePackages = "com.destiny1020")public class CommonConfiguration {}向@EnableAspectJAutoProxy注解中添加屬性proxyTargetClass = true即可。
CGLIB實現AOP代理的原理是通過動態地創建一個目標Bean的子類來實現的,該子類的實例就是AOP代理,它建立起了目標Bean到Advice的聯系。
當然還有另外一種解決方案,那就是將方法定義聲明在新創建的接口中并且去掉之前添加的proxyTargetClass = true:
@Componentpublic class SampleBean implements SampleInterface { @Override public void advicedMethod() { } @Override public void invokeAdvicedMethod() { advicedMethod(); }}public interface SampleInterface { void invokeAdvicedMethod(); void advicedMethod();}@Configuration@EnableAspectJAutoProxy@ComponentScan(basePackages = "com.destiny1020")public class CommonConfiguration {}- 從Debug Stacktrace的角度也可以看出這兩種AOP實現方式上的區別:
- JDK動態代理
- CGLIB
- 關于動態代理和CGLIB這兩種方式的簡要總結如下:
- JDK動態代理(Dynamic Proxy)
- 基于標準JDK的動態代理功能
- 只針對實現了接口的業務對象
- CGLIB
- 通過動態地對目標對象進行子類化來實現AOP代理,上面截圖中的SampleBean$$EnhancerByCGLIB$$1767dd4b即為動態創建的一個子類
- 需要指定@EnableAspectJAutoProxy(proxyTargetClass = true)來強制使用
- 當業務對象沒有實現任何接口的時候默認會選擇CGLIB
總結
以上是生活随笔為你收集整理的springaop实现原理_spring AOP的实现原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 归属地的判断规则有吗_IPO|创业板注册
- 下一篇: bool类型头文件_[C++基础入门]