一文解讀Spring Bean的生命周期
1. Spring IOC容器
1.1 Spring IOC 容器的設(shè)計(jì)
Spring IOC 容器的設(shè)計(jì)主要是基于BeanFactory和ApplicationContext兩個(gè)接口,其中ApplicationContext是BeanFactory的子接口之一,換句話說(shuō)BeanFactory是Spring IOC容器所定義的最頂層接口,而ApplicationContext是其高級(jí)接口之一,并且對(duì)于BeanFactory功能做了許多有用的擴(kuò)展,所以在絕大部分的工作場(chǎng)景中,都會(huì)使用ApplicationContext作為Spring IOC 容器,如下圖所示:

首先我們定義一個(gè)User實(shí)體類:
public class User {
String username;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
'}';
}
}1.1.1 BeanFactory
BeanFactory的使用注冊(cè)Bean對(duì)象以及獲取Bean對(duì)象代碼如下:
DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
RootBeanDefinition rootBeanDefinition=new RootBeanDefinition(User.class);
beanFactory.registerBeanDefinition("user",rootBeanDefinition);
System.out.println(beanFactory.getBean("user",User.class));1.1.2 ApplicationContext
ApplicationContext的使用注冊(cè)Bean對(duì)象以及獲取Bean對(duì)象代碼如下:
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(User.class);
applicationContext.refresh();
System.out.println(applicationContext.getBean("user", User.class));1.2 Spring Bean的生命周期
Spring 容器可以管理 singleton 作用域 Bean 的生命周期,在此作用域下,Spring 能夠精確地知道該 Bean 何時(shí)被創(chuàng)建,何時(shí)初始化完成,以及何時(shí)被銷毀。
而對(duì)于 prototype 作用域的 Bean,Spring 只負(fù)責(zé)創(chuàng)建,當(dāng)容器創(chuàng)建了 Bean 的實(shí)例后,Bean 的實(shí)例就交給客戶端代碼管理,Spring 容器將不再跟蹤其生命周期。每次客戶端請(qǐng)求 prototype 作用域的 Bean 時(shí),Spring 容器都會(huì)創(chuàng)建一個(gè)新的實(shí)例,并且不會(huì)管那些被配置成 prototype 作用域的 Bean 的生命周期。
生命周期主要是為了了解Spring IOC容器初始化和銷毀Bean的過(guò)程,通過(guò)對(duì)它的學(xué)習(xí)就可以知道如何在初始化和銷毀的時(shí)候加入自定義的方法,以滿足特定的需求。如下圖:

從上圖可以看到,Spring IoC容器對(duì)Bean 的管理還是比較復(fù)雜的,Spring loC容器在執(zhí)行了初始化和依賴注入后,會(huì)執(zhí)行一定的步驟來(lái)完成初始化,通過(guò)這些步驟我們就能自定義初始化,而在Spring IoC 容器正常關(guān)閉的時(shí)候,它也會(huì)執(zhí)行一定的步驟來(lái)關(guān)閉容器,釋放資源。除需要了解整個(gè)生命周期的步驟外,還要知道這些生命周期的接口是針對(duì)什么而言的,首先介紹生命周期的步驟。
1. 如果 Bean 實(shí)現(xiàn)了接口 BeanNameAware的setBeanName方法,那么它就會(huì)調(diào)用這個(gè)方法。
2. 如果 Bean 實(shí)現(xiàn)了接口 BeanFactoryAware 的 setBeanFactory方法,那么它就會(huì)調(diào)用這個(gè)方法。
3. 如果 Bean實(shí)現(xiàn)了接口 ApplicationContextAware 的 setApplicationContext方法,且
Spring loC容器也必須是一個(gè)ApplicationContext 接口的實(shí)現(xiàn)類,那么才會(huì)調(diào)用這個(gè)方法,否則是不調(diào)用的。
4. 如果 Bean 實(shí)現(xiàn)了接口 BeanPostProcessor 的 postProcessBeforeInitialization方法,那么它就會(huì)調(diào)用這個(gè)方法。
5 .如果 Bean實(shí)現(xiàn)了接口BeanFactoryPostProcessor的afterPropertiesSet方法,那么它就會(huì)調(diào)用這個(gè)方法。
6. 如果 Bean自定義了初始化方法,它就會(huì)調(diào)用已定義的初始化方法。
7. 如果Bean 實(shí)現(xiàn)了接口 BeanPostProcessor 的postProcessAfterInitialization方法,完成了這些調(diào)用,這個(gè)時(shí)候Bean 就完成了初始化,那么 Bean就生存在Spring loC的容器中了,使用者就可以從中獲取 Bean的服務(wù)。
8. 當(dāng)服務(wù)器正常關(guān)閉,或者遇到其他關(guān)閉 Spring loC 容器的事件,它就會(huì)調(diào)用對(duì)應(yīng)的方完成Bean 的銷毀,其步驟如下:
如果Bean實(shí)現(xiàn)了接口 DisposableBean 的 destroy方法,那么就會(huì)調(diào)用它。
如果定義了自定義的銷毀方法,那么就會(huì)調(diào)用它。
1.2.1 BeanDefinition
Spring容器啟動(dòng)的過(guò)程中,會(huì)將Bean解析成Spring內(nèi)部的BeanDefinition結(jié)構(gòu)。不管是是通過(guò)xml配置文件的<Bean>標(biāo)簽,還是通過(guò)注解配置的@Bean,還是@Compontent標(biāo)注的類,還是掃描得到的類,它最終都會(huì)被解析成一個(gè)BeanDefinition對(duì)象,最后我們的Bean工廠就會(huì)根據(jù)這份Bean的定義信息,對(duì)bean進(jìn)行實(shí)例化、初始化等等操作。
你可以把BeanDefinition丟給Bean工廠,然后Bean工廠就會(huì)根據(jù)這個(gè)信息幫你生產(chǎn)一個(gè)Bean實(shí)例,拿去使用。
BeanDefinition里面里面包含了bean定義的各種信息,如:bean對(duì)應(yīng)的class、scope、lazy信息、dependOn信息、autowireCandidate(是否是候選對(duì)象)、primary(是否是主要的候選者)等信息。

RootBeanDefinition類:表示根bean定義信息,通常bean中沒(méi)有父bean的就使用這種表示。
ChildBeanDefinition類:表示子bean定義信息,如果需要指定父bean的,可以使用ChildBeanDefinition來(lái)定義子bean的配置信息,里面有parentName屬性,用來(lái)指定父bean的名稱。
GenericBeanDefinition類:通用的bean定義信息,既可以表示沒(méi)有父bean的bean配置信息,也可以表示有父bean的子bean配置信息,這個(gè)類里面也有parentName屬性,用來(lái)指定父bean的名稱。
ConfigurationClassBeanDefinition類:表示通過(guò)配置類中@Bean方法定義bean信息
可以通過(guò)配置類中使用@Bean來(lái)標(biāo)注一些方法,通過(guò)這些方法來(lái)定義bean,這些方法配置的bean信息最后會(huì)轉(zhuǎn)換為ConfigurationClassBeanDefinition類型的對(duì)象。
AnnotatedBeanDefinition接口:表示通過(guò)注解的方式定義的bean信息。
BeanDefinitionBuilder:構(gòu)建BeanDefinition的工具類
1.2.2 InstantiationAwareBeanPostProcessor和BeanPostProcessor
InstantiationAwareBeanPostProcessor實(shí)際上繼承了BeanPostProcessor接口。InstantiationAwareBeanPostProcessor作用于實(shí)例化階段的前后,BeanPostProcessor作用于初始化階段的前后。如下圖:

BeanPostProcessor是一個(gè)接口,還有很多子接口,這些接口中提供了很多方法,spring在bean生命周期的不同階段,會(huì)調(diào)用BeanPostProcessor中的一些方法,來(lái)對(duì)生命周期進(jìn)行擴(kuò)展,bean生命周期中的所有擴(kuò)展點(diǎn)都是依靠這個(gè)集合中的BeanPostProcessor來(lái)實(shí)現(xiàn)的。該接口提供了兩個(gè)函數(shù):postProcessBeforeInitialzation( Object bean, String beanName ) 當(dāng)前正在初始化的bean對(duì)象會(huì)被傳遞進(jìn)來(lái),我們就可以對(duì)這個(gè)bean作任何處理。 這個(gè)函數(shù)會(huì)先于InitialzationBean執(zhí)行,因此稱為前置處理。 所有Aware接口的注入就是在這一步完成的。postProcessAfterInitialzation( Object bean, String beanName ) 當(dāng)前正在初始化的bean對(duì)象會(huì)被傳遞進(jìn)來(lái),我們就可以對(duì)這個(gè)bean作任何處理。 這個(gè)函數(shù)會(huì)在InitialzationBean完成后執(zhí)行,因此稱為后置處理。
Spring Aware是Spring定義的回調(diào)接口。何為回調(diào)?就是客戶程序C調(diào)用服務(wù)程序S中的某個(gè)函數(shù)A,然后S又在某個(gè)時(shí)候反過(guò)來(lái)調(diào)用C中的某個(gè)函數(shù)B,對(duì)于C來(lái)說(shuō),這個(gè)B便叫做回調(diào)函數(shù)。
1.2.3 測(cè)試生命周期
我們自定義一個(gè)User實(shí)體類,要求Spring容器使用我們自定義的@MyAutowired注解標(biāo)注的構(gòu)造方法進(jìn)行構(gòu)造Bean對(duì)象,然后我們觀察在Bean周期的日志打印,更好的理解Bean周期過(guò)程。
自定義@MyAutowired注解
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.CONSTRUCTOR)
public @interface MyAutowired {
}定義User實(shí)體類
public class User implements InitializingBean, DisposableBean {
String username;
//Spring為了降低對(duì)客戶代碼的侵入性,給bean的配置提供了init-method屬性,
// 該屬性指定了在這一階段需要執(zhí)行的函數(shù)名。Spring便會(huì)在初始化階段執(zhí)行我們?cè)O(shè)置的函數(shù)。
// init-method本質(zhì)上仍然使用了InitializingBean接口。
public void init(){
System.out.println(this.getClass().getSimpleName()+" 執(zhí)行自定義初始化方法");
}
public User() {
}
@MyAutowired
public User(String username) {
this.username = username;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
'}';
}
@Override
public void destroy() throws Exception {
System.out.println("調(diào)用DisposableBean接口的destroy方法");
}
//afterPropertiesSet()這一階段也可以在bean正式構(gòu)造完成前增加我們自定義的邏輯,
// 但它與前置處理不同,由于該函數(shù)并不會(huì)把當(dāng)前bean對(duì)象傳進(jìn)來(lái),因此在這一步?jīng)]辦法處理對(duì)象本身,
// 只能增加一些額外的邏輯。 若要使用它,我們需要讓bean實(shí)現(xiàn)該接口,把要增加的邏輯寫(xiě)在該函數(shù)中。
// 然后Spring會(huì)在前置處理完成后檢測(cè)當(dāng)前bean是否實(shí)現(xiàn)了該接口,并執(zhí)行afterPropertiesSet函數(shù)
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("調(diào)用afterPropertiesSet方法");
}
}測(cè)試代碼
public class MyTest {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
//InstantiationAwareBeanPostProcessor接口在Bean對(duì)象實(shí)例化前的方法
beanFactory.addBeanPostProcessor(new InstantiationAwareBeanPostProcessor() {
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if(beanName.equals("user"))
System.out.println(beanName+" Bean對(duì)象在實(shí)例化之前操作**");
return null;
}
});
//InstantiationAwareBeanPostProcessor接口在Bean對(duì)象實(shí)例化后的方法
beanFactory.addBeanPostProcessor(new InstantiationAwareBeanPostProcessor() {
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
if(beanName.equals("user"))
System.out.println(beanName+" Bean對(duì)象在實(shí)例化之后操作**");
return false;
}
});
//使用自己自定義含有@MyAutowired注解的構(gòu)造方法,實(shí)例化Bean對(duì)象
beanFactory.addBeanPostProcessor(new SmartInstantiationAwareBeanPostProcessor() {
public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException {
if(beanName.equals("user"))
System.out.println("實(shí)例化Bean對(duì)象");
Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors();
List<Constructor<?>> collect = Arrays.stream(declaredConstructors).filter(i -> i.isAnnotationPresent(MyAutowired.class)).collect(Collectors.toList());
Constructor[] constructors = collect.toArray(new Constructor[collect.size()]);
return constructors.length>0?constructors:null;
}
});
//BeanPostProcessor接口在Bean對(duì)象初始化之前的方法調(diào)用
beanFactory.addBeanPostProcessor(new BeanPostProcessor() {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if(beanName.equals("user"))
System.out.println(beanName+" Bean對(duì)象在初始化之前操作**");
return null;
}
});
//BeanPostProcessor接口在Bean對(duì)象初始化之后的方法調(diào)用
beanFactory.addBeanPostProcessor(new BeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if(beanName.equals("user"))
System.out.println(beanName+" Bean對(duì)象在初始化之后操作**");
return null;
}
});
RootBeanDefinition rootBeanDefinition= (RootBeanDefinition) BeanDefinitionBuilder.rootBeanDefinition(User.class).setInitMethodName("init").getBeanDefinition();
beanFactory.registerBeanDefinition("user",rootBeanDefinition);
beanFactory.registerBeanDefinition("username", BeanDefinitionBuilder.genericBeanDefinition(String.class)
.addConstructorArgValue("admin").getBeanDefinition());
System.out.println(beanFactory.getBean("user",User.class));
beanFactory.destroySingletons();
}
}測(cè)試截圖

到此這篇關(guān)于一文解讀Spring Bean的生命周期的文章就介紹到這了,更多相關(guān)Spring Bean生命周期內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
spring security獲取用戶信息為null或者串值的解決
這篇文章主要介紹了spring security獲取用戶信息為null或者串值的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03
Java中實(shí)現(xiàn)接口與繼承的區(qū)別及說(shuō)明
這篇文章主要介紹了Java中實(shí)現(xiàn)接口與繼承的區(qū)別及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03
使用遞歸算法結(jié)合數(shù)據(jù)庫(kù)解析成Java樹(shù)形結(jié)構(gòu)的代碼解析
這篇文章主要介紹了使用遞歸算法結(jié)合數(shù)據(jù)庫(kù)解析成Java樹(shù)形結(jié)構(gòu)的代碼解析的相關(guān)資料,需要的朋友可以參考下2017-09-09
Java數(shù)據(jù)結(jié)構(gòu)之ArrayList從順序表到實(shí)現(xiàn)
Java中的ArrayList是一種基于數(shù)組實(shí)現(xiàn)的數(shù)據(jù)結(jié)構(gòu),支持動(dòng)態(tài)擴(kuò)容和隨機(jī)訪問(wèn)元素,可用于實(shí)現(xiàn)順序表等數(shù)據(jù)結(jié)構(gòu)。ArrayList在內(nèi)存中連續(xù)存儲(chǔ)元素,支持快速的隨機(jī)訪問(wèn)和遍歷。通過(guò)學(xué)習(xí)ArrayList的實(shí)現(xiàn)原理和使用方法,可以更好地掌握J(rèn)ava中的數(shù)據(jù)結(jié)構(gòu)和算法2023-04-04
Java手把手必會(huì)的實(shí)例漢諾塔講解練習(xí)
漢諾塔,傳說(shuō)神在創(chuàng)造世界的時(shí)候做了三根金剛石柱子,并在一個(gè)教塔里留下了三根金剛石棒,第一根上面從上到下套著64個(gè)按從小到大排列的金盤,神命令廟里的眾僧將它們一個(gè)個(gè)地從這根金剛石棒搬到另一根金剛石棒上,大盤不能放在小盤上。最后64個(gè)金盤仍然要按從小到大排列2021-09-09

