Spring框架IOC和AOP的實現原理

1、IoC(Inversion of Control)  java

  (1). IoC(Inversion of Control)是指容器控制程序對象之間的關係,而不是傳統實現中,由程序代碼直接操控。控制權由應用代碼中轉到了外部容器,控制權的轉移是所謂反轉。 對於Spring而言,就是由Spring來控制對象的生命週期和對象之間的關係;IoC還有另一個名字——「依賴注入(Dependency Injection)」。從名字上理解,所謂依賴注入,即組件之間的依賴關係由容器在運行期決定,即由容器動態地將某種依賴關係注入到組件之中。  web

(2). 在Spring的工做方式中,全部的類都會在spring容器中登記,告訴spring這是個什麼東西,你須要什麼東西,而後spring會在系統運行到適當的時候,把你要的東西主動給你,同時也把你交給其餘須要你的東西。全部的類的建立、銷燬都由 spring來控制,也就是說控制對象生存週期的再也不是引用它的對象,而是spring。對於某個具體的對象而言,之前是它控制其餘對象,如今是全部對象都被spring控制,因此這叫控制反轉。正則表達式

(3). 在系統運行中,動態的向某個對象提供它所須要的其餘對象。  spring

(4). 依賴注入的思想是經過反射機制實現的,在實例化一個類時,它經過反射調用類中set方法將事先保存在HashMap中的類屬性注入到類中。 總而言之,在傳統的對象建立方式中,一般由調用者來建立被調用者的實例,而在Spring中建立被調用者的工做由Spring來完成,而後注入調用者,即所謂的依賴注入or控制反轉。 注入方式有兩種:依賴注入和設置注入; IoC的優勢:下降了組件之間的耦合,下降了業務對象之間替換的複雜性,使之可以靈活的管理對象。express

2、AOP(Aspect Oriented Programming)編程

(1). AOP面向方面編程基於IoC,是對OOP的有益補充;數組

(2). AOP利用一種稱爲「橫切」的技術,剖解開封裝的對象內部,並將那些影響了 多個類的公共行爲封裝到一個可重用模塊,並將其名爲「Aspect」,即方面。所謂「方面」,簡單地說,就是將那些與業務無關,卻爲業務模塊所共同調用的 邏輯或責任封裝起來,好比日誌記錄,便於減小系統的重複代碼,下降模塊間的耦合度,並有利於將來的可操做性和可維護性。緩存

(3). AOP表明的是一個橫向的關 系,將「對象」比做一個空心的圓柱體,其中封裝的是對象的屬性和行爲;則面向方面編程的方法,就是將這個圓柱體以切面形式剖開,選擇性的提供業務邏輯。而 剖開的切面,也就是所謂的「方面」了。而後它又以巧奪天功的妙手將這些剖開的切面復原,不留痕跡,但完成了效果。安全

(4). 實現AOP的技術,主要分爲兩大類:一是採用動態代理技術,利用截取消息的方式,對該消息進行裝飾,以取代原有對象行爲的執行;二是採用靜態織入的方式,引入特定的語法建立「方面」,從而使得編譯器能夠在編譯期間織入有關「方面」的代碼。性能優化

(5). Spring實現AOP:JDK動態代理和CGLIB代理 JDK動態代理:其代理對象必須是某個接口的實現,它是經過在運行期間建立一個接口的實現類來完成對目標對象的代理;其核心的兩個類是InvocationHandler和Proxy。 CGLIB代理:實現原理相似於JDK動態代理,只是它在運行期間生成的代理對象是針對目標類擴展的子類。CGLIB是高效的代碼生成包,底層是依靠ASM(開源的java字節碼編輯類庫)操做字節碼實現的,性能比JDK強;須要引入包asm.jar和cglib.jar。     使用AspectJ注入式切面和@AspectJ註解驅動的切面實際上底層也是經過動態代理實現的。

(6). AOP使用場景:                     

Authentication 權限檢查        

