深入解析Spring架構與設計原理4

關於AOP的個人理解 

AOP聯盟定義的AOP體系結構把與AOP相關的概念大致分爲了由高到低、從使用到實現的三個層次。關於這個體系結構,個人的理解是這樣的,從上往下,最高層是語言和開發環境,在這個環境中可以看到幾個重要的概念:base可以視爲待增強對象,或者說目標對象;aspect指切面,通常包含對於base的增強應用;configuration可以看成是一種編織或者說配置,通過在AOP體系中提供這個configuration配置環境,可以把base和aspect結合起來,從而完成切面對目標對象的編織實現。 

對Spring平臺或者說生態系統來說,AOP是Spring框架的核心功能模塊之一。AOP與IOC容器的結合使用, 爲應用開發或者Spring自身功能的擴展都提供了許多便利。Spring AOP的實現和其他特性的實現一樣,非常豐富,除了可以使用Spring本身提供的AOP實現之外,還封裝了業界優秀的AOP解決方案AspectJ來讓應用使用。在這裏,主要對Spring自身的AOP實現原理做一些解析;在這個AOP實現中,Spring充分利用了IOC容器Proxy代理對象以及AOP攔截器的功能特性,通過這些對AOP基本功能的封裝機制,爲用戶提供了AOP的實現框架。所以,要了解這些AOP的基本實現,需要我們對Java 的Proxy機制有一些基本瞭解。 

AOP實現的基本線索 

AOP實現中,可以看到三個主要的步驟,一個是代理對象的生成,然後是攔截器的作用,然後是Aspect編織的實現。AOP框架的豐富,很大程度體現在這三個具體實現中,所具有的豐富的技術選擇,以及如何實現與IOC容器的無縫結合。畢竟這也是一個非常核心的模塊,需要滿足不同的應用需求帶來的解決方案需求。 
在Spring AOP的實現原理中,我們主要舉ProxyFactoryBean的實現作爲例子和實現的基本線索進行分析;很大一個原因,是因爲ProxyFactoryBean是在Spring IoC環境中,創建AOP應用的最底層方法,從中,可以看到一條實現AOP的基本線索。在ProxyFactoryBean中,它的AOP實現需要依賴JDK或者CGLIB提供的Proxy特性。從FactoryBean中獲取對象,是從getObject()方法作爲入口完成的。然後爲proxy代理對象配置advisor鏈,這個配置是在initializeAdvisorChain方法中完成的;然後就爲生成AOP代理對象做好了準備,生成代理對象如下所示:

 

  1. private synchronized Object getSingletonInstance() {   
  2.     if (this.singletonInstance == null) {   
  3.         this.targetSource = freshTargetSource();   
  4.         if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {   
  5.             // Rely on AOP infrastructure to tell us what interfaces to proxy.   
  6.              Class targetClass = getTargetClass();   
  7.             if (targetClass == null) {   
  8.                 throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");   
  9.              }   
  10. // 這裏設置代理對象的接口      setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));   
  11.          }   
  12.         // Initialize the shared singleton instance.   
  13.         super.setFrozen(this.freezeProxy);   
  14.         // 注意這裏的方法會使用ProxyFactory來生成我們需要的Proxy   
  15.         this.singletonInstance = getProxy(createAopProxy());   
  16.      }   
  17.     return this.singletonInstance;   
  18. }   
  19. //使用createAopProxy返回的AopProxy來得到代理對象   
  20. protected Object getProxy(AopProxy aopProxy) {   
  21.     return aopProxy.getProxy(this.proxyClassLoader);   
  22. }  



