Spring的IoC容器會以某種方式加載配置信息,然后根據這些信息綁定整個系統的對象,最終組裝成一個可用的基於輕量級容器的應用系統。實現以上功能,分為兩個階段:容器啟動階段和Bean實例化階段。而且Spring的IoC容器在每個階段都加入了相應的擴展點,以便根據具體場景的需要加入自定義的擴展邏輯。
1 容器啟動階段
首先會通過某種途徑加載配置信息,大部分情況下,容器需要依賴某些工具類(BeanDefinitionReader)對加載的配置信息進行解析和分析,並將分析后的信息編組為相應的BeanDefinition,最后把這些保存了bean定義必要信息的BeanDefinition,注冊到相應的BeanDifinitionRegistry,這樣容器啟動工作就完成了。
該階段所作工作是准備性的,重點更加側重於對象管理信息的收集,以及一些驗證性的和輔助性的工作。
2 Bean實例化階段
現在所有的bean定義信息都已經注冊到了BeanDefinitionRegistry中,當某個請求方通過容器的getBean方法明確地請求某個對象,或者因依賴關系容器需要隱式地調用getBean方法時,就會觸發bean實例化。
在這一階段,容器會先檢查所請求的對象之前是否已經初始化,如果沒有,會根據注冊的BeanDefinition所提供的信息實例化被請求對象,並為其注入依賴。如果該對象實現了某些回調接口,也會根據回調接口的要求來裝配它。當該對象裝配完畢之后,容器會立即將其返回請求方使用。
3 干預容器啟器
Spring提供了一種叫做BeanFactoryPostProcessor的容器擴展機制,允許在容器實例化對象之前,對注冊到容器的BeanDefinition所保存的信息做相應的修改。這就相當於在容器實現的第一階段最后加入一道工序,讓我們對最終的BeanDefinition做一些額外的操作,比如修改其中bean定義的某些屬性,為bean定義增加其他信息等。
可以通過兩種方式來應用BeanFactoryPostProcessor, 分別針對基本的IoC容器BeanFactory和較為先進的容器ApplicationContext.
對於BeanFactory來說,我們需要用手動方式應用所有的BeanFactoryPostProcessor:
ConfigurableListableBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("...")); PropertyPlaceholderConfigurer propertyPostProcessor = new PropertyPlaceholderConfigurer(); propertyPostProcessor.setLocation(new ClassPathResource("...")); propertyPostProcessor.postProcessBeanFactory(beanFactory);
對於ApplicationContext來說,它會自動識別配置文件中的BeanFactoryPostProcessor並應用它,所以僅需要在XML中將這些BeanFacotryPostProcessor簡單配置一下即可。
<beans> <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>conf/jdbc.properties</value> <value>conf/mail.properties</value> </list> </property> </bean> ... </beans>
Spring提供的幾個BeanFactoryPostProcessor
- PropertyPlaceholderConfigurer
PropertyPlaceholderConfigurer允許在XML配置文件中使用占位符,並將這些占位符所代表的資源單獨配置到簡單的properties文件中來加載。以數據源的配置為例:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="url"> <value>${jdbc.url}</value> </property> <property name="driverClassName"> <value>${jdbc.driver}</value> </property> <property name="username"> <value>${jdbc.username}</value> </property> <property name="password"> <value>${jdbc.password}</value> </property>
<property name="maxActive">
<value>100</value>
</property>
< /bean>
jdbc.properties文件如下:
jdbc.url=jdbc:mysql://server/MAIN?useUnicode=true&characterEncoding=ms932& failOverReadOnly=false&roundRobinLoadBalance=true jdbc.driver=com.mysql.jdbc.Driver jdbc.username=your username jdbc.password=your password
原理:
當BeanFactory在第一階段加載完成所有配置信息時,BeanFactory中保存的對象的屬性信息還只是以占位符的形式存在,如${jdbc.url}。當PropertyPlaceholderConfigurer作為BeanFactoryPostProcessor被應用時,它會使用properties配置文件中的配置信息來替換相應BeanDefinition中占位符表示的屬性值。這樣,當進入容器實現的第二個階段實例化bean時,bean定義中的屬性值就是最終替換完成后的了。
PropertyPlaceholderConfigurer不僅會從其配置的properties文件中加載配置項,同時還會檢查System類中的Properties. PropertyPlaceholderConfigurer提供了SYSTEM_PROPERTIES_MODE_FALLBACK/SYSTEM_PROPERTIES_MODE_NEVER/SYSTEM_PROPERTIES_MODE_OVERRIDE三種模式,默認采用FALLBACK,即如果properties文件中找不到相應配置項,則到System的properties中查找。
- PropertyOverrideConfigurer
PropertyOverrideConfigurer可以對容器中配置的任何你想處理的bean定義的property信息進行覆蓋替換。比如前一個例子中的dataSource,maxActive值為100, 如果想把這個值覆蓋掉,改成200,就可以在一個properties文件中配置:
dataSource.maxActive=200
所以如果要對容器中某些bean的property信息進行覆蓋,需要按照如下規則提供一個PropertyOverrideConfigurer使用的配置文件:
beanName.propertyName=value
也就是說,properties文件中的鍵是以XML中配置的bean定義的beanName為標志開始的(通常就是id指定的值),后面跟着相應被覆蓋的property的名稱。如下是PropertyOverridConfigurer在XML中的配置信息:
<bean class="org.springframework.beans.factory.config.PropertyOverrideConfigurer"> <property name="location" value="pool-adjustment.properties"/> </bean>
當容器中配置的多個PropertyOverrideConfigurer對同一個bean定義的同一個property值進行處理的時候,最后一個會生效。
4 Bean的生命周期
轉載自 http://www.jianshu.com/p/3944792a5fff
- ApplicationContext Bean生命周期
1. Bean的實例化
首先容器啟動后,會對scope為singleton且非懶加載的bean進行實例化。
容器在內部實現的時候,采用“策略模式”來決定采用何種方式初始化bean實例。通常,可以通過反射或者CGLIB動態字節碼生成來初始化相應的bean實例或者動態生成其子類。默認情況下,容器內部采用CglibSubclassingInstantiationStartegy。容器只要根據相應bean定義的BeanDefinition取得實例化信息,結合CglibSubclassingInstantiationStartegy以及不同的bean定義類型,就可以返回實例化完成的對象實例。但不是直接返回構造完成的對象實例,而是以BeanWrapper對構造完成的對象實例進行包裹,返回相應的BeanWrapper實例。這個BeanWrapper的實現類BeanWrapperImpl是對某個bean進行包裹,然后對包裹后的bean進行操作,比如設置或獲取bean的相應屬性值。
2. 設置對象屬性
BeanWrapper繼承了PropertyAccessor接口,可以以同一的方式對對象屬性進行訪問,同時又繼承了PropertyEditorRegistry和TypeConverter接口,然后BeanWrapper就可以很方便地對bean注入屬性了。
3. 如果Bean實現了BeanNameAware接口,會回調該接口的setBeanName()方法,傳入該bean的id,此時該Bean就獲得了自己在配置文件中的id。
4. 如果Bean實現了BeanFactoryAware接口,會回調該接口的setBeanFactory()方法,傳入該Bean的BeanFactory,這樣該Bean就獲得了自己所在的BeanFactory.
5. 如果Bean實現了ApplicationContextAware接口,會回調該接口的setApplicationContext()方法,傳入該Bean的ApplicationContext, 這樣該Bean就獲得了自己所在的ApplicationContext.
6. 如果有一個Bean實現了BeanPostProcessor接口,並將該接口配置到配置文件中,則會調用該接口的postProcessBeforeInitialization()方法。
7.如果Bean實現了InitializingBean接口,則會回調該接口的afterPropertiesSet()方法。
8. 如果Bean配置了init-method方法,則會執行init-method配置的方法。
9. 如果有一個Bean實現了BeanPostProcessor接口,並將該接口配置到配置文件中,則會調用該接口的postProcessAfterInitialization方法。
10.經過9之后,就可以正式使用該Bean了,對於scope為singleton的Bean, Spring IoC容器會緩存一份該Bean的實例,而對於scope為prototype的Bean, 每次被調用都回new一個對象,而且生命周期也交給調用方管理了,不再是Spring容器進行管理了。
11. 容器關閉后,如果Bean實現了DisposableBean接口,則會調用該接口的destroy()方法。
12. 如果Bean配置了destroy-method方法,則會執行destroy-method配置的方法,至此,整個Bean生命周期結束。
示例
我們定義了一個Person類,該類實現了BeanNameAware,BeanFactoryAware,ApplicationContextAware,InitializingBean,DisposableBean五個接口,並且在applicationContext.xml文件中配置了該Bean的id為person1,並且配置了init-method和destroy-method,為該Bean配置了屬性name為jack的值,然后定義了一個MyBeanPostProcessor方法,該方法實現了BeanPostProcessor接口,且在applicationContext.xml文件中配置了該方法的Bean
Person.class
package com.ivy.beanlifecycle; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; public class Person implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean, DisposableBean{ private String name; public Person() { System.out.println("Person constructor"); } public String getName() { return name; } public void setName(String name) { this.name = name; System.out.println("setter() invoked"); } public void myInit() { System.out.println("myInit() invoked"); } public void myDestroy() { System.out.println("myDestroy() invoked"); } @Override public void setBeanName(String beanName) { System.out.println("setBeanName() invoked, beanName : " + beanName); } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { // TODO Auto-generated method stub System.out.println("setBeanFactory() invoked, beanFactory : " + beanFactory); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { System.out.println("setApplicationContext() invoked"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("afterPropertiesSet() invoked"); } @Override public void destroy() throws Exception { System.out.println("destroy() invoked"); } public String toString() { return "Person[name=" + name +"]"; } }
MyBeanPostProcessor.class
package com.ivy.beanlifecycle; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; public class MyBeanPostProcessor implements BeanPostProcessor{ @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { // TODO Auto-generated method stub System.out.println("postProcessAfterInitialization() invoked, beanName : " + beanName); return bean; } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("postProcessBeforeInitialization() invoked, beanName : " + beanName); return bean; } }
applicationContext.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="person1" class="com.ivy.beanlifecycle.Person" init-method="myInit" destroy-method="myDestroy"> <property name="name" value="ivy"></property> </bean> <bean id="myPostProcessor" class="com.ivy.beanlifecycle.MyBeanPostProcessor"></bean> </beans>
PersonServiceApplicationContextTest.class
package com.ivy.beanlifecycle; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class PersonServiceApplicationContextTest{ public static void main(String[] args) { // TODO Auto-generated method stub System.out.println("start init ioc container"); ApplicationContext aContext = new ClassPathXmlApplicationContext("applicationContext.xml"); System.out.println("end loading xml"); Person person = (Person)aContext.getBean("person1"); System.out.println(person); System.out.println("close container"); ((ClassPathXmlApplicationContext)aContext).close(); } }
運行結果:
start init ioc container log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment). log4j:WARN Please initialize the log4j system properly. Person constructor setter() invoked setBeanName() invoked, beanName : person1 setBeanFactory() invoked, beanFactory : org.springframework.beans.factory.support.DefaultListableBeanFactory@5fa12d: defining beans [person1,myPostProcessor]; root of factory hierarchy setApplicationContext() invoked postProcessBeforeInitialization() invoked, beanName : person1 afterPropertiesSet() invoked myInit() invoked postProcessAfterInitialization() invoked, beanName : person1 end loading xml Person[name=ivy] close container destroy() invoked myDestroy() invoked
可以看出,在加載xml的時候ApplicationContext就實例化了所有的bean
- BeanFactory Bean生命周期
BeanFactoty容器中, Bean的生命周期如上圖所示,與ApplicationContext相比,有如下幾點不同:
1. BeanFactory容器中,不會調用ApplicationContextAware接口的setApplicationContext()方法
2. BeanPostProcessor接口的postProcessBeforeInitialization方法和postProcessAfterInitialization方法不會自動調用,必須自己通過代碼手動注冊
3. BeanFactory容器啟動的時候,不會去實例化所有bean,包括所有scope為singleton且非延遲加載的bean也是一樣,而是在調用的時候去實例化。
還是以上邊Person為示例
PersonServiceBeanFactoryTest.class
package com.ivy.beanlifecycle; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; public class PersonServiceBeanFactoryTest { public static void main(String[] args) { // TODO Auto-generated method stub System.out.println("start init ioc container"); ConfigurableListableBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml")); System.out.println("end loading xml"); beanFactory.addBeanPostProcessor(new MyBeanPostProcessor()); Person person = (Person)beanFactory.getBean("person1"); System.out.println(person); System.out.println("close container"); beanFactory.destroySingletons(); } }
運行結果:
start init ioc container log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment). log4j:WARN Please initialize the log4j system properly. end loading xml Person constructor setter() invoked setBeanName() invoked, beanName : person1 setBeanFactory() invoked, beanFactory : org.springframework.beans.factory.xml.XmlBeanFactory@ccd65d: defining beans [person1,myPostProcessor]; root of factory hierarchy postProcessBeforeInitialization() invoked, beanName : person1 afterPropertiesSet() invoked myInit() invoked postProcessAfterInitialization() invoked, beanName : person1 Person[name=ivy] close container destroy() invoked
可以看出,end loading xml之后才實例化的person。