1. 程式人生 > >手把手教你深入理解Spring原始碼-spring開篇(中)

手把手教你深入理解Spring原始碼-spring開篇(中)

授人以魚不如授人以漁,《手把手教你深入理解Spring原始碼》專欄教你如何學習、思考、閱讀Spring框架,並應對其它開源框架不再畏懼。

接著上篇的文章講,上篇的文章講述了什麼是IOC,這篇講述什麼又是AOP? 一樣的在看這篇文章之前,大家不妨先花點時間思考一下。

1、AOP的設計原理

1:在Spring中,AOP的實現有兩種方式,如果一個 Bean 是基於介面實現的,則Spring會採用JDK的動態代理,否則使用Cglib的動態代理

2:在Spring中,代理方法被呼叫時,會被多個“方法攔截器”攔截,方法攔截器的攔截分為兩層,外層是由Spring的核心實現,內層是使用者自定義的,攔截器鏈路的實現使用的是責任鏈模式

實現的。

2、什麼是AOP

AOP是一種程式設計思想,它的作用實際上就是兩個字"解耦",降低業務邏輯和功能模組(日誌統計、事務)之間的耦合性!做過專案的都知道,當一個專案複雜的時候,程式設計師所寫的業務邏輯是不可預估的。

比如我們要統計一個方法執行的時間,最簡單的方式應該是如下

 1、待統計的方法:

public void studyAop() {
        log.info("手把手教你深入理解Spring原始碼");
    }

2、實現統計方法執行時間

  static public void studyAop() {
        long start = System.currentTimeMillis();
        log.info("手把手教你深入理解Spring原始碼");
        long end = System.currentTimeMillis();
        log.info("method run time:" + (end - start));
    }

3、總結:以上程式碼確實實現了統計一個方法的執行時間,可是當用上面方法統計整個專案方法的執行時間時,衍生出來的程式碼是爆炸性的,那這種方法肯定不符合規範。於是jdk提供了api供程式設計師定製自己的AOP程式設計思想,Spring實現AOP時其中的一種就是採用jdk的動態代理。

下面看看什麼是jdk的動態代理

public class A implements OneInterface {

    private static final MicroLogUtil log = MicroLogFactory.getLogger();

    public void studyAop() {
        log.info("A Class 手把手教你深入理解Spring原始碼");
    }
    
}
public class B implements OneInterface {
    private static final MicroLogUtil log = MicroLogFactory.getLogger();
    
    @Override
    public void studyAop() {
        log.info("B Class 手把手教你深入理解Spring原始碼");
    }
}
public interface OneInterface {

    public void studyAop();
}

以上有兩個類,分別為 A,B 類,他們都實現了OneInterface介面,我們現在沒有在studyAop()方法中直接硬編碼,而是準備利用jdk的動態代理實現統計一個方法的執行時間。

public class TimeHandler implements InvocationHandler {
    //目標物件實現的介面
    private OneInterface target;

    private static final MicroLogUtil log = MicroLogFactory.getLogger();

    //封裝的獲取JDK動態代理生成的代理物件
    public Object getProxyBean(OneInterface target) {
        this.target = target;
        Class clazz = target.getClass();
        return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
    }

    @Override
    //重寫jdk 的invoke 方法
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long start = System.currentTimeMillis();
        //目標物件的具體實現
        target.studyAop();
        long end = System.currentTimeMillis();
        log.info("method run time:" + (end - start));
        return null;
    }
}

上面程式碼是自己寫了個TimeHandler類實現jdk動態代理的InvocationHandler介面,並重寫invoke方法,從而實現方法執行時間統計功能,注意我們現在統計方法執行時間的程式碼是現在invoke方法中。

重寫invoke方法後,通過呼叫jdk提供的,Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this)方法;jdk動態代理內部會進行一系列處理,從而生成一個代理物件並返回。

現在我們分別執行A,B類中的 studyAop()方法,測試統計執行時間功能是否實現

    public static void main(String args[]) {
        A a = new A();
        OneInterface aProxyBean = (OneInterface) new TimeHandler().getProxyBean(a);
        aProxyBean.studyAop();

        //        B b = new B();
//        OneInterface bProxyBean = (OneInterface) new TimeHandler().getProxyBean(b);
//        bProxyBean.studyAop();
    }

將A類作為被代理物件,傳入getProxyBean()方法中。在沒有改動A類中的待統計方法的程式碼實現了,執行時間統計功能

  public static void main(String args[]) {
//        A a = new A();
//        OneInterface aProxyBean = (OneInterface) new TimeHandler().getProxyBean(a);
//        aProxyBean.studyAop();

                B b = new B();
        OneInterface bProxyBean = (OneInterface) new TimeHandler().getProxyBean(b);
        bProxyBean.studyAop();
    }

將B類作為被代理物件,傳入getProxyBean()方法中。在沒有改動B類中的待統計方法的程式碼實現了,執行時間統計功能

從以上分析可以知道,被代理物件其實就是我們專案中寫的一些增刪查改業務邏輯,具體的執行程式碼寫在實現jdk動態代理提供的api:InvocationHandler介面後的,invoke方法中。通過呼叫jdk動態代理提供的api:Proxy.newProxyInstance()方法,內部生成一個代理物件。從而達到業務邏輯和功能模組的解耦並實現了統計方法執行時間的功能。

Spring的事務,使用者自定義的前置、後置通知,都可以通過jdk的動態代理實現。具體的原始碼分析將會在下面章節介紹。

大家對本篇文章感興趣的朋友可以加 Q群:837348118 一起討論原始碼,面試題,演算法,公司文化。