1. 程式人生 > >【學習底層原理系列】重讀spring原始碼1-建立基本的認知模型

【學習底層原理系列】重讀spring原始碼1-建立基本的認知模型

開篇閒扯

在工作中,相信很多人都有這種體會,與其修改別人程式碼,寧願自己重寫。

為什麼?

先說為什麼願意自己寫:

從0-1的過程,是建立在自己已有認知基礎上,去用自己熟悉的方式構建一件作品。也就是說,

1.對目標的認知是熟悉的(當然每個人水平可能不一樣,也有可能是錯的,這不重要,重要的是自認為是符合的);

2.使用的工具是自己熟悉的。

接下來就是去做一件自己熟悉的領域的事而已。

再說為什麼不願意修改別人程式碼:

1.首先要在閱讀程式碼過程中,去不斷的檢視對方的實現是否符合自己的認知,很可能由於雙方認識程度不同,導致一開始對目標的認識就不一致。

2.實現方式是自己不熟悉的,要不斷的去適應對方的風格,要不斷的去猜測對方的用意,還要記憶大量的內容。

3.人都有趨利避害的心裡,對於自己不熟悉的東西進行閱讀,是有讀錯的風險的,如果再修改,那風險就更大了。對這種風險的規避,是來自骨子裡的。

在猿界,閱讀原始碼的經驗是非常被看重的

你很多人工作了很多年,卻總是不能沉下心來讀一讀

這裡扯了這麼多,算是一種讓自己心安的解釋吧

下面開始正文,嘗試換一種風格來解析spring原始碼。因為看了網上很多人寫的文章,就是粘了一堆程式碼,簡單的做了解釋,個人認為這並不具有可操作性,與其這樣看隻言片語的程式碼,還不如直接看原始碼來的完整。

 Spring臨門一腳

我們知道,spring的兩大核心功能是IOC和AOP。其中IOC解決了我們用的例項的建立問題,AOP解決的是對方法的擴充套件問題。不管是出於什麼考慮,初衷都是為了減少程式碼編寫,減少在非業務開發之外的精力。

今天我們先來學習IOC:

依賴注入,或者叫控制反轉。不熟悉的可以自行查一下,無非概念而已。

Java是面向物件的語言,在沒有Spring之前,甚至於現在我們在開發過程中,需要用到某物件了,我們是這麼來做的:

MyObject obj=new MyObject();
...

obj.methodName();

...  

於是你會發現,到處都充斥著這種例項初始化的程式碼,可能在類變數裡,也可能在方法的區域性變數裡。於是有人就想了,我能不能把這些變數統一管理起來呢?比如統一放在類變數裡,可以在當前類例項化時在構造方法裡統一例項化,也可以在宣告時就例項化。比如這樣:

public class MySuperClass1{
  MySubClassA subA=new MySubClassA(); 
  MySubClassB subB=new MySubClassB(); 
  MySuperClass2 super2=new MySuperClass2();
  ...
public void super1Methord(){
   super2.super2Methord(this.subA);
 } 
}

 

嗯,看起來好了很多,這樣在MySuperClass1類中,無論有多少個方法用到了那兩個sub類的例項,都不用再自己例項化了。

那麼問題來了,如果需要在方法super1Methord()中呼叫另一個類MySuperClass2的方法super2Methord()【如程式碼所示】,而這個方法也用到了MySubClassA的例項,怎麼辦?聰明的你肯定想到了,把物件作為引數傳遞進去,就如程式碼中一樣。

那麼問題又來了,假如在第三個類MySuperClass3中,存在著和MySuperClass1一樣的情況,那麼該怎麼辦呢?是不是還要自己建立物件,然後傳遞進去?這樣不就是重複建立嗎?那怎麼辦才能更好一些呢?

可能你會想到,我弄一個根類,所有類都繼承自這個類,在這個根類裡實例化好所有物件,然後就不用重複建立了。

