1. 程式人生 > >tiny-spring學習記錄

tiny-spring學習記錄

最近想深入研究一下spring的內部實現原理,但是spring的原始碼有很多功能,如果直接看很可能會深入到各種功能邏輯無法自拔,剛剛開始看可以只關注spring的主幹,其他枝節可以先不加考慮,碰巧發現了一個很好的專案,也就是tiny-spring。
之後會畫出類圖和sequence圖,幫助理清邏輯。

1.step-1-container-register-and-get

定義BeanFactory用於註冊與獲取bean,
所有的bean使用BeanDefinition進行定義

step-2-abstract-beanfactory-and-do-bean-initilizing-in-it

基於step-1進一步進行改進,對BeanFactory進行分層 Interface-abstractclass-class
明確規定BeanFactory的功能,增加拓展性。
BeanDefinition擴充套件設定bean全類名方法,跟step1比較更加貼進BeanDefinition的作用,
Step1中直接將bean的例項傳入BeanDefinition中,這樣不是很好,如果我們想要在bean例項化的時候統一的做一些事情的話這樣肯定不行。
而在step2中則把例項化放到了BeanFactory中去,
這樣各自的分工更加明確,BeanDefinition只負責bean的定義,而註冊和例項化在BeanFactory中統一進行。

step-3-inject-bean-with-property

增加屬性注入功能,同樣屬性定義通過封裝放在BeanDefinition中,
屬性的實際注入工作交給BeanFactory完成。

step-4-config-beanfactory-with-xml

把bean的定義放到xml中,讀取xml到BeanDefinition的map中。
主要就是xml檔案的解析和資料的填充,在AbstractBeanDefinitionReader
中表現的很清楚,一個是registry即是要解析成的資料,一個是resourceLoader
資料來源的載入器,具體中間怎麼解析資料不需要太關心。

step-5-inject-bean-to-bean

在這一步新建了一個BeanReference用於標記引用型別並且存放ref的name,
剛開始很奇怪為什麼要這個BeanReference類,直接new一個BeanDefinition給PropertyValue的value屬性不就行了麼?
實現bean的載入辦法:懶載入和預載入兩種。
問題類的迴圈依賴如何解決?
在AutowireCapableBeanFactory的doCreateBean中先將Bean的例項new出來,再把property set進去就不會出現所謂迴圈依賴的問題。

step-6-invite-application-context

把Bean的讀取,向BeanFactory註冊功能封裝到ApplicationContext中去,只要傳入配置檔案位置就可以直接從ClassPathXmlApplicationContext拿到bean。

到這裡應該有個總結:

前六步其實也就做了一件事情那就是製作一個最簡化的IOC容器,它的功能是

  1. Bean的載入(xml)與初始化,
  2. 實現依賴注入(未實現註解支援,跟普通屬性注入其實差不多),
  3. 把IOC容器封裝成一個便於操作的ApplicationContext物件。

step-7-method-interceptor-by-jdk-dynamic-proxy

實現aop需要用到的包

<dependency>
    <groupId>aopalliance</groupId>
    <artifactId>aopalliance</artifactId>
    <version>1.0</version>
</dependency>

