第八講 面向切面編程(AOP)

一、AOP爲Aspect Oriented Programming的縮寫,意爲:面向切面編程。

二、AOP在Spring中的作用

     提供聲明式服務(最重要的服務是聲明式事務管理)

     允許用戶實現自定義切面

三、AOP:在不改變原有代碼的情況下,增加新的功能。

     傳統的編程模式:


     AOP的編程模式:橫向的編程


   AOP就是通過代理(Proxy)的方式來實現的。

四、AOP的好處(就是使用代理的好處)

   使得真實角色處理的業務更加純粹,不再去關注一些公共的事情。
   公共的業務由代理來完成 --- 實現了業務的分工。
   公共業務發生擴展時變得更加集中和方便。

五、名詞解釋

     關注點:增加的某個業務。如:日誌、安全、緩存、事務、異常處理等。
     
- 切面(Aspect):一個關注點的模塊化,這個關注點可能會橫切多個對象。事務管理是J2EE應用中一個關於橫切關注點的很好的例子。在Spring AOP中,切面可以使用基於模式)或者基於@Aspect註解的方式來實現。

- 連接點(Joinpoint):在程序執行過程中某個特定的點,比如某方法調用的時候或者處理異常的時候。在Spring AOP中,一個連接點總是表示一個方法的執行。

- 通知(Advice):在切面的某個特定的連接點上執行的動作。其中包括了“around”、“before”和“after”等不同類型的通知(通知的類型將在後面部分進行討論)。許多AOP框架(包括Spring)都是以攔截器做通知模型,並維護一個以連接點爲中心的攔截器鏈。

- 切入點(Pointcut):匹配連接點的斷言。通知和一個切入點表達式關聯,並在滿足這個切入點的連接點上運行(例如,當執行某個特定名稱的方法時)。切入點表達式如何和連接點匹配是AOP的核心:Spring缺省使用AspectJ切入點語法。

- 引入(Introduction):用來給一個類型聲明額外的方法或屬性(也被稱爲連接類型聲明(inter-type declaration))。Spring允許引入新的接口(以及一個對應的實現)到任何被代理的對象。例如,你可以使用引入來使一個bean實現IsModified接口,以便簡化緩存機制。

- 目標對象(Target Object): 被一個或者多個切面所通知的對象。也被稱做被通知(advised)對象。 既然Spring AOP是通過運行時代理實現的,這個對象永遠是一個被代理(proxied)對象。

- AOP代理(AOP Proxy):AOP框架創建的對象,用來實現切面契約(例如通知方法執行等等)。在Spring中,AOP代理可以是JDK動態代理或者CGLIB代理。

- 織入(Weaving):把切面連接到其它的應用程序類型或者對象上,並創建一個被通知的對象。這些可以在編譯時(例如使用AspectJ編譯器),類加載時和運行時完成。Spring和其他純Java AOP框架一樣,在運行時完成織入。

通知類型:

- 前置通知(Before advice):在某連接點之前執行的通知,但這個通知不能阻止連接點之前的執行流程(除非它拋出一個異常)。

- 後置通知(After returning advice):在某連接點正常完成後執行的通知:例如,一個方法沒有拋出任何異常,正常返回。

- 異常通知(After throwing advice):在方法拋出異常退出時執行的通知。

- 最終通知(After (finally) advice):當某連接點退出的時候執行的通知(不論是正常返回還是異常退出)。

- 環繞通知(Around Advice):包圍一個連接點的通知,如方法調用。這是最強大的一種通知類型。環繞通知可以在方法調用前後完成自定義的行爲。它也會選擇是否繼續執行連接點或直接返回它自己的返回值或拋出異常來結束執行。

五、使用Spring實現AOP

     第一種實現方式:通過Spring API來實現

     導包:

           <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>4.2.1.RELEASE</version>
           </dependency>
           <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.8.10</version>
           </dependency>

     目標類:

publicclass UserServiceImpl implementsUserService {
     publicvoid add() {
           System.out.println("增加用戶");
     }
     publicvoid update() {
           System.out.println("修改用戶");
     }
     publicvoid delete() {
           System.out.println("刪除用戶");
     }
     publicvoid search() {
           System.out.println("查詢用戶");
     }
}

     通知:

          前置通知:

publicclass Log implementsMethodBeforeAdvice {
     
     /**
      *@param method 被調用的方法對象
      *@param args 被調用的方法的參數
      *@param target 被調用的方法的目標對象
      */
     publicvoid before(Method method, Object[]args, Objecttarget)
                throwsThrowable {
           System.out.println(target.getClass().getName() + "的" + method.getName() +"方法被執行");
     }
     
}

          後置通知:

publicclass AfterLog implementsAfterReturningAdvice {
     
     /**
      * 目標方法執行後執行的通知
      *@param returnValue 被調用的方法對象的返回值
      *@param method 被調用的方法對象
      *@param args 被調用的方法對象的參數
      *@param target 被調用的方法對象的目標對象
      */
     publicvoid afterReturning(ObjectreturnValue, Methodmethod, Object[]args, Objecttarget)
                throwsThrowable {
           System.out.println(target.getClass().getName() + "的" + method.getName()
           +"被成功執行,返回值是:" + returnValue);
     }
}

     Spring配置:

<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:aop="http://www.springframework.org/schema/aop"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/aop
     http://www.springframework.org/schema/aop/spring-aop.xsd">
     
     <beanid="userService"class="com.liujie.service.impl.UserServiceImpl"></bean>
     
     <beanid="log"class="com.liujie.log.Log"></bean>
     
     <beanid="afterLog"class="com.liujie.log.AfterLog"></bean>
     
     <aop:config>
           <aop:pointcutexpression="execution(* com.liujie.service.impl.*.*(..))"
           id="pointcut"/>
           <aop:advisoradvice-ref="log"pointcut-ref="pointcut"/>
           <aop:advisoradvice-ref="afterLog"pointcut-ref="pointcut"/>
     </aop:config>
     
</beans>

     測試:

publicclass Test {
     publicstatic void main(String[]args) {
           ApplicationContextcontext = newClassPathXmlApplicationContext("beans.xml");
           
           UserServiceuserService = (UserService)context.getBean("userService");
           userService.add();
     }
}

   AOP的重要性:很重要。

   Spring AOP就是將公共的業務(如:日誌、安全等)和領域業務結合。當執行領域業務時,將會把公共業務加進來。實現公共業務的重複
   利用。領域業務更純粹。程序員專注於領域業務。其本質還是動態代理

     第二種實現方式:自定義類來實現

publicclass UserServiceImpl implementsUserService {
     publicvoid add() {
           System.out.println("增加用戶");
     }
     publicvoid update() {
           System.out.println("修改用戶");
     }
     publicvoid delete() {
           System.out.println("刪除用戶");
     }
     publicvoid search() {
           System.out.println("查詢用戶");
     }
}

publicclass Log {
     
     publicvoid before() {
           System.out.println("---------方法執行前--------");
     }
     
     publicvoid after() {
           System.out.println("---------方法執行後--------");
     }
     
}

<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:aop="http://www.springframework.org/schema/aop"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/aop
     http://www.springframework.org/schema/aop/spring-aop.xsd">
     
     <beanid="userService"class="com.liujie.service.impl.UserServiceImpl"></bean>
     
     <beanid="log"class="com.liujie.log.Log"></bean>
     
     <aop:config>
           <aop:aspectref="log">
                <aop:pointcutexpression="execution(* com.liujie.service.impl.*.*(..))"
                id="pointcut"/>
                <aop:beforemethod="before"pointcut-ref="pointcut"/>
                <aop:aftermethod="after"pointcut-ref="pointcut"/>  
           </aop:aspect>
     </aop:config>
     
</beans>

publicclass Test {
     publicstatic void main(String[]args) {
           ApplicationContextcontext = newClassPathXmlApplicationContext("beans.xml");
           
           UserServiceuserService = (UserService)context.getBean("userService");
           userService.add();
     }
}

     第三種實現方式:通過註解來實現

@Aspect
publicclass Log {
     
     @Before("execution(* com.liujie.service.impl.*.*(..))")
     publicvoid before() {
           System.out.println("---------方法執行前--------");
     }
     
     @After("execution(* com.liujie.service.impl.*.*(..))")
     publicvoid after() {
           System.out.println("---------方法執行後--------");
     }
     
     @Around("execution(* com.liujie.service.impl.*.*(..))")
     publicObject around(ProceedingJoinPoint joinPoint) throws Throwable {
           System.out.println("還繞前");
           System.out.println("簽名:"+ joinPoint.getSignature());
           //執行目標方法
           Objectresult = joinPoint.proceed();
           System.out.println("還繞後");
           returnresult;
     }
     
}

<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:aop="http://www.springframework.org/schema/aop"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/aop
     http://www.springframework.org/schema/aop/spring-aop.xsd">
     
     <beanid="userService"class="com.liujie.service.impl.UserServiceImpl"></bean>
     
     <beanid="log"class="com.liujie.log.Log"></bean>
     
     <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
     
</beans>


     使用第一種方式實現異常通知的AOP:

Spring提供類型檢查的異常通知(typed throws advice),這意味着org.springframework.aop.ThrowsAdvice接口不包含任何方法:它只是一個標記接口用來標識所給對象實現了一個或者多個針對特定類型的異常通知方法。這些方法應當滿足下面的格式:

afterThrowing([Method, args, target],subclassOfThrowable)

只有最後一個參數是必須的。根據異常通知方法對方法及參數的需求,方法的簽名可以有一個或者四個參數。

publicclass ExceptionLog implementsThrowsAdvice {
     
     publicvoid afterThrowing(Exception ex) throws Throwable {
           
     }
     
}

下面是一些異常通知的例子。

當一個RemoteException(包括它的子類)被拋出時,下面的通知會被調用:

publicclass RemoteThrowsAdvice implementsThrowsAdvice {
     
   public void afterThrowing(RemoteExceptionex)throws Throwable {
       // Do something with remote exception
    }
     
}

當一個ServletException被拋出,下面的通知將被調用。 和上面的通知不同,它聲明瞭4個參數,因此它可以訪問被調用的方法,方法的參數以及目標對象:

publicclass ServletThrowsAdviceWithArguments implements ThrowsAdvice {
     publicvoid afterThrowing(Method m, Object[]args, Objecttarget, ServletExceptionex) {
           // Do something with all arguments
     }
}

最後一個例子說明怎樣在同一個類裏使用兩個方法來處理 RemoteException和ServletException。可以在一個類裏組合任意數量的異常通知方法。

publicstatic class CombinedThrowsAdviceimplementsThrowsAdvice {
     publicvoid afterThrowing(RemoteException ex) throws Throwable {
           // Do something with remote exception
     }
     publicvoid afterThrowing(Method m, Object[]args, Objecttarget, ServletExceptionex) {
           // Do something with all arguments
     }
}

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