Caching 緩存        

Context passing 內容傳遞        

Error handling 錯誤處理        

Lazy loading 延遲加載        

Debugging  調試      

logging, tracing, profiling and monitoring 日誌記錄,跟蹤,優化,校準        

Performance optimization 性能優化,效率檢查        

Persistence  持久化        

Resource pooling 資源池        

Synchronization 同步        

Transactions 事務管理    

另外Filter的實現和struts2的攔截器的實現都是AOP思想的體現。  

3、詳細介紹

       Spring提供了不少輕量級應用開發實踐的工具集合,這些工具集以接口、抽象類、或工具類的形式存在於Spring中。經過使用這些工具集,能夠實現應用程序與各類開源技術及框架間的友好整合。好比有關jdbc封裝的數據訪問工具Spring JDBC,有關編寫單元測試的spring test包以及spring-mock,有關訪問動態腳本語言的Spring Script,另外還有發送郵件的工具Spring Mail、日程及任務處理工具Spring scheduling等。 能夠這麼說,大多數企業級應用開發中常常涉及到的一些通用的問題,均可以經過Spring提供的一些實用工具包輕鬆解決。

       依賴注入的三種方式:(1)接口注入(2)Construct注入(3)Setter注入

       控制反轉(IoC)與依賴注入(DI)是同一個概念,引入IOC的目的:(1)脫開、下降類之間的耦合;(2)倡導面向接口編程、實施依賴倒換原則; (3)提升系統可插入、可測試、可修改等特性。
       具體作法:

     (1)將bean之間的依賴關係儘量地抓換爲關聯關係;
     (2)將對具體類的關聯儘量地轉換爲對Java interface的關聯,而不是與具體的服務對象相關聯;
     (3)Bean實例具體關聯相關Java interface的哪一個實現類的實例,在配置信息的元數據中描述;
     (4)由IoC組件(或稱容器)根據配置信息,實例化具體bean類、將bean之間的依賴關係注入進來。

      org.springframework.beans及org.springframework.context包是Spring IoC容器的基礎。BeanFactory提供的高級配置機制,使得管理任何性質的對象成爲可能。ApplicationContext是BeanFactory的擴展,功能獲得了進一步加強,好比更易與Spring AOP集成、消息資源處理(國際化處理)、事件傳遞及各類不一樣應用層的context實現(如針對web應用的WebApplicationContext)。 簡而言之,BeanFactory提供了配製框架及基本功能,而ApplicationContext則增長了更多支持企業核心內容的功能。ApplicationContext徹底由BeanFactory擴展而來,於是BeanFactory所具有的能力和行爲也適用於ApplicationContext。

     IoC容器負責容納bean,並對bean進行管理。在Spring中,BeanFactory是IoC容器的核心接口。它的職責包括:實例化、定位、配置應用程序中的對象及創建這些對象間的依賴。Spring爲咱們提供了許多易用的BeanFactory實現,XmlBeanFactory就是最經常使用的一個。該實現將以XML方式描述組成應用的對象以及對象間的依賴關係。XmlBeanFactory類將持有此XML配置元數據,並用它來構建一個徹底可配置的系統或應用。

    實現化容器:

  1. Resource resource = new FileSystemResource("beans.xml");  
  2. BeanFactory factory = new XmlBeanFactory(resource);  
  3. ... 或...  
  4. ClassPathResource resource = new ClassPathResource("beans.xml");  
  5. BeanFactory factory = new XmlBeanFactory(resource);  
  6. ... 或...  
  7. ApplicationContext context = new ClassPathXmlApplicationContext(  
  8.         new String[] {"applicationContext.xml", "applicationContext-part2.xml"});  
  9. // of course, an ApplicationContext is just a BeanFactory  
  10. BeanFactory factory = (BeanFactory) context;  

   將XML配置文件分拆成多個部分是很是有用的。爲了加載多個XML文件生成一個ApplicationContext實例,能夠將文件路徑做爲字符串數組傳給ApplicationContext構造器。而bean factory將經過調用bean defintion reader從多個文件中讀取bean定義。

   一般狀況下,Spring團隊傾向於上述作法,由於這樣各個配置並不會查覺到它們與其餘配置文件的組合。另一種方法是使用一個或多個的<import/>元素來從另一個或多個文件加載bean定義。全部的<import/>元素必須放在<bean/>元素以前以完成bean定義的導入。 讓咱們看個例子:

<beans><import resource="services.xml"/>
    <import resource="resources/messageSource.xml"/>
    <import resource="/resources/themeSource.xml"/>
      <bean id="bean1" class="..."/>
    <bean id="bean2" class="..."/>
  </beans>

    在上面的例子中,咱們從3個外部文件:services.xml、messageSource.xml及themeSource.xml來加載bean定義。這裏採用的都是相對路徑,所以,此例中的services.xml必定要與導入文件放在同一目錄或類路徑,而messageSource.xml和themeSource.xml的文件位置必須放在導入文件所在目錄下的resources目錄中。正如你所看到的那樣,開頭的斜槓‘/’實際上可忽略。所以不用斜槓‘/’可能會更好一點。

   根據Spring XML配置文件的Schema(或DTD),被導入文件必須是徹底有效的XML bean定義文件,且根節點必須爲<beans/> 元素。

   BeanFactory和FactoryBean的區別,簡而言之,BeanFactory是加載的容器,加載一切的BEAN,而FactoryBean用於建立代理類

===============================================================上面已講到

 BeanFactory它的職責包括:實例化、定位、配置應用程序中的對象及創建這些對象間的依賴。

FactoryBean(一般狀況下,bean無須本身實現工廠模式,Spring容器擔任工廠角色;但少數狀況下,容器中的bean自己就是工廠,其做用是產生其它bean實例),做用是產生其餘bean實例。一般狀況下,這種bean沒有什麼特別的要求,僅須要提供一個工廠方法,該方法用來返回其餘bean實例。由工廠bean產生的其餘bean實例,再也不由Spring容器產生,所以與普通bean的配置不一樣,再也不須要提供class元素。

ProxyFactoryBean用於建立代理(根據Advisor生成的Bean,也就是TargetBean的代理)

咱們的Advisor,PointCut等等,其最終目的都是爲了建立這個代理。

===============================================================下面將講到

AOP全名Aspect-Oriented Programming,中文直譯爲面向切面(方面)編程,當前已經成爲一種比較成熟的編程思想,能夠用來很好的解決應用系統中分佈於各個模塊的交叉關注點問題。在輕量級的J2EE中應用開發中,使用AOP來靈活處理一些具備橫切性質的系統級服務,如事務處理、安全檢查、緩存、對象池管理等,已經成爲一種很是適用的解決方案。 AOP中比較重要的概念有:Aspect、JoinPoint、PonitCut、Advice、Introduction、Weave、Target Object、Proxy Object等

引介(Introduction)是指給一個現有類添加方法或字段屬性,引介還能夠在不改變現有類代碼的狀況下,讓現有的Java類實現新的接口,或者爲其指定一個父類從而實現多重繼承。相對於加強(Advice)能夠動態改變程序的功能或流程來講,引介(Introduction)則用來改變一個類的靜態結構。好比咱們可讓一個現有爲實現java.lang.Cloneable接口,從而能夠經過clone()方法複製這個類的實例。

攔截器是用來實現對鏈接點進行攔截,從而在鏈接點前或後加入自定義的切面模塊功能。在大多數JAVA的AOP框架實現中,都是使用攔截器來實現字段訪問及方法調用的攔截(interception)。所用做用於同一個鏈接點的多個攔截器組成一個鏈接器鏈(interceptor chain),連接上的每一個攔截器一般會調用下一個攔截器。Spring AOP及JBoos AOP實現都是採用攔截器來實現的。

