Spring3開發實戰 之 第三章:AOP開發(1) – JAVA編程語言程序開發技術文章

AOP是什麼(Aspect   Oriented   Programming)
AOP是一種編程范式,提供從另一個角度來考慮程序結構以完善面向對象編程(OOP)。
AOP為開發者提供瞭一種描述橫切關註點的機制,並能夠自動將橫切關註點織入到面向對象的軟件系統中,從而實現瞭橫切關註點的模塊化。
 AOP能夠將那些與業務無關,卻為業務模塊所共同調用的邏輯或責任,例如事務處理、日志管理、權限控制等,封裝起來,便於減少系統的重復代碼,降低模塊間的耦合度,並有利於未來的可操作性和可維護性。
AOP能幹什麼,也是AOP帶來的好處
1:降低模塊的耦合度
2:使系統容易擴展
3:設計決定的遲綁定:使用AOP,設計師可以推遲為將來的需求作決定,因為它
可以把這種需求作為獨立的方面很容易的實現。
4:更好的代碼復用性

 



仍然存在問題:
大傢會發現,需要修改的地方分散在很多個文件中,如果需要修改的文件多那麼修改的量會很大,這無疑會增加出錯的幾率,並且加大系統維護的難度。
而且,如果添加功能的需求是在軟件開發的後期才提出的話,這樣大量修改已有的文件,也不符合基本的“開-閉原則”。
 
改進的解決方案
采用裝飾器模式或者代理模式來實現。
 
裝飾器模式定義
動態地給一個對象添加一些額外的職責。就增加功能來說, 裝飾器模式相比生成子類更為靈活。
代理模式定義
為其他對象提供一種代理以控制對這個對象的訪問
 
JDK動態代理解決方案(比較通用的解決方案)

java代碼:
查看復制到剪貼板打印
public class MyInvocationHandler implements InvocationHandler { 
    private Object target; 
    public MyInvocationHandler(Object target) { 
        this.target = target; 
    } 
    public Object invoke(Object proxy, Method method, Object[] args) 
throws Throwable { 
        //1.記錄日志    2.時間統計開始      3.安全檢查 
        Object retVal = method.invoke(target, args); 
        //4.時間統計結束 
        return retVal;   
    } 
    public Object proxy() { 
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), 
                target.getClass().getInterfaces(), new MyInvocationHandler(target)); 
    } 

CGLIB動態代理的解決方案:

java代碼:
查看復制到剪貼板打印
public class MyInterceptor implements MethodInterceptor  { 
    private Object target; 
    public MyInterceptor(Object target) { 
        this.target = target; 
    } 
    public Object intercept(Object proxy, Method method, Object[] args, 
                            MethodProxy invocation) throws Throwable { 
        //1.記錄日志 2.時間統計開始   3.安全檢查 
        Object retVal = invocation.invoke(target, args); 
        //4.時間統計結束 
        return retVal;   
    } 
    public Object proxy() { 
        return Enhancer.create(target.getClass(), new MyInterceptor(target)); 
    } 

JDK動態代理的特點
不能代理類,隻能代理接口
CGLIB動態代理的特點
能代理類和接口,不能代理final類
動態代理的本質
用來實現對目標對象進行增強,最終表現為類,隻不過是動態創建子類,不用手工生成子類。
 
動態代理的限制
隻能在父類方法被調用之前或之後進行增強(功能的修改),不能在中間進行修改,要想在方法調用中增強,需要ASM(一個Java 字節碼操作和分析框架)

更好的解決方案——AOP提供
人們認識到,傳統的程序經常表現出一些不能自然地適合跨越多個程序模塊的行為,例如日志記錄、對上下文敏感的錯誤處理等等,人們將這種行為稱為“橫切關註點(CrossCuttingConcern)”,因為它跨越瞭給定編程模型中的典型職責界限。
如果使用過用於密切關註點的代碼,您就會知道缺乏模塊性所帶來的問題。因為橫切行為的實現是分散的,開發人員發現這種行為難以作邏輯思維、實現和更改。因此,面向方面的編程AOP應運而生。
 
回過頭來再次理解AOP的概念。
關註點
就是所關註的公共功能,比如像事務管理,就是一個關註點。表示 “要做什麼”
連接點(Joinpoint):
在程序執行過程中某個特定的點,通常在這些點需要添加關註點的功能,比如某方法調用的時候或者處理異常的時候。在Spring AOP中,一個連接點總是代表一個方法的執行。表示 “在什麼地方做”
通知(Advice):
在切面的某個特定的連接點(Joinpoint)上執行的動作。
通知有各種類型,其中包括“around”、“before”和“after”等通知。 通知的類型將在後面部分進行討論。許多AOP框架,包括Spring,都是以攔截器做通知模型,並維護一個以連接點為中心的攔截器鏈。表示 “具體怎麼做”
切面/方面(Aspect):
一個關註點的模塊化,這個關註點可能會橫切多個對象。 綜合表示 “ 在什麼地方,要 做什麼,以及具體如何做 ”
切入點(Pointcut):
匹配連接點的斷言。通知和一個切入點表達式關聯,並在滿足這個切入點的連接點上運行(例如,當執行某個特定名稱的方法時)。切入點表達式如何和連接點匹配是AOP的核心:Spring缺省使用AspectJ切入點語法。
目標對象(Target Object):
被一個或者多個切面所通知(advise)的對象。也有人把它叫做 被通知(advised) 對象。 既然Spring AOP是通過運行時代理實現的,這個對象永遠是一個 被代理(proxied) 對象。
AOP代理(AOP Proxy):
 AOP框架使用代理模式創建的對象,從而實現在連接點處插入通知(即應用切面),就是 通過代理來對目標對象應用切面。在Spring中,AOP代理可以用JDK動態代理或CGLIB代理實現,而通過攔截器模型應用切面。 註意:Spring引入的基於模式(schema-based)風格和@AspectJ註解風格的切面聲明,對於使用這些風格的用戶來說,代理的創建是透明的。
 
 
織入(Weaving):
把切面連接到其它的應用程序類型或者對象上,並創建一個被通知的對象的過程。也就是說織入是一個過程,是將切面應用到目標對象從而創建出AOP代理對象的過程。這些可以在編譯時(例如使用AspectJ編譯器),類加載時和運行時完成。Spring和其他純Java AOP框架一樣,在運行時完成織入。
引入(Introduction):
也被稱為內部類型聲明(inter-type declaration)。為已有的類聲明額外的方法或者某個類型的字段。 Spring允許引入新的接口(以及一個對應的實現)到任何被代理的對象。 例如,你可以使用一個引入來使bean實現 IsModified 接口,以便簡化緩存機制。

前置通知(Before advice):
在某連接點之前執行的通知,但這個通知不能阻止連接點前的執行(除非它拋出一個異常)。
返回後通知(After returning advice):
在某連接點正常完成後執行的通知:例如,一個方法沒有拋出任何異常,正常返回。
拋出異常後通知(After throwing advice):
在方法拋出異常退出時執行的通知。
後通知(After (finally) advice):
當某連接點退出的時候執行的通知(不論是正常返回還是異常退出)。
環繞通知(Around Advice):
包圍一個連接點的通知,如方法調用。這是最強大的一種通知類型。 環繞通知可以在方法調用前後完成自定義的行為。它也會選擇是否繼續執行連接點或直接返回它們自己的返回值或拋出異常來結束執行。

 
構建環境和前面一樣
定義一個接口如下:

java代碼:
查看復制到剪貼板打印
package cn.javass.Spring3.aop; 
public interface Api { 
public String testAop(); 

寫一個類實現這個接口

java代碼:
查看復制到剪貼板打印
public class Impl implements Api{ 
public String testAop() { 
System.out.println("test aop"); 
return "aop is ok"; 


寫一個類作為Before的Advice,沒有任何特殊要求,就是一個普通類

java代碼:
查看復制到剪貼板打印
public class MyBefore { 
public void b1(){ 
System.out.println("now befoer———>"); 


配置文件,要註意配置的時候要保證一定要有命名空間aop的定義,如下:

java代碼:
查看復制到剪貼板打印
<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:context="http://www.springframework.org/schema/context" 
xmlns:aop="http://www.springframework.org/schema/aop" 
xmlns:tx="http://www.springframework.org/schema/tx" 
xsi:schemaLocation=" 
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd 
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd 
"> 
<bean id=“testAopApi” class=“cn.javass.spring3.aop.Impl"></bean> 
<bean id=“myBefore” class=“cn.javass.spring3.aop.MyBefore"></bean> 
  
<aop:config> 
  <aop:aspect id="abcd" ref="myBefore"> 
    <aop:pointcut id="myPointcut" 
        expression=“execution(* cn.javass.spring3.aop.*.*(..))"/> 
    <aop:before 
       pointcut-ref="myPointcut" 
       method="b1"/>  
  </aop:aspect> 
</aop:config> 
</beans> 
客戶端如下:

java代碼:
查看復制到剪貼板打印
public class Client { 
public static void main(String[] args) { 
ApplicationContext context = new ClassPathXmlApplicationContext( 
        new String[] {"applicationContext.xml"}); 
Api api = (Api)context.getBean("testAopApi"); 
String s = api.testAop(); 
System.out.println("s=="+s); 


  
測試結果如下:
now befoer———>
test aop
s==aop is ok

作者:jinnianshilongnian

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *