環境准備:
使用spring5.1.6版本
1 xml配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="user" class="com.hou.spring.bean.User"></bean> </beans>
2 測試類
public class BeanTest{ @Test public void beanTest(){ //spring4之后XmlBeanFactory被廢棄,改用以下方式 ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-bean.xml"); User user = (User) applicationContext.getBean("user"); System.out.println(user); } }
然后點進去源碼,跟着一步步debug來分析:
1 構造器調用:
ClassPathXmlApplicationContext的構造器中調用類同名方法:
點擊this跳轉到初始化方法:
2 super()方法是一直到父類AbstractApplicationContext中,將ApplicationContext的環境屬性設置給本類的環境屬性,包括一些profile,系統屬性等
3 setConfigLocations方法也是調用父類方法,將xml配置文件名字設置給父類的String數組屬性
4 refresh() 方法,所有的邏輯其實都在這個方法里面進行,主要分析這個方法:
5 prepareRefresh主要還是環境屬性的一些初始化,主要看第二步:
// Tell the subclass to refresh the internal bean factory. 告訴子類刷新內部bean工廠 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
點進去obtainFreshBeanFactory:
6 首先看refreshBeanFactory方法,注意,如果不知道是哪個子類的話,可以跟着debug斷點走:
主要分為這么幾個步驟:
一 首先判斷本類的DefaultListableBeanFactory屬性是否為null,如果不為null,就先清除一寫跟Bean有關的Map或者List等屬性集合
二 將BeanFactory設置為null,序列化id設置為null,
三 創建DefaultListableBeanFactory,這個類很重要,是springBean初始化的核心類,
四 對beanFactory進行設置,bean注冊等操作,最后將beanFactory賦值給本類的beanFactory屬性
7 customizeBeanFactory(beanFactory); 只做了兩件事:
8 loadBeanDefinitions: Bean的注冊主要是在這一步進行,下面進行分析,這個方法有5個子類實現:
我寫的測試類不是Web項目,所以會進入AbstractXmlApplicationContext這個類里的方法,如果是Web項目,會走XmlWebApplicationContext:
首先創建XmlBeanDefinitionReader:xml配置讀寫器然后設置環境屬性以及資源加載器為ClassPathXmlApplicationContext,這個加載器很重要,后面會用到
接着初始化讀取器: initBeanDefinitionReader,最后加載Bean
8 initBeanDefinitionReader
這個方法默認實現是空的,允許用戶自定義實現讀取器的定制化,需要實現接口,可以設置xml解析完成校驗,定制化解析器等
9 loadBeanDefinitions: 加載Bean信息,點進去:
這個方法主要是加載類的兩個資源屬性,Resource[] 和xml位置信息,主要看加載Xml的:
10 reader.loadBeanDefinitions(configLocations);
循環加載xml文件的Bean返回Bean總個數,查看加載方法:
11 查看這個load方法:
這里需要注意第八步設置的加載器,查看加載器的時序圖:
因為有繼承關系所以直接進if分支,繼續分析if分支代碼:
主要步驟:
1 獲取加載器中的Resource[] 數組
2 加載資源中的Bean,返回加載數量
12 查看loadBeanDefinitions,循環加載了所有的資源,返回總數
13 查看單個加載方法loadBeanDefinitions,主要看中間一段邏輯:
這里對正在解析的xml資源放入ThreadLocal中,保證只有本次線程可以訪問,加載完之后再移除
13 查看doLoadBeanDefinitions(inputSource, encodedResource.getResource());