面向對象編程(OOP)解決問題的重點在於對具體領域模型的抽象,而面向切面編程(AOP)解決問題的關鍵則在於對關注點的抽象。也就是說,系統中對於一些須要分散在多個不相關的模塊中解決的共同問題,則交由AOP來解決;AOP可以使用一種更好的方式來解決OOP不能很好解決的橫切關注點問題以及相關的設計難題來實現鬆散耦合。所以,面向方面編程 (AOP) 提供另一種關於程序結構的思惟完善了OOP,是OOP的一種擴展技術,彌補補了OOP的不足。

AOP概念詳解:注意如下實例<aop:開頭的AspectJ的概念,Spring沒有分的這麼細。

  — 方面(Aspect):一個關注點的模塊化,這個關注點實現可能另外橫切多個對象。事務管理是一個很好的橫切關注點例子。方面用Spring的Advisor或攔截器實現, 而後能夠經過@Aspect標註或在applictionContext.xml中進行配置: 

      <aop:aspect id="fourAdviceAspect" ref="fourAdviceBean" order="2"> 

  — 鏈接點(Joinpoint):程序執行過程當中的行爲,如方法的調用或特定的異常被拋出,在代碼上有JoinPoint類和ProceedingJoinPoint類,以下所示,能夠經過JoinPoint獲取不少參數,JoinPoint通常用在Advice實現方法中做爲參數傳入,ProceedingJoinPoint用於實現圍繞Advice的參數傳入。  經過下面JoinPoint的接口能夠看出經過JoinPoint能夠獲得代理對象和Target對象。

  1. package org.aspectj.lang;  
  2. import org.aspectj.lang.reflect.SourceLocation;  
  3. public interface JoinPoint {  
  4.     String toString();         //鏈接點所在位置的相關信息  
  5.     String toShortString();     //鏈接點所在位置的簡短相關信息  
  6.     String toLongString();     //鏈接點所在位置的所有相關信息  
  7.     Object getThis();         //返回AOP代理對象  
  8.     Object getTarget();       //返回目標對象  
  9.     Object[] getArgs();       //返回被通知方法參數列表  
  10.     Signature getSignature();  //返回當前鏈接點簽名  
  11.     SourceLocation getSourceLocation();//返回鏈接點方法所在類文件中的位置  
  12.     String getKind();        //鏈接點類型  
  13.     StaticPart getStaticPart(); //返回鏈接點靜態部分  
  14. }  
  15.   
  16. public interface ProceedingJoinPoint extends JoinPoint {  
  17.     public Object proceed() throws Throwable;  
  18.     public Object proceed(Object[] args) throws Throwable;  
  19. }  

      — 切入點(Pointcut):指定一個Adivce將被引起的一系列鏈接點的集合。AOP框架必須容許開發者指定切入點,例如,使用正則表達式。

          xml中配置:

<aop:pointcut id="myPointcut" expression="execution(* com.wicresoft.app.service.impl.*.*(..))" method="release" /> 

          或使用Annoation :@pointcut("execution * transfer(..)")並用一個返回值爲void,方法體爲空的方法來命名切入點如:  private void anyOldTransfer(){}

     以後就能夠在Advice中引用,如: @AfterReturning(pointcut="anyOldTransfer()", returning="reVal")  

  1. package org.springframework.aop;  
  2. public interface Pointcut {  
  3.     ClassFilter getClassFilter();  
  4.     MethodMatcher getMethodMatcher();  
  5.     Pointcut TRUE = TruePointcut.INSTANCE;  
  6. }  
  7. package org.springframework.aop;  
  8. public interface ClassFilter {  
  9.      boolean matches(Class<?> clazz);//若是clazz與咱們關注的現象相符時返回true,負責返回false  
  10.      ClassFilter TRUE = TrueClassFilter.INSTANCE;//靜態參數 若是類型對於要撲捉的Pointcut來講無所謂,可將此參數傳遞給Pointcut  
  11. }  
  12. package org.springframework.aop;  
  13. public interface MethodMatcher {  
  14.    boolean matches(Method method, Class<?> targetClass);  
  15.   
  16.  /** 
  17.   * 是否對參數值敏感 
  18.   * 若是爲false代表匹配時不須要判斷參數值(參數值不敏感),稱之爲StaticMethodMatcher,這時只有 
  19.   * matches(Method method, Class<?> targetClass); 被執行,執行結果能夠緩存已提升效率。 
  20.   * 若是爲true代表匹配時須要判斷參數值(參數值敏感),稱之爲DynamicMethodMatcher,這時先執行 
  21.   * matches(Method method, Class<?> targetClass);若是返回true,而後再執行 
  22.   * boolean matches(Method method, Class<?> targetClass, Object[] args);已作進一步判斷 
  23.   *  
  24.   */  
  25.  boolean isRuntime();  
  26.  boolean matches(Method method, Class<?> targetClass, Object[] args);  
  27.  MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;  
  28. }  