上面我們看到了在Spring中通過ProxyFactoryBean實現AOP功能的第一步,得到AopProxy代理對象的基本過程,下面我們看看AopProxy代理對象的攔截機制是怎樣發揮作用,是怎樣實現AOP功能的。我們知道,對代理對象的生成,有CGLIB和JDK兩種生成方式,在CGLIB中,對攔截器設計是通過在Cglib2AopProxy的AopProxy代理對象生成的時候,在回調DynamicAdvisedInterceptor對象中實現的,這個回調的實現在intercept方法中完成。對於AOP是怎樣完成對目標對象的增強的,這些實現是封裝在AOP攔截器鏈中,由一個個具體的攔截器來完成的。具體攔截器的運行是在以下的代碼實現中完成的,這些調用在ReflectiveMethodInvocation中。

  1. public Object proceed() throws Throwable {   
  2.     //   We start with an index of -1 and increment early.   
  3.     //如果攔截器鏈中的攔截器迭代調用完畢,這裏開始調用target的函數,這個函數是通過反射機制完成的,具體實現在:AopUtils.invokeJoinpointUsingReflection方法裏面。   
  4.     if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {   
  5.         return invokeJoinpoint();   
  6.      }   
  7.     //這裏沿着定義好的 interceptorOrInterceptionAdvice鏈進行處理。   
  8.      Object interceptorOrInterceptionAdvice =   
  9.         this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);   
  10.     if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {   
  11.         // Evaluate dynamic method matcher here: static part will already have   
  12.         // been evaluated and found to match.   
  13.         //這裏對攔截器進行動態匹配的的判斷,還記得我們前面分析的pointcut嗎?這裏是觸發進行匹配的地方,如果和定義的pointcut匹配,那麼這個advice將會得到執行。   
  14.          InterceptorAndDynamicMethodMatcher dm =   
  15.              (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;   
  16.         if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {   
  17.             return dm.interceptor.invoke(this);   
  18.          }   
  19.         else {   
  20.             // Dynamic matching failed.   
  21.             // Skip this interceptor and invoke the next in the chain.   
  22.             // //如果不匹配,那麼這個proceed會被遞歸調用,直到所有的攔截器都被運行過爲止。   
  23.             return proceed();   
  24.          }   
  25.      }   
  26.     else {   
  27.         // It's an interceptor, so we just invoke it: The pointcut will have   
  28.         // been evaluated statically before this object was constructed.   
  29.         //如果是一個interceptor,直接調用這個interceptor對應的方法   
  30.         return((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);   
  31.      }   
  32. }  


在調用攔截器的時候,我們接下去就可以看到對advice的通知的調用。而經過一系列的註冊,適配的過程以後,攔截器在攔截的時候,會調用到預置好的一個通知適配器,設置通知攔截器,這是一系列Spring設計好爲通知服務的類的一個,是最終完成通知攔截和實現的地方,非常的關鍵。比如,對MethodBeforeAdviceInterceptor的實現是這樣的:

  1. public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {   
  2.   
  3.     private MethodBeforeAdvice advice;   
  4.   
  5.   
  6.     /**
  7.       * Create a new MethodBeforeAdviceInterceptor for the given advice.
  8.       * @param advice the MethodBeforeAdvice to wrap
  9.       */  
  10.     public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {   
  11.          Assert.notNull(advice, "Advice must not be null");   
  12.         this.advice = advice;   
  13.      }   
  14.     //這個invoke方法是攔截器的回調方法,會在代理對象的方法被調用的時候觸發回調。   
  15.     public Object invoke(MethodInvocation mi) throws Throwable {   
  16.         this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );   
  17.         return mi.proceed();   
  18.      }   
  19. }  



在代碼中,可以看到,就是這裏,會調用advice的before方法!這樣就成功的完成了before通知的編織! 

因爲Spring AOP本身並不打算成爲一個一統天下的AOP框架,秉持Spring的一貫設計理念,設想中的Spring設計目標應該是,致力於AOP框架與IOC容器的緊密集成,通過集成AOP技術爲JavaEE應用開發中遇到的普遍問題提供解決方案,從而爲AOP用戶使用AOP技術提供最大的便利,從這個角度上爲Java EE的應用開發人員服務。在沒有使用第三方AOP解決方案的時候,Spring通過虛擬機的Proxy特性和CGLIB實現了AOP的基本功能,我想,如果有了Spring AOP實現原理的知識背景,再加上我們對源代碼實現的認真解讀,可以爲我們瞭解其他AOP框架與IOC容器的集成原理,也打下了很好的基礎,並真正瞭解一個AOP框架是在怎樣實現的。 

這還真是就是我們喜歡開源軟件一個原因,有了源代碼,軟件就沒有什麼神祕的面紗了!本立而道生,多讀源代碼吧,或者找一本從源代碼出發講解軟件實現的書來看看,就像以前我們學習操作系統,學習TCP/IP那樣!一定會有長進的。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章