前面講過,spring的生命周期為:實例化前奏-->實例化-->實例化后期-->初始化前期-->初始化-->初始化后期-->bean的具體調用-->銷毀前-->銷毀。那么,從裝配XML屬性到實例化bean的內部機制是怎樣的,沒有細說,今天我們來一起刨根問底。
還是老風格,以具體例子先入為主。下面是一個再簡單不過的spring框架的栗子。(XML,有。Bean,有。Spring容器,有。main函數,有。麻雀雖小,但是夠了。)
這是XML,簡單易懂,嘎嘣脆:
1 。。。。。。 2 3 <bean id="car" class="com.mesopotamia.test1.Car" 4 p:brand="寶馬X5" 5 p:maxSpeed="200"/> 6 </beans>
這是Bean,要個子有個子,要西一翁有西一翁:
代碼001
1 public class Car { 2 private String name; 3 private String brand; 4 private double maxSpeed; 5 public double getMaxSpeed() { 6 return maxSpeed; 7 } 8 public void setMaxSpeed(double maxSpeed) { 9 this.maxSpeed = maxSpeed; 10 } 11 12 13 private Log log=LogFactory.getLog(Car.class); 14 15 public Car(){ 16 name="寶馬"; 17 log.info("調用了Car的構造函數,實例化了Car,並把Car的name屬性設為:"+name); 18 } 19 public String getName() { 20 return name; 21 } 22 public void setName(String name) { 23 this.name = name; 24 } 25 public String getBrand() { 26 return brand; 27 } 28 public void setBrand(String brand) { 29 this.brand = brand; 30 } 31 32 33 public String toString(){ 34 return "名字"+name+" 型號"+" 速度:"+maxSpeed; 35 } 36 37 38 }
下面是啟動函數,精悍!干練:
代碼002
1 public class Main { 2 private static Log log=LogFactory.getLog(Main.class); 3 4 public static void main(String args[]){ 5 ApplicationContext ctx = new ClassPathXmlApplicationContext("com/mesopotamia/test1/*.xml"); 6 Car car1 = ctx.getBean("car",Car.class); 7 log.info(car1.toString()); 8 }
但是我要講的重點是main函數跑起來后的日志:
代碼003
1 2016-11-25 20:19:04,318 INFO [main] (AbstractApplicationContext.java:456) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@1ff5ea7: startup date [Mon Nov 16 20:19:04 CST 2015]; root of context hierarchy 2 2016-11-25 20:19:04,371 INFO [main] (XmlBeanDefinitionReader.java:315) - Loading XML bean definitions from file [C:\MySoftware\workspace\springtest\WebRoot\WEB-INF\classes\com\mesopotamia\test1\beans.xml] 3 2016-11-25 20:19:04,482 INFO [main] (DefaultListableBeanFactory.java:555) - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@6e293a: defining beans [car]; root of factory hierarchy 4 2016-11-25 20:19:04,483 INFO [main] (Car.java:22) - 調用了Car的構造函數,實例化了Car,並把Car的name屬性設為:寶馬 5 2016-11-25 20:19:04,533 INFO [main] (Main.java:15) - 名字寶馬 型號 速度:200.0
一開始就加載了AbstractApplicationContext里的方法,那么這個方法做了什么?實際上,第一行是由AbstractApplicationContext的refresh()方法打印出的,容器一啟動就要加載這個方法,讓我們來揭開refresh()的面紗吧。
首先,這個AbstractApplicationContext必須是ClassPathXmlApplicationContext的父類,否則代碼002的第5行怎么一跑起來會跑到AbstractApplicationContext里面的方法里去?孩子被打,當然是去叫爹咯。
在MyEclipse里,我們按住ctrl鍵,點擊ClassPathXmlApplicationContext一路點下去你就發現其中的繼承關系(姑且用—>表示子類指向被繼承的父類):
ClassPathXmlApplicationContext —> AbstractXmlApplicationContext —>AbstractRefreshableConfigApplicationContext —>AbstractRefreshableApplicationContext—>AbstractApplicationContext。
我們進入AbstractApplicationContext,看到refresh()方法的廬山真面目:
1 public void refresh() throws BeansException, IllegalStateException { 2 synchronized (this.startupShutdownMonitor) { 3 // Prepare this context for refreshing. 4 prepareRefresh(); 5 6 // Tell the subclass to refresh the internal bean factory. 7 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); 8 9 // Prepare the bean factory for use in this context. 10 prepareBeanFactory(beanFactory); 11 12 try { 13 // Allows post-processing of the bean factory in context subclasses. 14 postProcessBeanFactory(beanFactory); 15 16 // Invoke factory processors registered as beans in the context. 17 invokeBeanFactoryPostProcessors(beanFactory); 18 19 // Register bean processors that intercept bean creation. 20 registerBeanPostProcessors(beanFactory); 21 22 // Initialize message source for this context. 23 initMessageSource(); 24 25 // Initialize event multicaster for this context. 26 initApplicationEventMulticaster(); 27 28 // Initialize other special beans in specific context subclasses. 29 onRefresh(); 30 31 // Check for listener beans and register them. 32 registerListeners(); 33 34 // Instantiate all remaining (non-lazy-init) singletons. 35 finishBeanFactoryInitialization(beanFactory); 36 37 // Last step: publish corresponding event. 38 finishRefresh(); 39 } 40 41 catch (BeansException ex) { 42 // Destroy already created singletons to avoid dangling resources. 43 destroyBeans(); 44 45 // Reset 'active' flag. 46 cancelRefresh(ex); 47 48 // Propagate exception to caller. 49 throw ex; 50 } 51 } 52 }
人生若只如初見,看到這如此美麗的代碼是否驚呆了天真爛漫的你?OK,我知道你英語沒我好,我就大概講一下這refresh()里面都干了些什么,這些步驟實際上就是實例化之前的一系列美麗動作:
第9行:准備bean factory (BeanFactory是spring框架的基礎設施)
第16行:調用被注冊為bean的BeanFactoryPostProcessors(工廠后處理器) (工廠后處理器負責對實例化之前未成形的bean進行加工處理)
第19行:注冊BeanPostProcessors(Bean后處理器)來阻擋bean的創建。 (實例化后的bean需要用這個后處理器來進一步加工)
第22行:初始化消息源(國際化信息資源)。 (國際化很容易理解吧?比如微信可以切換中英文版本等等,后面會獨列篇幅講解)
第25行:初始化應用上下文事件廣播器。 (spring有一套完善的事件發布和監聽機制,事件廣播器負責把事件通知給監聽器,監聽器來執行事件。后面會獨列篇幅講解)。
第28行:初始化其他特殊的bean。
第31行:檢查是否有監聽器然后注冊監聽器(監聽器就跟bean一樣,需要放在注冊表中。后面會獨列篇幅講解)。
第34行:初始化所有單實例的bean(懶模式bean除外),單實例的bean初始化后把bean的引用放在spring容器的緩存中,調用者使用的是同一個額引用,任何一個調用者對bean的修改都會影響其他調用者。懶模式是指,spring容器啟動時不會初始化,而在需要用到該bean時才初始化。XML<bean>標簽中的lazy-init屬性就是設置懶模式或者勤快模式的,false是勤快模式,true是懶模式。
第37行:創建上下文刷新事件,發布廣播器。 (事件機制后面會獨列篇幅講解)
上面這些就是bean實例化前后的細枝末節了。那么上面的什么工廠后處理器,bean后處理器被調用后又是怎樣處理的呢?
下面我們來細化一下創建一個完整的bean的作業流程:
整體是下面這樣的:
簡單點:
讀取XML,轉化並加工成BeanDefinition,實例化BeanDefinition。
具體點:
ResourceLoader加載XML配置信息后,由BeanDefinitionReader讀取配置信息文件,把每個<bean>解析成BeanDefinition對象保存在注冊表中。容器首先掃描注冊表取出工廠后處理器,對注冊表中的BeanDefinition進行加工處理。Spring容器接着從注冊表中取出加工過的BeanDefinition開始着手bean實例化的事情。實例化時,首先由BeanWapper對bean進行封裝,配置屬性。最后利用注冊表中的Bean后處理器裝飾打扮,裝配出一個准備就緒的Bean來。(注冊表就類似於一個Map<K,V>,把所有的bean,不管是業務bean,還是spring自己的bean,都放到注冊表里,用的時候取出來)。
再具體點:
- ResourceLoader加載XML配置信息后,由BeanDefinitionReader讀取配置信息文件,把每個<bean>解析成BeanDefinition對象保存在BeanDefinitionRegistry注冊表中。這時的BeanDefinition可能只是個半成品,因為某些XML屬性配置里會有占位符變量,這些變量此時不會被解析出來,需要繼續優化,比如下面這樣:
1 <bean id="simpleBean" class="com.spring.ch04.SimplePostProcessor"> 2 <property name="connectionString" value="${simpleBean.connectionString}"/> 3 <property name="password" value="${simpleBean.password}"/> 4 <property name="username" value="${simpleBean.username}"/> 5 6 </bean>
- 因為有1的情況出現,所以容器首先掃描注冊表取出工廠后處理器,對注冊表中的BeanDefinition進行加工處理,把占位符替換成真正的值,產生成品的BeanDefinition。
- 通過反射機制掃描BeanDefinitionRegistry所有屬性編輯器的bean類,並把這些bean放到spring容器的屬性編輯器注冊表(PropertyEditorRegistry)中。(Spring的屬性編輯器負責將配置文件中的文本配置值轉換為Bean屬性的配置值,這個后面會獨辟章節來講)。
- Spring容器從BeanDefinitionRegistry中取出加工后的BeanDefinition,並調用InstantiationStrategy着手對bean的實例化工作。(這里的實例化只是相當於new了一個新對象一樣,也就是說,只是跑一個構造函數,不會具體的為屬性設置值,當然如果構造函數里寫了設置值的語句,那么也可以賦值。比如一開始的那個例子,實例化時在構造函數里就給Car的name屬性附上了"寶馬"的名字)。
- 實例化的過程中,spring容器使用BeanWrapper對bean進行封裝,BeanWrapper結合BeanDefinition和屬性編輯器注冊表中的屬性編輯器完成bean的屬性設置工作。
- 最后調用Bean后處理器對bean 作最后的潤色。
話音到此戛然而止。洗洗睡吧。
知者不惑,仁者不憂,勇者不懼。
----子曰