關於PointCut中使用的execution的說明:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?) 

modifiers-pattern:方法的操做權限

ret-type-pattern:返回值

declaring-type-pattern:方法所在的包

name-pattern:方法名

parm-pattern:參數名

throws-pattern:異常

記憶法則就是Java定義一個方法時的樣子:public boolean produceValue(int oo) throws Exception, 只要在方法名前加上包名就能夠了。

其中,除ret-type-pattern和name-pattern以外,其餘都是可選的。上例中,execution(* com.spring.service.*.*(..))表示com.spring.service包下,返回值爲任意類型;方法名任意;參數不做限制的全部方法。

常見的PointCut結構圖:
 

  — 通知(Advice):在特定的鏈接點,AOP框架執行的動做。各類類型的通知包括「around」、「before」和「throws」通知。通知類型將在下面討論。許多AOP框架包括Spring都是以攔截器作通知模型,維護一個「圍繞」鏈接點的攔截器鏈。Advice中必須用到PointCut

     在xml中配置,配置中的method爲Aspect實現類中的方法名,使用pointcut自定義或pointcut-ref進行引用已有pointcut

       <aop:before pointcut="execution(* com.wicresoft.app.service.impl.*.*(..))" method="authority" /> 

       <aop:after pointcut="execution(* com.wicresoft.app.service.impl.*.*(..))" method="release" /> 

       <aop:after-returning pointcut="execution(* com.wicresoft.app.service.impl.*.*(..))" method="log" />

       <aop:around pointcut="execution(* com.wicresoft.app.service.impl.*.*(..))" method="processTx" /> 

       <aop:after-throwing pointcut-ref="myPointcut" method="doRecovertyActions" throwing="ex" />     

     或使用Annoation: 

       @Before("execution(* com.wicresoft.app.service.impl.*.*(..))")

       @AfterReturning(returning="rvt", pointcut="execution(* com.wicresoft.app.service.impl.*.*(..))")

       @AfterThrowing(throwing="ex", pointcut="execution(* com.wicresoft.app.service.impl.*.*(..))")

       @After("execution(* com.wicresoft.app.service.impl.*.*(..))")

       @Around("execution(* com.wicresoft.app.service.impl.*.*(..))")  

注意

  • AfterReturning 加強處理處理只有在目標方法成功完成後纔會被織入。
  • After 加強處理無論目標方法如何結束(保存成功完成和遇到異常停止兩種狀況),它都會被織入。

使用方法攔截器的around通知,需實現接口MethodInterceptor:

public interface MethodInterceptor extends Interceptor {

    Object invoke(MethodInvocation invocation) throws Throwable;

}

invoke()方法的MethodInvocation 參數暴露將被調用的方法、目標鏈接點、AOP代理和傳遞給被調用方法的參數。 invoke()方法應該返回調用的結果:鏈接點的返回值。