這個包定義了一系列的aop介面規範。
切面的實現,具體見類圖
tiny-spring-step7

  1. TargetSource只是對目標物件的資訊進行了封裝,方便使用。
  2. AdvisedSupport封裝了切面和切入點,切面由MethodInterceptor定義,切入點由TargetSource定義,這裡需要注意我們通常在spring裡邊的切入點可以精確到某個方法,這裡只實現了精確到某個目標物件,即這個目標物件裡的所有方法。
  3. JdkDynamicAopProxy代理封裝。談到動態代理就不得不提一個類和一個介面,即Proxy和InvocationHandler。網上有很多文章講解動態代理,而且篇幅不短很容易把人搞暈。其實簡單點可以理解為動態代理的實現都是在Proxy類裡邊完成的,這個類把我們要執行的方法,目標物件,方法引數這三個要素抽離出來備用。而InvocationHandler唯一的介面方法invoke正是把Proxy準備好的這三個要素當作引數傳入,最終我們可以在invoke方法中使用反射的方式完成方法呼叫返回結果。(使用jdk動態代理需要注意:Proxy生成代理類的輸入引數有三個:classLoader/interfaces/invocationHandler,值得注意的是interfaces這個引數,這個引數是被代理類所有實現的介面,也就是說jdk動態代理要求被代理物件必須實現介面,而且在代理類生成以後轉型時也必須轉型成接口才可以,假如你的被代理類實現了兩個介面,這兩個接口裡分別有一個方法,在生成代理後要分別轉型成對應的接口才能呼叫到相應的方法。jdk動態代理的不足也就是在這裡,它要求被代理類必須要繼承介面,而如果要直接要對類進行代理可以用cglib(下面會講到))
  4. 還有就是為什麼要用到MethodInterceptor,MethodInvocation這些介面呢?看起來很複雜的說。
    MethodInvocation用來包裝方法反射所用到的所有元素,即目標物件,方法,引數。在proceed方法中進行反射得到結果。
    MethodInterceptor顧名思義方法的攔截器,就是在方法執行前後執行一些定製的程式碼。它的介面方法invoke()以MethodInvocation作為引數。
    MethodInvocation,MethodInterceptor等等這一系列的介面組成了aop的協議。

step-8-invite-pointcut-and-aspectj

引入aspectj包,實現根據切面表示式匹配類和方法。

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.6.11</version>
</dependency>

這是eclipse的專案,官網地址&Getting Start

類圖
step-8-invite-pointcut-and-aspectj

這一步做的事就是解析與匹配,可以看測試類AspectJExpressionPointcutTest。

step-9-auto-create-aop-proxy

這一步做的事情是在springContext初始化的時候,找一個合適的時機生成aop代理。
1. 時機:
如seq圖
step-9-seq
在例項化bean後,初始化bean時把bean替換為代理類。
2. 生成代理這一步中最主要的類就是
AspectJAwareAdvisorAutoProxyCreator
類圖
step-9-class
實現BeanPostProcessor,在postProcessAfterInitialization方法中完成代理。
實現BeanFactoryAware,獲取BeanFactory例項,也就是相當於springContext,使用BeanFactory獲取所有切面,對每個bean進行匹配,如果匹配就使用JdkDynamicAopProxy進行代理。
做個比喻,每個切面就行機場安檢,有很多層,每一個bean就像是乘客。在上飛機之前乘客需要依次過層層安檢,如果安檢發現乘客身上有匹配項,就對乘客進行相應處理,最終上飛機的乘客可能已經和安檢之前不同了(哈哈,想想就怕)。

step-10-invite-cglib-and-aopproxy-factory

引入cglib

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib-nodep</artifactId>
    <version>2.1_3</version>
</dependency>

cglib使用

    public interface A{
        void a();
    }
    public interface B{
        void b();
    }
    public static class C implements A,B{

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

        @Override
        public void b() {
            System.out.println("b");
        }
    }
    public static class MyHandler implements net.sf.cglib.proxy.InvocationHandler{
        private Object o;
        public MyHandler(Object o){
            this.o=o;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("cglib before");
            Object invoke = method.invoke(o, args);
            System.out.println("cglib after");
            return invoke;
        }
    }
    @Test
    public void test(){
        Enhancer enhancer = new Enhancer();
        //提供了針對介面和類的代理,使用一種即可
        enhancer.setSuperclass(C.class);
        enhancer.setInterfaces(C.class.getInterfaces());

        MyHandler myHandler = new MyHandler(new C());
        enhancer.setCallback(myHandler);
        C c = (C)enhancer.create();
        c.b();
        c.a();
    }

為什麼要使用cglib

這是我們的代理物件決定的,aop所要代理的類通常都沒有繼承介面,所以就要使用能夠直接對類進行代理的cglib。

ProxyFactory

ProxyFactory
ProxyFactory裡邊封裝了所有aop相關的代理所需要的功能和原料