目錄
一、Bean的生命周期
二、常用擴充點
2.1 第一大類:影響多個Bean的接口
2.1.1 InstantiationAwareBeanPostProcessor
2.1.2 BeanPostProcesso
2.2 第二大類:隻調用一次的接口
2.2.1 無所不知的Aware
2.2.2 簡單的兩個生命周期接口
三、擴充閱讀: BeanPostProcessor 注冊時機與執行順序
3.1 注冊時機
3.2 執行順序
四、總結
4.1 四個階段
4.2 多個擴充點
4.3 Bean的生命周期流程圖
之前看到過一篇對Bean生命周期講解的很不錯的部落格,現在我對其再做一些整理補充分享給大家,以供學習使用。
一、Bean的生命周期
Spring Bean的生命周期是Spring面試熱點問題。這個問題即考察對Spring的微觀了解,又考察對Spring的宏觀認識,想要答好并不容易!本文希望能夠從源碼角度入手,幫助面試者徹底搞定Spring Bean的生命周期。
隻有四個!
是的,Spring Bean的生命周期隻有這四個階段。把這四個階段和每個階段對應的擴充點糅合在一起雖然沒有問題,但是這樣非常淩亂,難以記憶。要徹底搞清楚Spring的生命周期,首先要把這四個階段牢牢記住。執行個體化和屬性指派對應構造方法和setter方法的注入,初始化和銷毀是使用者能自定義擴充的兩個階段。在這四步之間穿插的各種擴充點,稍後會講。
- 執行個體化 Instantiation
- 屬性指派 Populate
- 初始化 Initialization
- 銷毀 Destruction
執行個體化 -> 屬性指派 -> 初始化 -> 銷毀
主要邏輯都在doCreate()方法中,邏輯很清晰,就是順序調用以下三個方法,這三個方法與三個生命周期階段一一對應,非常重要,在後續擴充接口分析中也會涉及。
- createBeanInstance() -> 執行個體化
- populateBean() -> 屬性指派
- initializeBean() -> 初始化
注:bean的生命周期是從将bean定義全部注冊到BeanFacotry中以後開始的。
源碼如下,能證明執行個體化,屬性指派和初始化這三個生命周期的存在。關于本文的Spring源碼都将忽略無關部分,便于了解:
建立Bean:
// 忽略了無關代碼
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (instanceWrapper == null) {
// 執行個體化階段!
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
// 屬性指派階段!
populateBean(beanName, mbd, instanceWrapper);
// 初始化階段!
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}
上面這些這個執行個體化Bean的方法是在getBean()方法中調用的,而getBean是在finishBeanFactoryInitialization方法中調用的,用來執行個體化單例非懶加載Bean,源碼如下:
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
// 所有BeanPostProcesser初始化的調用點
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
// 所有單例非懶加載Bean的調用點
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
}
銷毀Bean:
至于銷毀,是在容器關閉時調用的,詳見ConfigurableApplicationContext#close()
二、常用擴充點
Spring生命周期相關的常用擴充點非常多,是以問題不是不知道,而是記不住或者記不牢。其實記不住的根本原因還是不夠了解,這裡通過源碼+分類的方式幫大家記憶。
區分影響一個bean或者多個bean是從源碼分析得出的.
以BeanPostProcessor為例:
- 從refresh方法來看,BeanPostProcessor 執行個體化比正常的bean早.
- 從initializeBean方法看,每個bean初始化前後都調用所有BeanPostProcessor的postProcessBeforeInitialization和postProcessAfterInitialization方法.
2.1 第一大類:影響多個Bean的接口
實作了這些接口的Bean會切入到多個Bean的生命周期中。正因為如此,這些接口的功能非常強大,Spring内部擴充也經常使用這些接口,例如自動注入以及AOP的實作都和他們有關。
- InstantiationAwareBeanPostProcessor
- BeanPostProcessor
這兩兄弟可能是Spring擴充中最重要的兩個接口!InstantiationAwareBeanPostProcessor作用于執行個體化階段的前後,BeanPostProcessor作用于初始化階段的前後。正好和第一、第三個生命周期階段對應。通過圖能更好了解:
2.1.1 InstantiationAwareBeanPostProcessor
InstantiationAwareBeanPostProcessor實際上繼承了BeanPostProcessor接口,嚴格意義上來看他們不是兩兄弟,而是兩父子。但是從生命周期角度我們重點關注其特有的對執行個體化階段的影響,圖中省略了從BeanPostProcessor繼承的方法。
InstantiationAwareBeanPostProcessor extends BeanPostProcessor
InstantiationAwareBeanPostProcessor源碼分析:
- postProcessBeforeInstantiation調用點,忽略無關代碼:
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
try {
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
// postProcessBeforeInstantiation方法調用點,這裡就不跟進了,
// 有興趣的同學可以自己看下,就是for循環調用所有的InstantiationAwareBeanPostProcessor
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
try {
// 上文提到的doCreateBean方法,可以看到
// postProcessBeforeInstantiation方法在建立Bean之前調用
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
}
可以看到,postProcessBeforeInstantiation在doCreateBean之前調用,也就是在bean執行個體化之前調用的,英文源碼注釋解釋道該方法的傳回值會替換原本的Bean作為代理,這也是Aop等功能實作的關鍵點。
- postProcessAfterInstantiation調用點,忽略無關代碼:
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
// state of the bean before properties are set. This can be used, for example,
// to support styles of field injection.
boolean continueWithPropertyPopulation = true;
// InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation()
// 方法作為屬性指派的前置檢查條件,在屬性指派之前執行,能夠影響是否進行屬性指派!
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
continueWithPropertyPopulation = false;
break;
}
}
}
}
// 忽略後續的屬性指派操作代碼
}
可以看到該方法在屬性指派方法内,但是在真正執行指派操作之前。其傳回值為boolean,傳回false時可以阻斷屬性指派階段(continueWithPropertyPopulation = false;)。
2.1.2 BeanPostProcesso
關于BeanPostProcessor執行階段的源碼穿插在下文Aware接口的調用時機分析中,因為部分Aware功能的就是通過他實作的!隻需要先記住BeanPostProcessor在初始化前後調用就可以了。
接口源碼:
public interface BeanPostProcessor {
//bean初始化之前調用
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
//bean初始化之後調用
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
2.2 第二大類:隻調用一次的接口
這一大類接口的特點是功能豐富,常用于使用者自定義擴充。
第二大類中又可以分為兩類:
- Aware類型的接口
- 生命周期接口
2.2.1 無所不知的Aware
Aware類型的接口的作用就是讓我們能夠拿到Spring容器中的一些資源。基本都能夠見名知意,Aware之前的名字就是可以拿到什麼資源,例如BeanNameAware可以拿到BeanName,以此類推。調用時機需要注意:所有的Aware方法都是在初始化階段之前調用的!
Aware接口衆多,這裡同樣通過分類的方式幫助大家記憶。
Aware接口具體可以分為兩組,至于為什麼這麼分,詳見下面的源碼分析。如下排列順序同樣也是Aware接口的執行順序,能夠見名知意的接口不再解釋。
Aware Group1
- BeanNameAware
- BeanClassLoaderAware
- BeanFactoryAware
Aware Group2
- EnvironmentAware
- EmbeddedValueResolverAware 這個知道的人可能不多,實作該接口能夠擷取Spring EL解析器,使用者的自定義注解需要支援spel表達式的時候可以使用,非常友善。
- ApplicationContextAware(ResourceLoaderAware\ApplicationEventPublisherAware\MessageSourceAware) 這幾個接口可能讓人有點懵,實際上這幾個接口可以一起記,其傳回值實質上都是目前的ApplicationContext對象,因為ApplicationContext是一個複合接口,如下:
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver {}
這裡涉及到另一道面試題,ApplicationContext和BeanFactory的差別,可以從ApplicationContext繼承的這幾個接口入手,除去BeanFactory相關的兩個接口就是ApplicationContext獨有的功能,這裡不詳細說明。
Aware調用時機源碼分析
詳情如下,忽略了部分無關代碼。代碼位置就是我們上文提到的initializeBean方法詳情,這也說明了Aware都是在初始化階段之前調用的!
// 見名知意,初始化階段調用的方法
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
// 這裡調用的是Group1中的三個Bean開頭的Aware
invokeAwareMethods(beanName, bean);
Object wrappedBean = bean;
// 這裡調用的是Group2中的幾個Aware,
// 而實質上這裡就是前面所說的BeanPostProcessor的調用點!
// 也就是說與Group1中的Aware不同,這裡是通過BeanPostProcessor(ApplicationContextAwareProcessor)實作的。
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
// 這個是初始化方法,下文要介紹的InitializingBean調用點就是在這個方法裡面
invokeInitMethods(beanName, wrappedBean, mbd);
// BeanPostProcessor的另一個調用點
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
return wrappedBean;
}
可以看到并不是所有的Aware接口都使用同樣的方式調用。Bean××Aware都是在代碼中直接調用的,而ApplicationContext相關的Aware都是通過BeanPostProcessor#postProcessBeforeInitialization()實作的。感興趣的可以自己看一下ApplicationContextAwareProcessor這個類的源碼,就是判斷目前建立的Bean是否實作了相關的Aware方法,如果實作了會調用回調方法将資源傳遞給Bean。
至于Spring為什麼這麼實作,應該沒什麼特殊的考量。也許和Spring的版本更新有關。基于對修改關閉,對擴充開放的原則,Spring對一些新的Aware采用了擴充的方式添加。
BeanPostProcessor的調用時機也能在這裡展現,包圍住invokeInitMethods方法,也就說明了在初始化階段的前後執行。
關于Aware接口的執行順序,其實隻需要記住第一組在第二組執行之前就行了。每組中各個Aware方法的調用順序其實沒有必要記,有需要的時候點進源碼一看便知。
2.2.2 簡單的兩個生命周期接口
至于剩下的兩個生命周期接口就很簡單了,執行個體化和屬性指派都是Spring幫助我們做的,能夠自己實作的有初始化和銷毀兩個生命周期階段。
2.2.2.1 InitializingBean接口
InitializingBean顧名思義,是初始化Bean相關的接口。
接口定義:
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
看方法名,是在讀完Properties檔案,之後執行的方法。afterPropertiesSet()方法是在初始化過程中被調用的。
InitializingBean 對應生命周期的初始化階段,在上面源碼的invokeInitMethods(beanName, wrappedBean, mbd);方法中調用。
有一點需要注意,因為Aware方法都是執行在初始化方法之前,是以可以在初始化方法中放心大膽的使用Aware接口擷取的資源,這也是我們自定義擴充Spring的常用方式。
除了實作InitializingBean接口之外還能通過注解(@PostConstruct)或者xml配置的方式指定初始化方法(init-method),至于這幾種定義方式的調用順序其實沒有必要記。因為這幾個方法對應的都是同一個生命周期,隻是實作方式不同,我們一般隻采用其中一種方式。
三種實作指定初始化方法的方法:
- 使用@PostConstruct注解,該注解作用于void方法上
- 在配置檔案中配置init-method方法
<bean id="student" class="com.demo.Student" init-method="init2">
<property name="name" value="小明"></property>
<property name="age" value="20"></property>
<property name="school" ref="school"></property>
</bean>
- 将類實作InitializingBean接口
@Component("student")
public class Student implements InitializingBean{
private String name;
private int age;
…
}
執行:
@Component("student")
public class Student implements InitializingBean{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//1.使用postconstrtct注解
@PostConstruct
public void init(){
System.out.println("執行 init方法");
}
//2.在xml配置檔案中配置init-method方法
public void init2(){
System.out.println("執行init2方法 ");
}
//3.實作InitializingBean接口
public void afterPropertiesSet() throws Exception {
System.out.println("執行init3方法");
}
}
通過測試我們可以得出結論,三種實作方式的執行順序是:
Constructor > @PostConstruct > InitializingBean > init-method
2.2.2.2 DisposableBean接口
DisposableBean 類似于InitializingBean,對應生命周期的銷毀階段,以ConfigurableApplicationContext#close()方法作為入口,實作是通過循環擷取所有實作了DisposableBean接口的Bean然後調用其destroy()方法 。
接口定義:
public interface DisposableBean {
void destroy() throws Exception;
}
定義一個實作了DisposableBean接口的Bean:
public class IndexBean implements InitializingBean,DisposableBean {
public void destroy() throws Exception {
System.out.println("destroy");
}
public void afterPropertiesSet() throws Exception {
System.out.println("init-afterPropertiesSet()");
}
public void test(){
System.out.println("init-test()");
}
}
執行:
public class Main {
public static void main(String[] args) {
AbstractApplicationContext applicationContext=new ClassPathXmlApplicationContext("classpath:application-usertag.xml");
System.out.println("init-success");
applicationContext.registerShutdownHook();
}
}
執行結果:
init-afterPropertiesSet()
init-test()
init-success
destroy
也就是說,在對象銷毀的時候,會去調用DisposableBean的destroy方法。在進入到銷毀過程時先去調用一下DisposableBean的destroy方法,然後後執行 destroy-method聲明的方法(用來銷毀Bean中的各項資料)。
三、擴充閱讀: BeanPostProcessor 注冊時機與執行順序
首先要明确一個概念,在spring中一切皆bean
所有的元件都會被作為一個bean裝配到spring容器中,過程如下圖:
是以我們前面所講的那些拓展點,也都會被作為一個個bean裝配到spring容器中
3.1 注冊時機
我們知道BeanPostProcessor也會注冊為Bean,那麼Spring是如何保證BeanPostProcessor在我們的業務Bean之前初始化完成呢?
請看我們熟悉的refresh()方法的源碼,省略部分無關代碼(refresh的詳細注解見refresh()):
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
// 注冊所有BeanPostProcesser的方法
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
// 所有單例非懶加載Bean的建立方法
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
}
可以看出,Spring是先執行registerBeanPostProcessors()進行BeanPostProcessors的注冊,然後再執行finishBeanFactoryInitialization建立我們的單例非懶加載的Bean。
3.2 執行順序
BeanPostProcessor有很多個,而且每個BeanPostProcessor都影響多個Bean,其執行順序至關重要,必須能夠控制其執行順序才行。關于執行順序這裡需要引入兩個排序相關的接口:PriorityOrdered、Ordered
- PriorityOrdered是一等公民,首先被執行,PriorityOrdered公民之間通過接口傳回值排序
- Ordered是二等公民,然後執行,Ordered公民之間通過接口傳回值排序
- 都沒有實作是三等公民,最後執行
在以下源碼中,可以很清晰的看到Spring注冊各種類型BeanPostProcessor的邏輯,根據實作不同排序接口進行分組。優先級高的先加入,優先級低的後加入。
// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
// 首先,加入實作了PriorityOrdered接口的BeanPostProcessors,順便根據PriorityOrdered排了序
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
// 然後,加入實作了Ordered接口的BeanPostProcessors,順便根據Ordered排了序
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
// 最後加入其他正常的BeanPostProcessors
boolean reiterate = true;
while (reiterate) {
reiterate = false;
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
reiterate = true;
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
}
根據排序接口傳回值排序,預設升序排序,傳回值越低優先級越高。
/**
* Useful constant for the highest precedence value.
* @see java.lang.Integer#MIN_VALUE
*/
int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
/**
* Useful constant for the lowest precedence value.
* @see java.lang.Integer#MAX_VALUE
*/
int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
PriorityOrdered、Ordered接口作為Spring整個架構通用的排序接口,在Spring中應用廣泛,也是非常重要的接口。
四、總結
Spring Bean的生命周期分為四個階段和多個擴充點。擴充點又可以分為影響多個Bean和影響單個Bean。整理如下:
4.1 四個階段
- 執行個體化 Instantiation
- 屬性指派 Populate
- 初始化 Initialization
- 銷毀 Destruction
4.2 多個擴充點
- 影響多個Bean
- BeanPostProcessor
- InstantiationAwareBeanPostProcessor
- 影響單個Bean
- Aware
- Aware Group1
- BeanNameAware
- BeanClassLoaderAware
- BeanFactoryAware
- Aware Group2
- EnvironmentAware
- EmbeddedValueResolverAware
- ApplicationContextAware(ResourceLoaderAware\ApplicationEventPublisherAware\MessageSourceAware)
- Aware Group1
- 生命周期
- InitializingBean
- DisposableBean
- Aware
4.3 Bean的生命周期流程圖
原文連結:https://www.jianshu.com/p/1dec08d290c1