一個簡單的MethodInterceptor實現看起來以下:

  1. public class DebugInterceptor implements MethodInterceptor {  
  2.     public Object invoke(MethodInvocation invocation) throws Throwable {  
  3.         System.out.println("Before: invocation=[" + invocation + "]");  
  4.         Object rval = invocation.proceed();  
  5.         System.out.println("Invocation returned");  
  6.         return rval;  
  7.     }  
  8. }  

注意MethodInvocation的proceed()方法的調用。這個調用會應用到目標鏈接點的攔截器鏈中的每個攔截器。大部分攔截器會調用這個方法,並返回它的返回值。可是, 一個MethodInterceptor,和任何around通知同樣,能夠返回不一樣的值或者拋出一個異常,而不調用proceed方法。可是,沒有好的緣由你要這麼作。

Before通知:需實現MethodBeforeAdvice接口

public interface MethodBeforeAdvice extends BeforeAdvice {

    void before(Method m, Object[] args, Object target) throws Throwable;

}

Throw通知,需實現ThrowsAdvice接口

After Returning通知須直線AfterReturningAdvice接口

public interface AfterReturningAdvice extends Advice {

    void afterReturning(Object returnValue, Method m, Object[] args, Object target)

            throws Throwable;

}

       — 引入(Introduction):添加方法或字段到被通知的類,引入新的接口到任何被通知的對象。例如,你可使用一個引入使任何對象實現IsModified接口,來簡化緩存。使用introduction要有三個步驟(1)聲明新接口(2)建立本身的IntrouductionInterceptor經過Implements IntroductionInterceptor或extends DelegatingIntroductionInterceptor 並同時implements(1)中聲明的接口 (3)將新接口和自定義的IntroductionInterceptor配置到DefaultIntroductionAdvisor中,而後將前三者配置到ProxyFactoryBean中。

  1. public interface IOtherBean {  
  2.     public void doOther();  
  3. }  
  4.   
  5. public class SomeBeanIntroductionInterceptor implements IOtherBean, IntroductionInterceptor {  
  6.   
  7.     public void doOther() {  
  8.         System.out.println("doOther!");  
  9.     }  
  10.   
  11.     public Object invoke(MethodInvocation invocation) throws Throwable {  
  12.           
  13.         //判斷調用的方法是否爲指定類中的方法  
  14.         if ( implementsInterface(invocation.getMethod().getDeclaringClass()) ) {  
  15.             return invocation.getMethod().invoke(this, invocation.getArguments());  
  16.         }  
  17.           
  18.         return invocation.proceed();  
  19.     }  
  20.       
  21.     /** 
  22.      * 判斷clazz是否爲給定接口IOtherBean的實現 
  23.      */  
  24.     public boolean implementsInterface(Class clazz) {  
  25.           
  26.         return clazz.isAssignableFrom(IOtherBean.class);  
  27.     }  
  28. }  

 

  1. <!-- 目標對象 -->  
  2. <bean id="someBeanTarget" class="aop.spring.introduction.SomeBeanImpl" />  
  3. <!-- 通知 -->  
  4. <bean id="someBeanAdvice" class="aop.spring.introduction.SomeBeanIntroductionInterceptor" />  
  5. <!-- 通知者,只能以構造器方法注入-->  
  6. <bean id="introductionAdvisor" class="org.springframework.aop.support.DefaultIntroductionAdvisor">  
  7.     <constructor-arg ref="someBeanAdvice" />  
  8.     <constructor-arg value="aop.spring.introduction.IOtherBean" />      
  9. </bean>  
  10.       
  11. <!-- 代理 (將咱們的切面織入到目標對象)-->  
  12. <bean id="someBeanProxy" class="org.springframework.aop.framework.ProxyFactoryBean">  
  13.     <!-- 若目標對象實現了代理接口,則能夠提供代理接口的配置 -->  
  14.     <property name="proxyInterfaces"  value="aop.spring.introduction.ISomeBean" />  
  15.     <!-- 配置目標對象 -->  
  16.     <property name="target" ref="someBeanTarget" />  
  17.     <!-- 配置切面 -->  
  18.     <property name="interceptorNames">  
  19.         <list>  
  20.             <value>introductionAdvisor</value>  
  21.         </list>  
  22.     </property>  
  23. </bean>  

    — 攔截器(Advisor )經常使用的有PointCutAdvisor和IntroudtionAdvisor。前者Advisor有PointCut和Advice組成,知足Poincut(指定了哪些方法須要加強),則執行相應的Advice(定義了加強的功能),後者由Introduction構成。PointCutAdvisor主要是根據PointCut中制定的Target Objects的方法在調用(前,後,around,throws, after-return等)時引入新的Aspect中的methods, 而IntroductionAdvisor主要是引入新的接口到Targets對象中。

  1. public interface PointcutAdvisor {  
  2.     Pointcut getPointcut();  
  3.     Advice getAdvice();  
  4. }  

