1. 程式人生 > >你如何理解AOP中的連線點(Joinpoint)、切點(Pointcut)、增強(Advice)、引介(Introduction)、織入(Weaving)、切面(Aspect)這些概念?

你如何理解AOP中的連線點(Joinpoint)、切點(Pointcut)、增強(Advice)、引介(Introduction)、織入(Weaving)、切面(Aspect)這些概念?

a. 連線點(Joinpoint):程式執行的某個特定位置(如:某個方法呼叫前、呼叫後,方法丟擲異常後)。一個類或一段程式程式碼擁有一些具有邊界性質的特定點,這些程式碼中的特定點就是連線點。Spring僅支援方法的連線點。 
b. 切點(Pointcut):如果連線點相當於資料中的記錄,那麼切點相當於查詢條件,一個切點可以匹配多個連線點。Spring AOP的規則解析引擎負責解析切點所設定的查詢條件,找到對應的連線點。 
c. 增強(Advice):增強是織入到目標類連線點上的一段程式程式碼。Spring提供的增強介面都是帶方位名的,比如:BeforeAdvice、AfterReturningAdvice、ThrowsAdvice等。很多資料上將增強譯為“通知”,這明顯是個詞不達意的翻譯,讓很多程式設計師困惑了許久。

說明: Advice在國內的很多書面資料中都被翻譯成"通知",但是很顯然這個翻譯無法表達其本質,有少量的讀物上將這個詞翻譯為"增強",這個翻譯是對Advice較為準確的詮釋,我們通過AOP將橫切關注功能加到原有的業務邏輯上,這就是對原有業務邏輯的一種增強,這種增強可以是前置增強、後置增強、返回後增強、拋異常時增強和包圍型增強。

d. 引介(Introduction):引介是一種特殊的增強,它為類新增一些屬性和方法。這樣,即使一個業務類原本沒有實現某個介面,通過引介功能,可以動態的為該業務類新增介面的實現邏輯,讓業務類成為這個介面的實現類。 
e. 織入(Weaving):織入是將增強新增到目標類具體連線點上的過程,AOP有三種織入方式:①編譯期織入:需要特殊的Java編譯器(例如AspectJ的ajc);②裝載期織入:要求使用特殊的類載入器,在裝載類的時候對類進行增強;③執行時織入:在執行時為目標類生成代理實現增強。Spring採用了動態代理的方式實現了執行時織入,而AspectJ採用了編譯期織入和裝載期織入的方式。 
f. 切面(Aspect):切面是由切點和增強(引介)組成的,它包括了對橫切關注功能的定義,也包括了對連線點的定義。

補充:代理模式是GoF提出的23種設計模式中最為經典的模式之一,代理模式是物件的結構模式,它給某一個物件提供一個代理物件,並由代理物件控制對原物件的引用。簡單的說,代理物件可以完成比原物件更多的職責,當需要為原物件新增橫切關注功能時,就可以使用原物件的代理物件。我們在開啟Office系列的Word文件時,如果文件中有插圖,當文件剛載入時,文件中的插圖都只是一個虛框佔位符,等使用者真正翻到某頁要檢視該圖片時,才會真正載入這張圖,這其實就是對代理模式的使用,代替真正圖片的虛框就是一個虛擬代理;Hibernate的load方法也是返回一個虛擬代理物件,等使用者真正需要訪問物件的屬性時,才向資料庫發出SQL語句獲得真實物件。

下面用一個找槍手代考的例子演示代理模式的使用:


/**
 * 參考人員介面
 *
 */
public interface Candidate {
 
    /**
     * 答題
     */
    public void answerTheQuestions();
}
/**

* 懶學生

*

*/

public class LazyStudent implements Candidate {

private String name; // 姓名


public LazyStudent(String name) {

this.name = name;

}


@Override

public void answerTheQuestions() {

// 懶學生只能寫出自己的名字不會答題

System.out.println("姓名: " + name);

}


}


/**

* 槍手

*

*/

public class Gunman implements Candidate {

private Candidate target; // 被代理物件


public Gunman(Candidate target) {

this.target = target;

}


@Override

public void answerTheQuestions() {

// 槍手要寫上代考的學生的姓名

target.answerTheQuestions();

// 槍手要幫助懶學生答題並交卷

System.out.println("奮筆疾書正確答案");

System.out.println("交卷");

}


}
public class ProxyTest1 {


public static void main(String[] args) {

Candidate c = new Gunman(new LazyStudent("王小二"));

c.answerTheQuestions();

}

}

說明:從JDK 1.3開始,Java提供了動態代理技術,允許開發者在執行時建立介面的代理例項,主要包括Proxy類和InvocationHandler介面。下面的例子使用動態代理為ArrayList編寫一個代理,在新增和刪除元素時,在控制檯列印新增或刪除的元素以及ArrayList的大小:

  1. import java.lang.reflect.InvocationHandler;
    
    import java.lang.reflect.Method;
    
    import java.util.List;
    
    
    public class ListProxy<T> implements InvocationHandler {
    
    private List<T> target;
    
    
    public ListProxy(List<T> target) {
    
    this.target = target;
    
    }
    
    
    @Override
    
    public Object invoke(Object proxy, Method method, Object[] args)
    
    throws Throwable {
    
    Object retVal = null;
    
    System.out.println("[" + method.getName() + ": " + args[0] + "]");
    
    retVal = method.invoke(target, args);
    
    System.out.println("[size=" + target.size() + "]");
    
    return retVal;
    
    }
    
    
    }
    
    
    
    
    import java.lang.reflect.Proxy;
    
    import java.util.ArrayList;
    
    import java.util.List;
    
    
    public class ProxyTest2 {
    
    
    @SuppressWarnings("unchecked")
    
    public static void main(String[] args) {
    
    List<String> list = new ArrayList<String>();
    
    Class<?> clazz = list.getClass();
    
    ListProxy<String> myProxy = new ListProxy<String>(list);
    
    List<String> newList = (List<String>)
    
    Proxy.newProxyInstance(clazz.getClassLoader(),
    
    clazz.getInterfaces(), myProxy);
    
    newList.add("apple");
    
    newList.add("banana");
    
    newList.add("orange");
    
    newList.remove("banana");
    
    }
    

--------------------- 本文來自 Chimomo 的CSDN 部落格 ,全文地址請點選:https://blog.csdn.net/troubleshooter/article/details/78467637?utm_source=copy

說明:使用Java的動態代理有一個侷限性就是代理的類必須要實現介面,雖然面向介面程式設計是每個優秀的Java程式都知道的規則,但現實往往不盡如人意,對於沒有實現介面的類如何為其生成代理呢?繼承!繼承是最經典的擴充套件已有程式碼能力的手段,雖然繼承常常被初學者濫用,但繼承也常常被進階的程式設計師忽視。CGLib採用非常底層的位元組碼生成技術,通過為一個類建立子類來生成代理,它彌補了Java動態代理的不足,因此Spring中動態代理和CGLib都是建立代理的重要手段,對於實現了介面的類就用動態代理為其生成代理類,而沒有實現介面的類就用CGLib通過繼承的方式為其建立代理。 --------------------- 本文來自 Chimomo 的CSDN 部落格 ,全文地址請點選:https://blog.csdn.net/troubleshooter/article/details/78467637?utm_source=copy