細讀Spring源碼(一)---refresh()方法概覽


      看了一星期的Spring源碼,把refresh()方法從頭至尾梳理了一遍,在看的過程中想記錄一些關鍵點,但是需要記錄的東西太多,有種無從下手的感覺。因為我在看源碼的過程中遇到了很多的疑惑,這些疑惑有時候是一個零散的點,比如動態代理(jdk動態代理和cglib動態代理)、設計模式,有時候是一個很長的鏈,比如一個完整的bean的創建過程,即bean的生命周期,有時候又是一個很廣的面,比如IOC和AOP的原理,發現、思考和解決種種疑惑的過程中,我對Spring有了一個全新的認知。說實話,讓我感覺以前的開發真的只是停留在了“用”的層面,因為Spring它真的強大到95%的開發中都不需要開發人員知道它的設計原理,只要進行黑盒開發即可,在看源碼的過程中就會發現平時自己一個小小的舉動,在源碼中都可以追溯到它的前世今生,真的會感知到並驚嘆程序設計之美!在寫之前我糾結過一些問題,是先補充一些看源碼的前置知識點,還是在遇到需要知道的前置知識時再補充,這就有點像單例模式的餓漢式和懶漢式了,我糾結的點在於:如果用餓漢式,就很難舉例子說明其在spring中的應用,比如設計模式;如果用懶漢式,又顯得邏輯跳來跳去,容易讓思想陷入死循環,就像看源碼時看一個方法的實現,如果對每一個方法的所有細節都刨根問底,見到方法就點進去,看着看着就會陷入無限循環中,從而失去最初的興趣,鑒於這兩種糾結,我決定將兩種方式結合起來,出一個滲透spring源碼的系列博客,先梳理整體情況,再說明前置知識點,最后跟源碼,在源碼解析中用到的知識點,以鏈接的形式加入,在前置知識點中可能提及的源碼,也以鏈接的形式加入,而不是在當前文章中另起段落,這就有點像循環引用了,哈哈~

    本系列博客的規划如下:

閱讀Spring的源碼,可以通過下面的方式開啟調試:

1  public static void main(String[] args) {
2         ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring.xml");
3         UserVo userVo = context.getBean(UserVo.class);
4         EnterpriseVo enterpriseVo = context.getBean(EnterpriseVo.class);
5         System.out.println("userVo=" + userVo);
6         System.out.println("enterpriseVo=" + enterpriseVo);
7         context.close();
8     }

主要是上面的第2行開始,進入該方法會進入下面的方法:

1    public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
2         super(parent);
3         this.setConfigLocations(configLocations);
4         if (refresh) {
5             this.refresh();
6         }
7 
8     }

第3行是設置配置文件,即為傳遞的classpath:spring.xml

第5行就是今天要說的refresh()方法,這是spring容器啟動的入口所在,因為該方法中總共有13個子方法,所以今天只是看一下概覽,后續對每個方法進行跟進

 

 通過一張思維導圖說明每個方法主要完成的事情:

下面是源碼中的添加的注釋:

 1 public void refresh() throws BeansException, IllegalStateException {
 2         synchronized (this.startupShutdownMonitor) {
 3             StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
 4 
 5             // Prepare this context for refreshing.
 6             /*准備要刷新的上下文:
 7             設置啟動日期和激活標志,以便執行任意屬性來源的初始化
 8             初始化上下文環境中的占位符屬性來演
 9             獲取環境信息並校驗必傳參數
10             准備早期的應用程序監聽器
11             准備早期應用監聽事件,一旦多播器可用就將早期的應用事件發布到多播器中*/
12             prepareRefresh();
13 
14             // Tell the subclass to refresh the internal bean factory.
15             //讓子類刷內置的bean工廠,返回的是ConfigurableListableBeanFactory的子類對象DefaultListableBeanFactory
16             //注意:BeanFactory和ApplicationContext的區別:前者在加載文件時不創建對象,后者在加載文件時就創建好bean對象
17             ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
18 
19             // Prepare the bean factory for use in this context.
20             //准備在上下文中使用的bean工廠
21             prepareBeanFactory(beanFactory);
22 
23             try {
24                 // Allows post-processing of the bean factory in context subclasses.
25                 postProcessBeanFactory(beanFactory);
26 
27                 StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
28                 // Invoke factory processors registered as beans in the context.
29                 //開始調用BeanFactory的后置處理器
30                 invokeBeanFactoryPostProcessors(beanFactory);
31 
32                 // Register bean processors that intercept bean creation.
33                 //注冊bean的后置處理器
34                 registerBeanPostProcessors(beanFactory);
35                 //后置處理器結束
36                 beanPostProcess.end();
37 
38                 // Initialize message source for this context.
39                 //國際化處理,為上下文初始化Message源,即不同語語言的消息體
40                 initMessageSource();
41 
42                 // Initialize event multicaster for this context.
43                 //初始化上下文的事件廣播器
44                 initApplicationEventMulticaster();
45 
46                 /*Initialize other special beans in specific context subclasses.
47                 能夠被覆蓋的模板方法,用來添加特定上下文的更新工作,在特殊bean進行初始化或者單例bean進行實例化時被調用,在該類中是一個空實現
48                 三個子類中都是調用UiApplicationContextUtils.initThemeSource(this)方法*/
49                 onRefresh();
50 
51                 // Check for listener beans and register them.
52                 //在所有注冊的bean中查找Listener bean,注冊到消息廣播器中,即向監聽器發布事件
53                 registerListeners();
54                 //-----------------------------------------正餐開始,前方高能預警-------------------------------------------
55                 // Instantiate all remaining (non-lazy-init) singletons.
56                 //對非延遲初始化的單例進行實例化,一般情況下的單例都會在這里就實例化了,這樣的好處是,在程序啟動過程中就可以及時發現問題
57                 finishBeanFactoryInitialization(beanFactory);
58 
59                 // Last step: publish corresponding event.
60                 //最后一步:完成刷新過程,通知生命周期處理器lifecycleProcessor刷新過程
61                 finishRefresh();
62             } catch (BeansException ex) {
63                 if (logger.isWarnEnabled()) {
64                     logger.warn("Exception encountered during context initialization - " +
65                             "cancelling refresh attempt: " + ex);
66                 }
67 
68                 // Destroy already created singletons to avoid dangling resources.
69                 //當發生異常時銷毀已經創建的單例
70                 destroyBeans();
71 
72                 // Reset 'active' flag.
73                 //重置active標識為false
74                 cancelRefresh(ex);
75 
76                 // Propagate exception to caller.
77                 throw ex;
78             } finally {
79                 // Reset common introspection caches in Spring's core, since we
80                 // might not ever need metadata for singleton beans anymore...
81                 //清空所有的緩存,因為單例bean是在容器啟動時初始化完畢,所以不需要保留它們的元數據信息
82                 resetCommonCaches();
83                 contextRefresh.end();
84             }
85         }
86     }

下一篇:細讀Spring源碼(二)---關於Spring中用到的設計模式

持續更新中........


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM