Spring Bean 的生命週期
目錄
五、ApplicationContext的Bean生命週期程式碼演示
Spring的生命週期是指例項化Bean時所經歷的一系列階段,即通過getBean()獲取bean物件及設定物件屬性時,Spring框架做了哪些事。Bean的生命週期從Spring容器例項化Bean到銷燬Bean。
本文分別對 BeanFactory 和 ApplicationContext 中的生命週期進行分析。
一、BeanFactory例項化Bean相關介面
Bean級生命週期介面:(4個)
1、BeanNameAware
//待物件例項化並設定屬性之後呼叫該方法設定BeanName
void setBeanName(String beanName);
2、BeanFactoryAware
//待呼叫setBeanName之後呼叫該方法設定BeanFactory,BeanFactory物件預設實現類是DefaultListableBeanFactory
void setBeanFactory(BeanFactory var1) throws BeansException;
3、InitializingBean
//例項化完成之後呼叫(呼叫了BeanPostProcessor.postProcessBeforeInitialization方法之後呼叫該方法)
void afterPropertiesSet() throws Exception;
4、DisposableBean
//關閉容器時呼叫
void destroy() throws Exception;
這4個介面都在包 org.springframework.beans.factory 下,它們是Bean級生命週期介面,這些介面由Bean類直接實現。
容器級Bean生命週期介面:(2個)
1、抽象類:InstantiationAwareBeanPostProcessorAdapter
例項化前/後,及框架設定Bean屬性時呼叫該介面。可覆蓋的常用方法有:
//在Bean物件例項化前呼叫
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException;
//在Bean物件例項化後呼叫(如呼叫構造器之後呼叫)
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException;
/**
* 在設定某個屬性前呼叫,然後再呼叫設定屬性的方法
* 注意:這裡的設定屬性是指通過配置設定屬性,直接呼叫物件的setXX方法不會呼叫該方法,如bean配置中配置了屬性address/age屬性,將會呼叫該方法
* @param pvs 如 PropertyValues: length=2; bean property 'address'; bean property 'age'
*/
@Override
public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException;
2、介面BeanPostProcessor
例項化完成之後呼叫該介面。可實現的介面方法有:
//例項化完成,setBeanName/setBeanFactory完成之後呼叫該方法
public Object postProcessBeforeInitialization(Object o, String s) throws BeansException;
//全部是例項化完成以後呼叫該方法
public Object postProcessAfterInitialization(Object o, String s) throws BeansException;
這兩個介面都在包 org.springframework.beans.factory.config 下,一般稱它們的實現類為“後處理器”。後處理器介面一般不由Bean本身實現,實現類以容器附加裝置的形式註冊到Spring容器中。
當Sprig容器建立任何Bean的時候,這兩個後處理器都會發生作用,所以這兩個後處理器的影響是全域性的。使用者可以通過合理的程式碼控制後處理器只對固定的Bean建立進行處理。
Bean級生命週期介面解決Bean個性化處理的問題,Bean容器級生命週期介面解決容器中某些Bean共性化處理的問題。
二、BeanFactory的bean生命週期相關程式碼
1、xml bena 配置
<bean id="person" class="demo02.bean.Person"
p:address="上海市"
p:age="25"
init-method="myInit"
destroy-method="myDestroy"
/>
p:address/p:age 在例項化的時候會呼叫Bean的對應setXX方法設定屬性。
2、java相關程式碼
InstantiationAwareBeanPostProcessorAdapter實現類相關程式碼
/**
* 例項化前/後,及框架設定Bean屬性時呼叫該介面
*/
public class MyInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter {
//在Bean物件例項化前呼叫
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
//僅對容器中的person bean處理
if ("person".equals(beanName)) {
System.out.println("例項化前呼叫:InstantiationAwareBeanPostProcessorAdapter.postProcessBeforeInstantiation");
}
return null;
}
//在Bean物件例項化後呼叫(如呼叫構造器之後呼叫)
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
//僅對容器中的person bean處理
if ("person".equals(beanName)) {
System.out.println("例項化後呼叫:InstantiationAwareBeanPostProcessorAdapter.postProcessAfterInstantiation");
}
return true;
}
/**
* 在設定某個屬性前呼叫,然後再呼叫設定屬性的方法
* 注意:這裡的設定屬性是指通過配置設定屬性,直接呼叫物件的setXX方法不會呼叫該方法,如bean配置中配置了屬性address/age屬性,將會呼叫該方法
* @param pvs 如 PropertyValues: length=2; bean property 'address'; bean property 'age'
*/
@Override
public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
//僅對容器中的person bean處理
if ("person".equals(beanName)) {
System.out.println("在Bean設定屬性時呼叫:InstantiationAwareBeanPostProcessorAdapter.postProcessPropertyValues,設定值為" + pvs);
}
return pvs;
}
}
BeanPostProcessor實現類相關程式碼
/**
* 例項化完成之後呼叫該介面
*/
public class MyBeanPostProcessor implements BeanPostProcessor {
//例項化完成,setBeanName/setBeanFactory完成之後呼叫該方法
public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
if ("person".equals(s)) {
Person person = (Person) o;
if (person.getName() == null) {
System.out.println("呼叫BeanPostProcessor.postProcessBeforeInitialization,name為空,設定預設名為無名氏");
person.setName("無名氏");
}
}
return o;
}
//全部是例項化完成以後呼叫該方法
public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
if ("person".equals(s)) {
Person person = (Person) o;
if (person.getAge()>20) {
System.out.println("呼叫BeanPostProcessor.postProcessAfterInitialization,age大於20,調整為20");
person.setAge(20);
}
}
return o;
}
}
BeanFactoryLifeCycleMain 容器裝載配置檔案並啟動
/**
* BeanFactory中的Bean的生命週期
* 容器裝載配置檔案,註冊 BeanPostProcessor 和 InstantiationAwareBeanPostProcessorAdapter 後置處理器
*/
public class BeanFactoryLifeCycleMain {
@Test
public void lifeCycleInBeanFactory() {
//裝載配置檔案並啟動容器
Resource resource = new ClassPathResource("beans/beans.xml");
BeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader((DefaultListableBeanFactory) beanFactory);
reader.loadBeanDefinitions(resource);
//向容器中註冊後處理器 MyBeanPostProcessor
((DefaultListableBeanFactory) beanFactory).addBeanPostProcessor(new MyBeanPostProcessor());
//向容器中註冊後處理器 MyInstantiationAwareBeanPostProcessor
//注意:後處理器呼叫順序和註冊順序無關。在處理多個後處理器的情況下,必需通過實現Ordered介面來確定順序
((DefaultListableBeanFactory) beanFactory).addBeanPostProcessor(new MyInstantiationAwareBeanPostProcessor());
//第一次從容器中獲取Person,將觸發容器例項化該bean,這將引發Bean生命週期方法的呼叫
Person person = (Person) beanFactory.getBean("person");
person.introduce();
person.setName("zhangsan");
//第二次從容器中獲取,直接從快取中獲取(預設就是單例)
Person person2 = (Person) beanFactory.getBean("person");
System.out.println(person == person2);//true
//關閉容器
((DefaultListableBeanFactory) beanFactory).destroySingletons();
}
}
執行結果:(DEBUG資訊為Spring框架輸出的,說明了底層呼叫方法和作用)
例項化前呼叫:InstantiationAwareBeanPostProcessorAdapter.postProcessBeforeInstantiation
呼叫了Person的無參構造器
DEBUG 2018-08-02 13:51:07: [org.springframework.beans.factory.support.DefaultListableBeanFactory.(523)doCreateBean] - Eagerly caching bean 'person' to allow for resolving potential circular references
例項化後呼叫:InstantiationAwareBeanPostProcessorAdapter.postProcessAfterInstantiation
在Bean設定屬性時呼叫:InstantiationAwareBeanPostProcessorAdapter.postProcessPropertyValues,設定值為PropertyValues: length=2; bean property 'address'; bean property 'age'
呼叫了setAddress方法設定了屬性
呼叫了setAge方法設定了屬性
呼叫了BeanNameAware.setBeanName, value is person
呼叫了BeanFactoryAware.setBeanFactory, value is org.springframework.beans.factory.support.DefaultListableBeanFactory@44c8afef: defining beans [car,person]; root of factory hierarchy
呼叫BeanPostProcessor.postProcessBeforeInitialization,name為空,設定預設名為無名氏
呼叫了setName方法設定了屬性
DEBUG 2018-08-02 13:51:07: [org.springframework.beans.factory.support.DefaultListableBeanFactory.(1595)invokeInitMethods] - Invoking afterPropertiesSet() on bean with name 'person'
呼叫InitializingBean.afterPropertiesSet
DEBUG 2018-08-02 13:51:07: [org.springframework.beans.factory.support.DefaultListableBeanFactory.(1653)invokeCustomInitMethod] - Invoking init method 'myInit' on bean with name 'person'
呼叫bean配置的init-method
呼叫BeanPostProcessor.postProcessAfterInitialization,age大於20,調整為20
呼叫了setAge方法設定了屬性
DEBUG 2018-08-02 13:51:07: [org.springframework.beans.factory.support.DefaultListableBeanFactory.(477)createBean] - Finished creating instance of bean 'person'
呼叫了introduce方法--> name:無名氏,age:20,address:中國
呼叫了setName方法設定了屬性
DEBUG 2018-08-02 13:51:07: [org.springframework.beans.factory.support.DefaultListableBeanFactory.(249)doGetBean] - Returning cached instance of singleton bean 'person'
true
DEBUG 2018-08-02 13:51:07: [org.springframework.beans.factory.support.DefaultListableBeanFactory.(474)destroySingletons] - Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@44c8afef: defining beans [car,person]; root of factory hierarchy
DEBUG 2018-08-02 13:51:07: [org.springframework.beans.factory.support.DisposableBeanAdapter.(244)destroy] - Invoking destroy() on bean with name 'person'
呼叫DisposableBean.destroy
DEBUG 2018-08-02 13:51:07: [org.springframework.beans.factory.support.DisposableBeanAdapter.(322)invokeCustomDestroyMethod] - Invoking destroy method 'myDestroy' on bean with name 'person'
呼叫bean配置的destroy-method
BeanFactory的Bean生命週期執行步驟如下:
三、關於BeanFactory的Bean生命週期介面的總結
1、通過實現BeanFactory的Bean生命週期相關介面,雖然讓Bean具有了更詳細的生命週期階段,但是也讓Spring框架和Bean緊密聯絡在了一起,同時也增加了程式碼複雜度。因此,如果使用者希望在Bean的生命週期中實現自己的業務,不需要和特定框架關聯,可以通過 bean 的 init-method 和 destroy-method 配置方法來進行業務實現。
2、Spring還擁有一個Bean後置處理器InitDestroyAnnotationBeanPostProcessor,它負責對標註了 @PostConstruct 和 @PreDestroy 的 Bean 進行處理,在 Bean 初始化及銷燬前執行相應的業務邏輯。
3、通常情況下,可以拋棄Bean級的生命週期的4個介面,用更加方便的方法進行代替(如1、2點中的方法)。但是容器級Bean生命週期介面可以合理的使用,處理一些共同的業務。
4、當bean的Scope為非單例,物件銷燬的時候,Spring容器不會幫我們呼叫任何方法,因為是非單例,這個型別的物件有很多個,Spring容器一旦把這個物件交給你之後,就不再管理這個物件了。如果bean的scope設為prototype時,當容器關閉時,destroy方法不會被呼叫。對於prototype作用域的bean,Spring不能對一個該bean的整個生命週期負責:容器在初始化、配置、裝飾或者是裝配完一個prototype例項後,將它交給客戶端,隨後就對該prototype例項不聞不問了。
四、ApplicationContext的Bean生命週期
1、Bean在應用上下文中的生命週期和在BeanFactory中生命週期類似,可以實現所有的BeanFactory的Bean級和容器級生命週期介面。但是,如果Bean實現了org.springframework.context.ApplicationContextAware介面,會增加一個呼叫該介面方法setApplicationContext()的步驟。
2、如果配置檔案中宣告瞭工廠後處理器介面BeanFactoryPostProcessor的實現類,則應用上下文在裝載配置檔案之後初始化Bean例項之前將呼叫這些BeanFactoryPostProcessor對配置資訊進行加工處理。Spring框架提供了多個工廠後處理器如CustomEditorConfigurer、PopertyPlaceholderConfigurer等。如果配置檔案中定義了多個工廠後處理器,最好讓它們實現org.springframework.core.Ordered介面,以便Spring以確定的順序呼叫它們。工廠後處理器是容器級的,僅在應用上下文初始化時呼叫一次,其目的是完成一些配置檔案的加工處理工作。
注意:BeanFactoryPostProcessor的介面方法引數是BeanFactory,而在BeanFactory級Bean生命週期中提到的BeanPostProcessor介面方法引數是Bean物件和Bean的ID值。所以前者是針對應用上下文的,後者是針對BeanFactory的。
3、ApplicationContext和BeanFactory另一個最大的不同之處在於:前者會利用Java反射機制自動識別出配置檔案中定義的BeanPostProcessor、InstantiationAwareBeanPostProcessor和BeanFactoryPostProcessor,並自動將它們註冊到應用上下文中;而後者需要在程式碼中通過手工呼叫addBeanPostProcessor()方法進行註冊。這也是為什麼在應用開發時,我們普遍使用ApplicationContext而很少使用BeanFactory的原因之一。
4、在ApplicationContext中,我們只需要在配置檔案中通過<bean>定義工廠後處理器和Bean後處理器,它們就會按預期的方式執行。
五、ApplicationContext的Bean生命週期程式碼演示
1、java程式碼
實現BeanFactoryPostProcessor介面方法
/**
* ApplicationContext bean生命週期演示
*/
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
//先呼叫本方法,將所有bean生成BeanDefinition物件並設定相關屬性。
//本方法執行完之後,會呼叫bean構造器,並呼叫set相關屬性方法
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition("person");
beanDefinition.getPropertyValues().addPropertyValue("address", "王二麻子");
System.out.println("呼叫了BeanFactoryPostProcessor.postProcessBeanFactory");
}
}
ApplicationContext在啟動時,將首先為配置檔案中每個<bean>生成一個BeanDefinition物件,BeanDefinition是<bean>在Spring容器中的內部表示。當配置檔案中所有的<bean>都被解析成BeanDefinition時,ApplicationContext將呼叫工廠後處理器的方法,因此我們有機會通過程式的方式調整Bean的配置資訊。如上對person的屬性address進行更改設定。
2、xml 相關配置
<!--1、註冊Bean-->
<bean id="person" class="demo02.bean.Person"
p:address="上海市"
p:age="25"
init-method="myInit"
destroy-method="myDestroy"
/>
<!--下面註冊bean後處理器是為了演示ApplicationContext bean生命週期的。-->
<!--ApplicationContext和BeanFactory另一個最大的不同之處在於:
前者會利用Java反射機制自動識別出配置檔案中定義的BeanPostProcessor、InstantiationAwareBeanPostProcessor和BeanFactoryPostProcessor,並自動將它們註冊到應用上下文中;
而後者需要在程式碼中通過手工呼叫addBeanPostProcessor()方法進行註冊。-->
<!--2、註冊Bean後處理器-->
<bean id="myBeanPostProcessor"
class="demo02.BeanFactoryLifeCycle.MyBeanPostProcessor"/>
<!--3、註冊Bean後處理器-->
<bean id="myBeanFactoryPostProcessor"
class="demo02.BeanFactoryLifeCycle.MyBeanFactoryPostProcessor"/>
②和③處定義的BeanPostProcessor和BeanFactoryPostProcessor會自動被ApplicationContext識別並註冊到容器中。②處註冊的工廠後處理器將會對①處配置的屬性值進行調整。在③處,我們還宣告瞭一個Bean後處理器,它也可以對Bean的屬性進行調整。啟動容器並檢視car Bean的資訊,我們將發現car Bean的brand屬性成功被工廠後處理器更改了。
3、測試類
/**
* ApplicationContext bean生命演示
*/
public class ApplicationContexBeanLifeCycleMain {
public static void main(String[] args) {
//ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans/beans.xml");
//ApplicationContext 介面中沒有實現close方法,所以可以用該類(該類是實現了ConfigurableApplicationContext介面和DisposableBean介面)
AbstractApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans/beans.xml");
Person person = (Person) applicationContext.getBean("person");
person.introduce();
applicationContext.close();
}
}
4、執行結果
最後輸出:
呼叫了BeanFactoryPostProcessor.postProcessBeanFactory
呼叫了Person的無參構造器
呼叫了setAddress方法設定了屬性,上海市
呼叫了setAge方法設定了屬性,25
呼叫了setName方法設定了屬性,王二麻子
呼叫了BeanNameAware.setBeanName, value is person
呼叫了BeanFactoryAware.setBeanFactory, value ……
呼叫InitializingBean.afterPropertiesSet
呼叫bean配置的init-method
呼叫BeanPostProcessor.postProcessAfterInitialization,age大於20,調整為20
呼叫了setAge方法設定了屬性,20
呼叫了introduce方法--> name:王二麻子,age:20,address:中國
呼叫DisposableBean.destroy
呼叫bean配置的destroy-method
六、總結
1、Spring的生命週期可以從 BeanFactory 和 ApplicationContext 中的生命週期進行分析;
2、BeanFactory例項化Bean相關介面分為Bean級和容器級。bean級介面我們一般不實現,容器級如果需要處理一些共有特性,可以考慮實現;
3、ApplicationContext中bean的生命週期與BeanFactory類似。beanFactory中的後處理器介面需要呼叫addBeanPostProcessor方法進行註冊,而ApplicationContext中的後處理器可以通過配置和反射進行呼叫;
4、從上可以看到Bean的生命週期相關介面很多,如果都實現很複雜。通常我們的業務都可以通過 init-method 和 destory-method 方法配置來解決,這樣做簡單粗暴。
參考書籍:《精通Spring4.x企業應用開發實戰》
相關文章
- Spring Bean的生命週期SpringBean
- Spring Bean 生命週期SpringBean
- Spring Bean生命週期SpringBean
- 【Spring】Bean的LifeCycle(生命週期)SpringBean
- Spring中bean的生命週期SpringBean
- spring bean的作用域和生命週期SpringBean
- 聊一聊Spring Bean 的生命週期SpringBean
- Spring原始碼:bean的生命週期(一)Spring原始碼Bean
- Spring原始碼:Bean的生命週期(二)Spring原始碼Bean
- 面試Spring之bean的生命週期面試SpringBean
- Spring原始碼:Bean生命週期(五)Spring原始碼Bean
- Spring原始碼:Bean生命週期(四)Spring原始碼Bean
- Spring原始碼:Bean生命週期(三)Spring原始碼Bean
- bean的生命週期Bean
- Spring中與bean有關的生命週期SpringBean
- 【spring原始碼系列】之【Bean的生命週期】Spring原始碼Bean
- 探索Spring系列(一)Spring容器和Bean的生命週期SpringBean
- Spring(十二):IOC容器中Bean的生命週期方法SpringBean
- JAVA面試題:Spring中bean的生命週期Java面試題SpringBean
- Spring Bean各階段生命週期的介紹SpringBean
- 淺析spring——IOC 之 分析 Bean 的生命週期SpringBean
- Spring Bean生命週期,好像人的一生。。SpringBean
- IOC - bean 生命週期Bean
- Bean-生命週期Bean
- Bean 的生命週期回撥Bean
- Bean的一生(Bean的生命週期)Bean
- 詳解Spring中Bean的作用域與生命週期SpringBean
- spring通過註解註冊bean的方式+spring生命週期SpringBean
- Spring的生命週期Spring
- Spring容器啟動流程+Bean的生命週期【附原始碼】SpringBean原始碼
- spring生命週期Spring
- spring迴圈依賴解決過程&Bean的生命週期SpringBean
- 劍指Spring原始碼(三)俯瞰Spring的Bean的生命週期(大眾版)Spring原始碼Bean
- Spring的生命週期主Spring
- Spring Boot 啟動原始碼解析結合Spring Bean生命週期分析Spring Boot原始碼Bean
- Spring的Bean生命週期中@PostConstruct註解SpringBeanStruct
- 2024-06-17-Spring 原始碼閱讀(三)Bean 的生命週期Spring原始碼Bean
- Spring 框架基礎(02):Bean的生命週期,作用域,裝配總結Spring框架Bean