一、 PointcutAdvisor: Advice和Pointcut,默認實現爲DefaultPointcutAdvisor, 還有NameMatchMethodPointcutAdvisor,RegexpMethodPointcutAdvisor等。 

 其中NameMacthMethodPointCutAdvisor、RegexpMethodPointCutAdvisor 能夠對比經常使用的PointCut類有NameMatchedMethodPointCut和JdkRegexMethodPointCut。
 前者須要注入mappedName和advice屬性,後者須要注入pattern和advice屬性。其中mappedName和pattern是直接配置的值,而advice須要本身實現具體的advice,可見實現advisor的時候,不須要實現PointCut,通常PointCut只須要配置就行了,不須要具體實現類
 mappedName指明瞭要攔截的方法名,pattern按照正則表達式的方法指明瞭要攔截的方法名,advice定義了一個加強(須要本身實 現MethodBeforeAdvice、  MethodAfterAdvice、ThrowsAdvice、MethodInterceptor接口之 一)。而後在ProxyFactoryBean的攔截器(interceptorNames)中注入這個PointCutAdvisor便可,如上面這個ProxyFactoryBean是一個靜態代理,只能代理一個類給加上AOP,那麼這個靜態代理須要注入有目標對象,目標對象的接口,和interceptorsNames

二、 IntroductionAdvisor :默認實現爲DefaultIntroductionAdvisor,這個主要與Introduction有關,能夠參考上面的例子

  — 目標對象(Target Object):包含鏈接點的對象,也被稱做被通知或被代理對象。

  — AOP代理(AOP Proxy):AOP框架建立的對象,包含通知。在Spring中,AOP代理能夠是JDK動態代理或CGLIB代理。如ProxyFactory,ProxyFactoryBean, 下面會進行詳細說明    

  — 編織(Weaving):組裝方面來建立一個被通知對象。這能夠在編譯時完成(例如使用AspectJ編譯器),也能夠在運行時完成。Spring和其餘純Java AOP框架同樣,在運行時完成織入。將Aspect加入到程序代碼的過程,對於Spring AOP,由ProxyFactory或者ProxyFactoryBean負責織入動做。 

    經過ProxyFactory能夠將對符合條件的類調用時添加上Aspect。

    或者 可以使用XML聲明式 ProxyFactoryBean:須要設定 target,interceptorNames(能夠是Advice或者Advisor,注意順序, 對接口代理需設置proxyInterfaces 

注意:一個ProxyFactoryBean只能指定一個代理目標,不是很方便,這就產生了自動代理。經過自動代理,能夠實現自動爲多個目標Bean實現AOP代理、避免客戶端直接訪問目標Bean(即getBean返回的都是Bean的代理對象)。spring的自動代理是經過BeanPostProcessor實現的,容器載入xml配置後會修改bean爲代理Bean,而id不變。 

ApplicationContext能夠直接檢測到定義在容器中的BeanPostProcessor,BeanFactory須要手動添加。 
有2種經常使用的BeanPostProcessor: 
1.BeanNameAutoProxyCreator 故名思議,BeanName須要注入的兩個屬性有BeanNames和interceptorNames

  1. <bean id="loginBeforeAdvisor" .../>    
  2. <bean id="loginThrowsAdvisor" .../>    
  3. <bean  class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">    
  4.         <!-- 注入目標Bean -->    
  5.         <property name="beanNames" value="*Service">    
  6.         </property>    
  7.         <property name="interceptorNames">    
  8.             <list>    
  9.                 <value>loginBeforeAdvisor</value>    
  10.                 <value>loginThrowsAdvisor</value>    
  11.             </list>    
  12.         </property>    
  13. </bean>    

2.DefaultAdvisorAutoProxyCreator: DefaultAdvisorAutoProxyCreator和BeanNameAutoProxyCreator不一樣的是,前者只和Advisor 匹配, 該類實現了BeanPostProcessor接口。當應用上下文讀入全部的Bean的配置信息後,該類將掃描上下文,尋找全部的Advisor,他將這些Advisor應用到全部符合切入點的Bean中。因此下面的xml中沒有綁定也無需綁定DefaultAdvisorAutoProxyCreator與Advisor的關係。

  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">  
  3. <beans>  
  4.      <bean id="kwikEMartTarget" class="demo.ApuKwikEMart"></bean>  
  5.      <bean id="performanceThresholdInterceptor" class="demo.advice.PerformanceThresholdInterceptor">  
  6.           <constructor-arg>  
  7.                <value>5000</value>  
  8.           </constructor-arg>  
  9.      </bean>  
  10.      <!-- 使用RegexpMethodPointcutAdvisor來匹配切入點完成個一個Advisor; -->  
  11.      <bean id="regexpFilterPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">  
  12.           <property name="pattern">  
  13.                <!-- 匹配的名字爲方法名-->  
  14.                <value>.*buy.*</value>  
  15.           </property>  
  16.           <property name="advice">  
  17.                <ref bean="performanceThresholdInterceptor"/>  
  18.           </property>  
  19.      </bean>  
  20.      <bean id="defaultAdvisorAutoProxyCreator"  
  21.      class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />  
  22. </beans>  

在使用Aonnotation的時候,須要進行在ApplicationContext.xml中進行配置:

  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.        xmlns:aop="http://www.springframework.org/schema/aop"        
  5.        xsi:schemaLocation="http://www.springframework.org/schema/beans  
  6.            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  7.            http://www.springframework.org/schema/aop  
  8.        http://www.springframework.org/schema/beans/spring-aop-3.0.xsd">  
  9.         <!-- 啓動對@AspectJ註解的支持 -->  
  10.         <aop:aspectj-autoproxy/>  
  11. </beans>  


 

綜上,Spring下AOP的配置與實現,BeanNameAutoProxyCreator,DefaultAdvisorAutoProxyCreator已經部分簡化了AOP配置,然而仍是很繁瑣: 首先要編寫xxxAdvice類(須要本身實現MethodBeforeAdvice、MethodAfterAdvice、 ThrowsAdvice、MethodInterceptor接口之一),而後還要在xml中配置Advisor,還要在Advisor中注入 Advice,最後還要將Advisor加入ProxyFactoryBean、BeanNameAutoProxyCreator或者 DefaultAdvisorAutoProxyCreator中

實際上AOP不止Spring進行了實現,還有AspectJ,後者對AOP中的概念實現比較完全,能夠看上面,而Spring中對AOP的方方面面進行簡化,拿上面定義的regexpFilterPointcutAdvisor是一種Advisor包含了PointCut和Advice,而此處的PointCut就是pattern屬性的值了,沒有特定的PointCut Bean定義,而advice定義了Bean。而其餘概念Aspect, JoinPoint都融匯於Advice的實現中即Advisor(MethodBeforeAdvice等和MethodIntector接口的實現類)或IntroductionInterceptor了

相關文章
相關標籤/搜索