是的,思路是對的,只是,這就需要自己來維護這些類,如果新增了,就要時刻記得去根類中新增一下,如果不需要了,要記得去根類中刪除下,專案小還好,專案大了,誰還記得哪個有用哪個沒用?最後這個根類,就誰都不敢輕易改。那有沒有什麼好的方式可以解決呢?比如我配置下,或者加個註解,這個根類就能自動識別我新加的類,就能給自動的例項化?

springIOC,就做了這件事。它提供了容器,也就是我們說的根類,我們在使用的時候,就可以通過名字或其他方式,從容器中拿到事先建立好的例項物件。

ApplicationContext ctx = new ClassPathXmlApplicationContext("aop-test.xml");

ITestBean testBean = (ITestBean) ((ClassPathXmlApplicationContext) ctx).getBean("testBean");

String str = testBean.getStr();

對應到原始碼裡,容器就是各種xxxApplicationContext,例如上面程式碼中的ClassPathXmlApplicationContext。

我覺得以上是一定要理解清晰的知識點。知道了what,再帶著疑問和目標去了解How,會事半功倍。

下面開始分析原始碼,在分析原始碼過程中,我會從繁雜的程式碼中把主流程梳理出來,嘗試去掉細枝末節,儘量保證思路的連貫性。

ApplicationContext ctx = new ClassPathXmlApplicationContext("aop-test.xml");

這就是初始化容器,我們跟進去,發現在其建構函式中,有一個核心方法是需要我們關注的:refresh()

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
            throws BeansException {

        super(parent);
        setConfigLocations(configLocations);
        if (refresh) {
            refresh();
        }
    }

為什麼這裡叫重新整理呢?不是應該叫建立、構造之類的嗎?

在介面註釋裡有一句話:

As this is a startup method, it should destroy already created singletons

作為一個啟動方法,它需要銷燬已建立的單例。

銷燬後然後再建立。這不就是重新整理的意思嗎。

繼續跟進去:

refresh()方法裡,最核心的12個方法,共同支撐起Spring的架子。

public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // 1 重新整理前的預處理:
            prepareRefresh();

            // 2 【建立bean】生成BeanFactory,並載入beanDefinition
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // 3 對BeanFactory進行預處理:
            prepareBeanFactory(beanFactory);

            try {
                //4 空實現,暫時忽略
                postProcessBeanFactory(beanFactory);

                // 5 【擴充套件點】新增BeanFactory的後置處理器BeanFactoryPostProcessor並執行之。用於在bean例項化之前,讀取beanDefinition並進行修改。
                invokeBeanFactoryPostProcessors(beanFactory);

                // 6 【擴充套件點】:新增BeanPostProcessor,注意區別於上述的BeanFactoryPostProcessor,這裡只是新增,在第11步中,bean例項化時執行初始化方法之前後,可對bean進行修改
                registerBeanPostProcessors(beanFactory);

                // 7 初始化MessageSource元件,做國際化功能:訊息繫結,訊息解析
                initMessageSource();

                // 8 【事件監聽】初始化事件處理器
                initApplicationEventMulticaster();

                // 9 在這裡是空實現
                onRefresh();

                // 10 【事件監聽】註冊監聽器,就是實現了ApplicationListener介面的bean,和上面的8搭配使用
                registerListeners();

                // 11 【建立bean】初始化非懶載入的單例bean,兩個作用
          // .真正的例項化bean
          // .實現Aop,建立代理bean finishBeanFactoryInitialization(beanFactory); // 12 【事件監聽】完成context的重新整理,釋出ContextRefereshedEvent事件 finishRefresh(); } catch (BeansException ex)// 忽略 } finally { // 忽略 } } }

其實歸納下,需要重點關注的就分為以下三類:

1.【建立bean】:2和11

2.【擴充套件點】:處理器註冊和執行,包括BeanFactoryPostProcessor和BeanPostProcessor:4,5,6,11

3.【事件監聽】:8,10,12

好,第一篇就先建立基本的印象。

&n