Spring理論基礎-面向切面程式設計

Yuicon發表於2018-07-10

AOPAspect-Oriented Programming的縮寫,中文翻譯是面向切面程式設計。作為Spring的特徵之一,是要好好學習的。

首先面向切面程式設計這個名稱很容易讓人想起物件導向程式設計(OOP)來。我們知道物件導向程式設計是從上往下的把系統拆分成若干個類,如Web專案中常見的ControllerServiceDao等分層。但是物件導向程式設計對於從左到右的水平抽象十分無力,類似於日誌、許可權等系統級功能的程式碼會重複的出現在任何地方。簡單的說,面向切面程式設計是對物件導向程式設計的一種補充,它可以把系統裡一些分散的重複程式碼集中在一起,抽象成一個切面。先來舉一個例子:

有一個內部使用的管理系統,Controller中的每個介面在呼叫時都要記錄下當前使用者名稱稱、當前時間等引數。

很明顯這個需求的實現程式碼在每個介面中都是重複的。即使提取成公共類的公共方法,在每個介面裡也都有一句該公共方法的呼叫,導致類之間的耦合。

介面流程圖

我們希望把原來介面中的記錄程式碼去除掉,使這些介面只關注自己的業務邏輯。然後使用預編譯或執行期動態代理等方式實現在不修改介面程式碼的情況下,為介面新增記錄功能。這種動態地將程式碼織入到類的指定方法、指定位置上的程式設計思想就是面向切面程式設計。

介面流程圖(新增AOP)

概念

面向切面程式設計具體的一些概念。

《EXPERT ONE ON ONE J2EE DEVELOPMENT WITHOUT EJB》第8章、《Spring實戰》第4章:

  • 增強(advice,另譯為通知,但《Spring3.x企業應用開發實戰》作者不贊成):在特定連線點執行的動作。例子中的實現程式碼就是增強。

  • 切點(pointcut):一組連線點的總稱,用於指定某個增強應該在何時被呼叫。例子中的指定哪些介面和指定位置就是切點。

  • 連線點(join point):在應用執行過程中能夠插入切面的一個點。例子中具體的一個介面和指定位置的結合就是連線點。

  • 切面(aspect):通知(即增強)和切點的結合。

AOPSpring中主要是通過動態代理實現的,具體實現也分為兩種:JDK動態代理CGLIB動態代理。其中JDK動態代理是基於介面代理,CGLIB動態代理是基於繼承代理。

JDK動態代理

作為Java自帶的一種動態代理技術,JDK動態代理的優勢是使用反射技術來生成代理類,在生成效率上比較高。缺點是隻能基於介面來動態代理,如果目標類沒有實現任何介面是不能使用JDK動態代理的。

來看一個用JDK動態代理的例子。

public class DynamicProxy implements InvocationHandler {

    /**
     * 被代理類
     */
    private Object target;

    public DynamicProxy(Object target) {
        this.target = target;
    }

    public static Object bind(Object target) {
        InvocationHandler invocationHandler = new DynamicProxy(target);
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), invocationHandler);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(method.getName() + " 方法執行前");
        //執行被代理類方法
        Object ret = method.invoke(target, args);
        System.out.println(method.getName() + " 方法執行後");
        return ret;
    }

    public static void main(String[] args) {
        Dao dao = new OrderDao();
        dao.doSomeThing();
        Dao daoProxy = (Dao) DynamicProxy.bind(dao);
        daoProxy.doSomeThing();
    }

}
複製程式碼
public class OrderDao implements Dao {

    @Override
    public void doSomeThing() {
        System.out.println("test");
    }

}
複製程式碼
public interface Dao {

    void doSomeThing();

}
複製程式碼
輸出:
test
doSomeThing 方法執行前
test
doSomeThing 方法執行後

Process finished with exit code 0
複製程式碼

後記

這次只是梳理下概念,把自己理解的AOP概念寫下來。也參考了很多文章,大體都是一致的細節上有些不同,把自己理解的寫出來給大家看下就知道有沒有錯了。順便整理一下,免得以後別人問起自己支支吾吾的不知道從哪說起。並沒有去了解Spring中具體的AOP實現,有興趣的同學可以自行深入瞭解下。

我的部落格地址

參考資料

什麼是面向切面程式設計AOP?

好書一起讀(115):重學Spring之面向切面

相關文章