Spring中Bean的生命周期原理解析:實(shí)例化、屬性賦值、初始化、運(yùn)行期和銷(xiāo)毀
1. 理解Bean的生命周期
在Spring IOC容器中,Bean的生命周期大致如下:
實(shí)例化:當(dāng)啟動(dòng)
Spring應(yīng)用時(shí),IOC容器就會(huì)為在配置文件中聲明的每個(gè)<bean>創(chuàng)建一個(gè)實(shí)例。屬性賦值:實(shí)例化后,
Spring就通過(guò)反射機(jī)制給Bean的屬性賦值。調(diào)用初始化方法:如果
Bean配置了初始化方法,Spring就會(huì)調(diào)用它。初始化方法是在Bean創(chuàng)建并賦值之后調(diào)用,可以在這個(gè)方法里面寫(xiě)一些業(yè)務(wù)處理代碼或者做一些初始化的工作。Bean運(yùn)行期:此時(shí),Bean已經(jīng)準(zhǔn)備好被程序使用了,它已經(jīng)被初始化并賦值完成。應(yīng)用程序關(guān)閉:當(dāng)關(guān)閉
IOC容器時(shí),Spring會(huì)處理配置了銷(xiāo)毀方法的Bean。調(diào)用銷(xiāo)毀方法:如果
Bean配置了銷(xiāo)毀方法,Spring會(huì)在所有Bean都已經(jīng)使用完畢,且IOC容器關(guān)閉之前調(diào)用它,可以在銷(xiāo)毀方法里面做一些資源釋放的工作,比如關(guān)閉連接、清理緩存等。
這就是Spring IOC容器管理Bean的生命周期,幫助我們管理對(duì)象的創(chuàng)建和銷(xiāo)毀,以及在適當(dāng)?shù)臅r(shí)機(jī)做適當(dāng)?shù)氖虑椤?/p>
我們可以將生命周期的觸發(fā)稱(chēng)為回調(diào),因?yàn)樯芷诘姆椒ㄊ俏覀冏约憾x的,但方法的調(diào)用是由框架內(nèi)部幫我們完成的,所以可以稱(chēng)之為“回調(diào)”。
2. 理解init-method和destroy-method
讓我們先了解一種最容易理解的生命周期階段:初始化和銷(xiāo)毀方法。這些方法可以在Bean的初始化和銷(xiāo)毀階段起作用,我們通過(guò)示例來(lái)演示這種方式。
為了方便演示XML和注解的方式,接下來(lái)我們會(huì)創(chuàng)建兩個(gè)類(lèi)來(lái)分別進(jìn)行演示,分別為Lion和Elephant,讓我們一步一步對(duì)比觀察。
2.1 從XML配置創(chuàng)建Bean看生命周期
先創(chuàng)建一個(gè)類(lèi)Lion
package com.example.demo.bean;
?
public class Lion {
?
private String name;
?
public void setName(String name) {
this.name = name;
}
?
public void init() {
System.out.println(name + " has been initialized...");
}
?
public void destroy() {
System.out.println(name + " has been destroyed...");
}
}在XML中,我們使用<bean>標(biāo)簽來(lái)注冊(cè)Lion:
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 class="com.example.demo.bean.Lion"
init-method="init" destroy-method="destroy">
<property name="name" value="simba"/>
</bean>
</beans>
?加上主程序
package com.example.demo.application;
?
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.support.ClassPathXmlApplicationContext;
?
@ComponentScan("com.example")public class DemoApplication {
public static void main(String[] args) {
System.out.println("Spring容器初始化開(kāi)始");
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println("Spring容器初始化完成。");
System.out.println("==================");
System.out.println("Spring容器準(zhǔn)備關(guān)閉");
context.close();
System.out.println("Spring容器已關(guān)閉。");
}
}運(yùn)行結(jié)果:

在上述 XML 配置文件中,使用 <bean> 標(biāo)簽向 Spring IOC 容器中注冊(cè)了一個(gè) Lion 類(lèi)型的 Bean。
<bean>標(biāo)簽的核心作用
<bean> 標(biāo)簽用于聲明一個(gè)由 Spring 容器管理的對(duì)象,其中:
class屬性用于指定 Bean 的全限定類(lèi)名;Spring 在容器啟動(dòng)時(shí),會(huì)根據(jù)該配置創(chuàng)建并管理該對(duì)象的生命周期。
init-method與destroy-method的含義
在 <bean> 標(biāo)簽中,定義了兩個(gè)非常關(guān)鍵的屬性:
init-method="init" destroy-method="destroy"
init-method指定 Bean 初始化完成后要執(zhí)行的方法 → 當(dāng) Bean 被實(shí)例化并完成屬性注入后,Spring 會(huì)自動(dòng)調(diào)用init()方法destroy-method指定 Bean 被銷(xiāo)毀前要執(zhí)行的方法 → 當(dāng) IOC 容器關(guān)閉時(shí),Spring 會(huì)自動(dòng)調(diào)用destroy()方法
這兩個(gè)方法無(wú)需實(shí)現(xiàn)任何接口,只需在類(lèi)中定義普通方法即可。
property標(biāo)簽的作用
<property name="name" value="simba"/>
用于給 Bean 的屬性進(jìn)行依賴(lài)注入
Spring 在調(diào)用初始化方法之前,會(huì)先完成屬性賦值
這也是為什么在
init()方法中可以直接使用name屬性
控制臺(tái)輸出與生命周期驗(yàn)證
當(dāng) IOC 容器啟動(dòng)時(shí),如果在 init() 方法中輸出類(lèi)似:
simba has been initialized...
說(shuō)明 初始化方法已成功被 Spring 調(diào)用。
當(dāng)程序執(zhí)行 context.close() 或應(yīng)用正常關(guān)閉時(shí),如果看到:
simba has been destroyed...
說(shuō)明 銷(xiāo)毀方法已成功被 Spring 調(diào)用。
生命周期整體過(guò)程總結(jié)
整個(gè) Bean 生命周期可以總結(jié)為:
IOC 容器啟動(dòng)
Spring 根據(jù) XML 配置創(chuàng)建 Bean 實(shí)例
完成屬性注入(如
name = simba)調(diào)用
init-method指定的初始化方法Bean 進(jìn)入可使用狀態(tài)
IOC 容器關(guān)閉
調(diào)用
destroy-method指定的銷(xiāo)毀方法所有 Bean 銷(xiāo)毀完成,IOC 容器關(guān)閉
小結(jié)
在 IOC 容器初始化完成之前,Bean 默認(rèn)已經(jīng)被創(chuàng)建并完成了初始化操作; 當(dāng)容器關(guān)閉時(shí),Spring 會(huì)先銷(xiāo)毀所有受其管理的 Bean,最后再銷(xiāo)毀整個(gè) IOC 容器。
通過(guò)這個(gè)簡(jiǎn)單的 XML 示例,可以直觀地看到 Spring Bean 的創(chuàng)建、初始化和銷(xiāo)毀全過(guò)程。 在實(shí)際開(kāi)發(fā)中,我們可以根據(jù)需要,在這些生命周期回調(diào)方法中完成諸如資源初始化、連接建立、資源釋放等操作。
2.2 從配置類(lèi)注解配置創(chuàng)建Bean看生命周期
這里再創(chuàng)建一個(gè)類(lèi)Elephant和上面對(duì)比
package com.example.demo.bean;
?
public class Elephant {
?
private String name;
?
public void setName(String name) {
this.name = name;
}
?
public void init() {
System.out.println(name + " has been initialized...");
}
?
public void destroy() {
System.out.println(name + " has been destroyed...");
}
}對(duì)于注解,@Bean注解中也有類(lèi)似的屬性:initMethod和destroyMethod,這兩個(gè)屬性的作用與XML配置中的相同。
package com.example.demo.configuration;
?
import com.example.demo.bean.Elephant;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
?
@Configuration
@ImportResource("classpath:applicationContext.xml")
public class AnimalConfig {
?
@Bean(initMethod = "init", destroyMethod = "destroy")
public Elephant elephant() {
Elephant elephant = new Elephant();
elephant.setName("Dumbo");
return elephant;
}
}這里用@ImportResource("classpath:applicationContext.xml")引入xml配置創(chuàng)建Bean進(jìn)行對(duì)比。
主程序改為如下:
package com.example.demo.application;
?
import com.example.demo.configuration.AnimalConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
?
@ComponentScan("com.example")public class DemoApplication {
public static void main(String[] args) {
System.out.println("Spring容器初始化開(kāi)始");
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AnimalConfig.class);
System.out.println("Spring容器初始化完成。");
System.out.println("==================");
System.out.println("Spring容器準(zhǔn)備關(guān)閉");
context.close();
System.out.println("Spring容器已關(guān)閉。");
}
}運(yùn)行結(jié)果:

注意:在Spring中,如果在Java配置中定義了一個(gè)Bean,并在XML中定義了一個(gè)相同id或name的Bean,那么最后注冊(cè)的那個(gè)Bean會(huì)覆蓋之前注冊(cè)的,這取決于配置文件加載順序,無(wú)論在Java配置中還是XML配置中定義的initMethod或destroyMethod,最后生效的總是后加載的配置中定義的。
“init-method”是指定初始化回調(diào)方法的屬性的統(tǒng)稱(chēng),無(wú)論它是在XML配置還是Java配置中使用。同樣地,“destroy-method”是指定銷(xiāo)毀回調(diào)方法的屬性的統(tǒng)稱(chēng)。后文我們講解多種聲明的周期共存的時(shí)候,將延續(xù)這種說(shuō)法。
2.3 初始化方法與銷(xiāo)毀方法的特性說(shuō)明
在 Spring 框架中,為 Bean 配置初始化方法和銷(xiāo)毀方法時(shí),需要遵循一定的規(guī)范,否則 Spring 可能無(wú)法按預(yù)期觸發(fā)生命周期回調(diào)。下面從幾個(gè)常見(jiàn)特性出發(fā),對(duì)這些方法的使用規(guī)則進(jìn)行說(shuō)明,并配合示例進(jìn)行解釋。
方法的訪問(wèn)權(quán)限不受限制
初始化方法和銷(xiāo)毀方法在訪問(wèn)權(quán)限上沒(méi)有強(qiáng)制要求,無(wú)論是 public、protected、private,還是默認(rèn)(包私有)權(quán)限,Spring 都可以正常調(diào)用。
這是因?yàn)?Spring 底層是通過(guò)反射機(jī)制來(lái)執(zhí)行這些方法的,因此不會(huì)受到 Java 訪問(wèn)控制符的限制。
示例:
public class MyBean {
private void init() {// 初始化代碼
}
}在上述示例中,即使 init() 方法被定義為 private,Spring 依然能夠在 Bean 初始化階段正確調(diào)用該方法。
方法通常不應(yīng)包含參數(shù)
在默認(rèn)情況下,Spring 并不知道應(yīng)該向初始化或銷(xiāo)毀方法傳遞哪些參數(shù),因此這些方法通常不應(yīng)定義參數(shù)。
示例:
public class MyBean {
public void init() {// 初始化代碼
}
}需要注意的是,Spring 并非完全禁止帶參數(shù)的方法。如果方法定義了參數(shù),Spring 容器會(huì)嘗試?yán)米詣?dòng)裝配機(jī)制,按照類(lèi)型或名稱(chēng)為參數(shù)匹配對(duì)應(yīng)的 Bean 并進(jìn)行注入。
但如果:
無(wú)法找到匹配的 Bean,或
存在多個(gè)符合條件的 Bean,
那么 Spring 會(huì)在應(yīng)用啟動(dòng)階段拋出異常,導(dǎo)致容器初始化失敗。因此在實(shí)際開(kāi)發(fā)中,一般不推薦在初始化或銷(xiāo)毀方法中使用參數(shù)。
方法不應(yīng)有返回值
初始化方法和銷(xiāo)毀方法的返回值對(duì) Spring 容器來(lái)說(shuō)沒(méi)有任何實(shí)際意義,因此這些方法通常應(yīng)定義為 void 類(lèi)型。
示例:
public class MyBean {
public void init() {// 初始化代碼
}
}即使將方法聲明為帶返回值的形式,例如:
public String init() {return "success";
}Spring 也會(huì)直接忽略該返回值,不會(huì)對(duì)其進(jìn)行任何處理。
方法允許拋出異常
在初始化或銷(xiāo)毀過(guò)程中,如果發(fā)生錯(cuò)誤,這些方法是允許拋出異常的,用于向 Spring 容器明確地反饋失敗信息。
示例:
public class MyBean {
public void init() throws Exception {// 初始化代碼
if (somethingGoesWrong) {throw new Exception("Initialization failed.");
}
}
}當(dāng)初始化或銷(xiāo)毀方法拋出異常(無(wú)論是檢查型異常還是運(yùn)行時(shí)異常)時(shí),Spring 容器都會(huì)捕獲該異常,并將其封裝為 BeanCreationException 或 BeanDestructionException 后再次拋出,從而導(dǎo)致 Bean 的創(chuàng)建或銷(xiāo)毀過(guò)程失敗。
方法不應(yīng)定義為靜態(tài)方法
初始化方法和銷(xiāo)毀方法本質(zhì)上是作用于 Bean 實(shí)例生命周期的,而靜態(tài)方法屬于類(lèi)級(jí)別,不依賴(lài)于具體實(shí)例,因此并不適合作為生命周期回調(diào)方法。
示例:
public class MyBean {
public static void init() {// 初始化代碼
}
}如果將初始化或銷(xiāo)毀方法定義為 static,Spring 并不會(huì)立即報(bào)錯(cuò),但這種做法違背了生命周期方法應(yīng)當(dāng)綁定到 Bean 實(shí)例的設(shè)計(jì)原則,也不符合實(shí)際使用場(chǎng)景。
2.4 探究Bean的初始化流程順序
在上面的代碼中,我們可以看出Bean在IOC容器初始化階段就已經(jīng)創(chuàng)建并初始化了,那么每個(gè)Bean的初始化動(dòng)作又是如何進(jìn)行的呢?我們修改一下Lion,在構(gòu)造方法和setName方法中加入控制臺(tái)打印,這樣在調(diào)用這些方法時(shí),會(huì)在控制臺(tái)上得到反饋。
package com.example.demo.bean;
?
public class Lion {
?
private String name;
?
public Lion() {
System.out.println("Lion's constructor is called...");
}
?
public void setName(String name) {
System.out.println("setName method is called...");
this.name = name;
}
?
public void init() {
System.out.println(name + " has been initialized...");
}
?
public void destroy() {
System.out.println(name + " has been destroyed...");
}
}我們重新運(yùn)行主程序:
@ComponentScan("com.example")public class DemoApplication {
public static void main(String[] args) {
System.out.println("Spring容器初始化開(kāi)始");
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println("Spring容器初始化完成。");
System.out.println("==================");
System.out.println("Spring容器準(zhǔn)備關(guān)閉");
context.close();
System.out.println("Spring容器已關(guān)閉。");
}
}運(yùn)行結(jié)果

3. @PostConstruct和@PreDestroy
在JSR250規(guī)范中,有兩個(gè)與Bean生命周期相關(guān)的注解,即@PostConstruct和@PreDestroy。這兩個(gè)注解對(duì)應(yīng)了Bean的初始化和銷(xiāo)毀階段。
@PostConstruct注解標(biāo)記的方法會(huì)在bean屬性設(shè)置完畢后(即完成依賴(lài)注入),但在bean對(duì)外暴露(即可以被其他bean引用)之前被調(diào)用,這個(gè)時(shí)機(jī)通常用于完成一些初始化工作。
@PreDestroy注解標(biāo)記的方法會(huì)在Spring容器銷(xiāo)毀bean之前調(diào)用,這通常用于釋放資源。
3.1 示例:@PostConstruct和@PreDestroy的使用
我們這里還是用Lion類(lèi)來(lái)創(chuàng)建這個(gè)例子,將Lion類(lèi)修改為使用@PostConstruct和@PreDestroy注解
package com.example.demo.bean;
?
import org.springframework.stereotype.Component;
?
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
?
@Componentpublic class Lion {
?
private String name;
?
public void setName(String name) {
this.name = name;
}
?
@PostConstructpublic void init() {
System.out.println("Lion is going through init.");
}
?
@PreDestroypublic void destroy() {
System.out.println("Lion is going through destroy.");
}
?
@Overridepublic String toString() {
return "Lion{" + "name=" + name + '}';
}
}給Lion類(lèi)加上@Component注解,讓IOC容器去管理這個(gè)類(lèi),我們這里就不把Elephant類(lèi)加進(jìn)來(lái)增加理解難度了。
被 @PostConstruct 和 @PreDestroy 注解標(biāo)注的方法與 init-method / destroy-method 方法的初始化和銷(xiāo)毀的要求是一樣的,訪問(wèn)修飾符沒(méi)有限制,private也可以。
我們可以注釋掉之前的配置類(lèi)和XML配置,因?yàn)楹瓦@里的例子沒(méi)有關(guān)系,我們來(lái)看看主程序:
package com.example.demo.application;
?
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
?
public class DemoApplication {
public static void main(String[] args) {
System.out.println("Spring容器初始化開(kāi)始");
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.example.demo.bean");
System.out.println("Spring容器初始化完成。");
System.out.println("==================");
System.out.println("Spring容器準(zhǔn)備關(guān)閉");
context.close();
System.out.println("Spring容器已關(guān)閉。");
}
}運(yùn)行結(jié)果

這里可以看到@PostConstruct和@PreDestroy注解正確地應(yīng)用在了Lion的初始化和銷(xiāo)毀過(guò)程中。
注意: @PostConstruct和@PreDestroy可用于任何Java類(lèi),初始化和銷(xiāo)毀方法與init-method和destroy-method類(lèi)似,但也有一定的區(qū)別。
這些方法必須是非靜態(tài)的,否則
Spring容器在啟動(dòng)或銷(xiāo)毀時(shí)會(huì)拋出BeanCreationException或BeanDestructionException異常,導(dǎo)致創(chuàng)建或銷(xiāo)毀bean失敗。這些方法推薦無(wú)參數(shù),與
init-method和destroy-method的行為類(lèi)似。這些方法推薦無(wú)返回值,與
init-method和destroy-method的行為類(lèi)似。可以是任何訪問(wèn)級(jí)別,與
init-method和destroy-method的行為類(lèi)似。可以拋出異常,與
init-method和destroy-method的行為類(lèi)似。不能被final修飾,如果使用
final修飾這兩個(gè)注解的方法,在編譯時(shí)不會(huì)報(bào)錯(cuò),可以正常編譯。但是在運(yùn)行時(shí),Spring容器在解析和設(shè)置注解時(shí),會(huì)嘗試使用CGLIB或JDK動(dòng)態(tài)代理生成子類(lèi),由于方法被final修飾,子類(lèi)無(wú)法覆蓋該方法,所以Spring容器會(huì)拋出異常,表示無(wú)法為生命周期方法生成代理,這會(huì)導(dǎo)致標(biāo)注了final的生命周期方法無(wú)法被Spring調(diào)用。
注意:init-method和destroy-method方法被final修飾也無(wú)影響,因?yàn)?code>Spring通過(guò)反射機(jī)制來(lái)調(diào)用init-method和destroy-method,不需要生成代理子類(lèi),并沒(méi)有試圖覆蓋這些方法。不過(guò)生命周期方法都不被建議設(shè)計(jì)為final的,這需要注意。
3.2 初始化和銷(xiāo)毀——注解和init-method共存對(duì)比
@PostConstruct和@PreDestroy注解與init-method/destroy-method屬性如何共存呢?我們來(lái)看看
我們只用Lion類(lèi)來(lái)舉例子,在Lion類(lèi)中添加新的open()和close()方法
需要的全部代碼如下:
Lion.java
package com.example.demo.bean;
?
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
?
?
public class Lion {
?
private String name;
?
public Lion() {
System.out.println("Lion構(gòu)造器");
}
?
public void setName(String name) {
System.out.println("Lion設(shè)置name");
this.name = name;
}
?
public void open() {
System.out.println("配置類(lèi)initMethod - 打開(kāi)Lion。。。");
}
?
public void close() {
System.out.println("配置類(lèi)destroyMethod - 關(guān)閉Lion。。。");
}
?
@PostConstruct
public void init() {
System.out.println("@PostConstruct - Lion正在進(jìn)行初始化。。。");
}
?
@PreDestroy
public void destroy() {
System.out.println("@PreDestroy - Lion正在進(jìn)行銷(xiāo)毀。。。");
}
?
@Override
public String toString() {
return "Lion{" + "name=" + name + '}';
}
}配置類(lèi)AnimalConfig.java
package com.example.demo.configuration;
?
import com.example.demo.bean.Lion;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
?
@Configuration
public class AnimalConfig {
?
@Bean(initMethod = "open", destroyMethod = "close")
public Lion lion() {
return new Lion();
}
}主程序
package com.example.demo.application;
?
import com.example.demo.configuration.AnimalConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
?
public class DemoApplication {
public static void main(String[] args) {
System.out.println("Spring容器初始化開(kāi)始");
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AnimalConfig.class);
System.out.println("Spring容器初始化完成。");
System.out.println("==================");
System.out.println("Spring容器準(zhǔn)備關(guān)閉");
context.close();
System.out.println("Spring容器已關(guān)閉。");
}
}運(yùn)行結(jié)果

這里可以看到@PostConstruct和@PreDestroy注解的優(yōu)先級(jí)始終高于配置類(lèi)中@Bean注解的initMethod和destroyMethod屬性。
4. 實(shí)現(xiàn)InitializingBean和DisposableBean接口
這兩個(gè)接口是 Spring 預(yù)定義的兩個(gè)關(guān)于生命周期的接口。他們被觸發(fā)的時(shí)機(jī)與上文中的 init-method / destroy-method 以及 JSR250 規(guī)范的注解相同,都是在 Bean 的初始化和銷(xiāo)毀階段回調(diào)的。下面演示如何使用這兩個(gè)接口。
4.1 示例:實(shí)現(xiàn)InitializingBean和DisposableBean接口
創(chuàng)建Bean,我們讓Lion類(lèi)實(shí)現(xiàn)這兩個(gè)接口:
Lion.java
package com.example.demo.bean;
?
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
?
?
@Component
public class Lion implements InitializingBean, DisposableBean {
?
private Integer energy;
?
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("獅子已經(jīng)充滿能量。。。");
this.energy = 100;
}
?
@Override
public void destroy() throws Exception {
System.out.println("獅子已經(jīng)消耗完所有能量。。。");
this.energy = 0;
}
?
@Override
public String toString() {
return "Lion{" + "energy=" + energy + '}';
}
} InitializingBean接口只有一個(gè)方法:afterPropertiesSet()。在Spring框架中,當(dāng)一個(gè)bean的所有屬性都已經(jīng)被設(shè)置完畢后,這個(gè)方法就會(huì)被調(diào)用。也就是說(shuō),這個(gè)bean一旦被初始化,Spring就會(huì)調(diào)用這個(gè)方法。我們可以在bean的所有屬性被設(shè)置后,進(jìn)行一些自定義的初始化工作。
DisposableBean接口也只有一個(gè)方法:destroy()。當(dāng)Spring容器關(guān)閉并銷(xiāo)毀bean時(shí),這個(gè)方法就會(huì)被調(diào)用。我們可以在bean被銷(xiāo)毀前,進(jìn)行一些清理工作。
主程序:
package com.example.demo.application;
?
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
?
public class DemoApplication {
public static void main(String[] args) {
System.out.println("Spring容器初始化開(kāi)始");
AnnotationConfigApplicationContext context
= new AnnotationConfigApplicationContext("com.example.demo.bean");
System.out.println("Spring容器初始化完成。");
System.out.println("==================");
System.out.println("Spring容器準(zhǔn)備關(guān)閉");
context.close();
System.out.println("Spring容器已關(guān)閉。");
}
}
?運(yùn)行結(jié)果:

4.2 三種生命周期并存
在Spring框架中,控制Bean生命周期的三種方式是:
使用
Spring的init-method和destroy-method(在XML配置或者Java配置中自定義的初始化和銷(xiāo)毀方法);使用
JSR-250規(guī)范的@PostConstruct和@PreDestroy注解;實(shí)現(xiàn)
Spring的InitializingBean和DisposableBean接口。
接下來(lái)我們測(cè)試一下,一個(gè)Bean同時(shí)定義init-method、destroy-method方法,使用@PostConstruct、@PreDestroy注解,以及實(shí)現(xiàn)InitializingBean、DisposableBean接口,執(zhí)行順序是怎樣的。
我們創(chuàng)建一個(gè)新的類(lèi)Lion2,并同時(shí)進(jìn)行三種方式的生命周期控制:
需要運(yùn)行的全部代碼如下:
package com.example.demo.bean;
?
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
?
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
?
@Component
public class Lion2 implements InitializingBean, DisposableBean {
?
private Integer energy;
?
public void open() {
System.out.println("init-method - 獅子開(kāi)始行動(dòng)。。。");
}
?
public void close() {
System.out.println("destroy-method - 獅子結(jié)束行動(dòng)。。。");
}
?
@PostConstruct
public void gainEnergy() {
System.out.println("@PostConstruct - 獅子已經(jīng)充滿能量。。。");
this.energy = 100;
}
?
@PreDestroy
public void loseEnergy() {
System.out.println("@PreDestroy - 獅子已經(jīng)消耗完所有能量。。。");
this.energy = 0;
}
?
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean - 獅子準(zhǔn)備行動(dòng)。。。");
}
?
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean - 獅子行動(dòng)結(jié)束。。。");
}
}
?接著,我們注冊(cè)Lion2:
package com.example.demo.configuration;
?
import com.example.demo.bean.Lion2;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
?
@Configuration
public class AnimalConfig {
?
@Bean(initMethod = "open", destroyMethod = "close")
public Lion2 lion2() {
return new Lion2();
}
}然后讓注解 IOC 容器驅(qū)動(dòng)這個(gè)配置類(lèi),主程序如下:
package com.example.demo.application;
?
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
?
public class DemoApplication {
public static void main(String[] args) {
System.out.println("Spring容器初始化開(kāi)始");
AnnotationConfigApplicationContext context
= new AnnotationConfigApplicationContext("com.example.demo");
System.out.println("Spring容器初始化完成。");
System.out.println("==================");
System.out.println("Spring容器準(zhǔn)備關(guān)閉");
context.close();
System.out.println("Spring容器已關(guān)閉。");
}
}運(yùn)行結(jié)果:

從上面的結(jié)果,我們可以得出以下結(jié)論,在Spring框架中單實(shí)例Bean的初始化和銷(xiāo)毀過(guò)程有這樣的執(zhí)行順序:
初始化順序:@PostConstruct → InitializingBean → init-method
銷(xiāo)毀順序:@PreDestroy → DisposableBean → destroy-method
在初始化Bean時(shí),@PostConstruct注解方法會(huì)首先被執(zhí)行,然后是實(shí)現(xiàn)InitializingBean接口的afterPropertiesSet方法,最后是init-method指定的方法。
在銷(xiāo)毀Bean時(shí),@PreDestroy注解方法會(huì)首先被執(zhí)行,然后是實(shí)現(xiàn)DisposableBean接口的destroy方法,最后是destroy-method指定的方法
結(jié)合前面說(shuō)過(guò)的屬性賦值(構(gòu)造器方法和setter方法),簡(jiǎn)單總結(jié)一下Spring Bean生命周期的流程:
實(shí)例化(通過(guò)構(gòu)造器方法);
設(shè)置
Bean的屬性(通過(guò)setter方法);調(diào)用
Bean的初始化方法(@PostConstruct、afterPropertiesSet方法或者init-method指定的方法);Bean可以被應(yīng)用程序使用;當(dāng)容器關(guān)閉時(shí),調(diào)用
Bean的銷(xiāo)毀方法(@PreDestroy、destroy方法或者destroy-method指定的方法)。
5. 原型Bean的生命周期
原型Bean的創(chuàng)建和初始化過(guò)程與單例Bean類(lèi)似,但由于原型Bean的性質(zhì),其生命周期與IOC容器的生命周期并不相同。
這里展示一下需要的全部代碼。
Lion2.java
package com.example.demo.bean;
?
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
?
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
?
public class Lion2 implements InitializingBean, DisposableBean {
?
private Integer energy;
?
public void roar() {
System.out.println("The lion is roaring...");
}
?
public void rest() {
System.out.println("The lion is resting...");
}
?
@PostConstruct
public void gainEnergy() {
System.out.println("@PostConstruct - 獅子已經(jīng)充滿能量。。。");
this.energy = 100;
}
?
@PreDestroy
public void loseEnergy() {
System.out.println("@PreDestroy - 獅子已經(jīng)消耗完所有能量。。。");
this.energy = 0;
}
?
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean - 獅子準(zhǔn)備行動(dòng)。。。");
}
?
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean - 獅子行動(dòng)結(jié)束。。。");
}
}然后在Spring的Java配置中聲明并設(shè)定其為原型Bean
package com.example.demo.configuration;
?
import com.example.demo.bean.Lion2;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
?
@Configuration
public class PrototypeLifecycleConfiguration {
?
@Bean(initMethod = "roar", destroyMethod = "rest")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Lion2 lion() {
return new Lion2();
}
}
? 如果我們只是啟動(dòng)了IOC容器,但并未請(qǐng)求Lion2的實(shí)例,Lion Bean的初始化不會(huì)立刻發(fā)生。也就是說(shuō),原型Bean不會(huì)隨著IOC容器的啟動(dòng)而初始化。以下是啟動(dòng)容器但并未請(qǐng)求Bean的代碼:
package com.example.demo.application;
?
import com.example.demo.configuration.PrototypeLifecycleConfiguration;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
?
public class DemoApplication {
public static void main(String[] args) {
System.out.println("Spring容器初始化開(kāi)始");
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
PrototypeLifecycleConfiguration.class);
}
}
?運(yùn)行結(jié)果:

當(dāng)我們明確請(qǐng)求一個(gè)Lion2的實(shí)例時(shí),我們會(huì)看到所有的初始化方法按照預(yù)定的順序執(zhí)行,這個(gè)順序跟單例Bean完全一致:
package com.example.demo.application;
?
import com.example.demo.bean.Lion2;
import com.example.demo.configuration.PrototypeLifecycleConfiguration;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
?
public class DemoApplication {
?
public static void main(String[] args) {
?
System.out.println("Spring容器初始化開(kāi)始");
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
PrototypeLifecycleConfiguration.class);
System.out.println("Ready to get a Lion instance...");
Lion2 lion = context.getBean(Lion2.class);
System.out.println("A Lion instance has been fetched...");
System.out.println("Lion instance is no longer needed, preparing to destroy...");
context.getBeanFactory().destroyBean(lion);
System.out.println("Lion instance has been destroyed...");
}
}運(yùn)行結(jié)果:
可以看出:
在對(duì) prototype Bean(原型 Bean) 與 singleton Bean(單例 Bean) 的三種銷(xiāo)毀方式進(jìn)行對(duì)比實(shí)驗(yàn)后可以發(fā)現(xiàn): 當(dāng)通過(guò) IOC 容器的 destroyBean() 方法手動(dòng)銷(xiāo)毀原型 Bean 時(shí),只有使用 @PreDestroy 注解標(biāo)注的方法以及實(shí)現(xiàn)了 DisposableBean 接口的 destroy() 方法會(huì)被正常調(diào)用,而通過(guò) destroy-method 屬性指定的自定義銷(xiāo)毀方法并不會(huì)被執(zhí)行。
由此可以得出結(jié)論:在原型 Bean 的銷(xiāo)毀過(guò)程中,Spring 并不會(huì)觸發(fā)由 destroy-method 配置的自定義銷(xiāo)毀邏輯。這也說(shuō)明,destroy-method 方式在原型 Bean 場(chǎng)景下存在一定的局限性。
因此,如果 Bean 在銷(xiāo)毀階段包含關(guān)鍵的資源釋放或清理邏輯,更穩(wěn)妥的做法是將這些邏輯放在 @PreDestroy 注解的方法中,或者實(shí)現(xiàn) DisposableBean 接口的 destroy() 方法,以確保在原型 Bean 被銷(xiāo)毀時(shí)能夠得到正確執(zhí)行。
6. Spring中控制Bean生命周期的三種方式總結(jié)
| 執(zhí)行順序 | 代碼依賴(lài)性 | 容器支持 | 單實(shí)例Bean | 原型Bean | |
|---|---|---|---|---|---|
| init-method & destroy-method | 最后 | 較低(依賴(lài)于Spring Bean配置,不侵入業(yè)務(wù)代碼) | xml、注解原生支持 | √ | 只支持 init-method |
| @PostConstruct & @PreDestroy | 最先 | 中等(需要在業(yè)務(wù)代碼中添加JSR規(guī)范的注解) | 注解原生支持,xml需開(kāi)啟注解驅(qū)動(dòng) | √ | √ |
| InitializingBean & DisposableBean | 中間 | 較高(需要業(yè)務(wù)代碼實(shí)現(xiàn)Spring特定接口) | xml、注解原生支持 | √ | √ |
7. 總結(jié)
到此這篇關(guān)于Spring中Bean的生命周期原理解析:實(shí)例化、屬性賦值、初始化、運(yùn)行期和銷(xiāo)毀的文章就介紹到這了,更多相關(guān)Spring中Bean的生命周期原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
超詳細(xì)講解Java秒殺項(xiàng)目用戶(hù)驗(yàn)證模塊的實(shí)現(xiàn)
這是一個(gè)主要使用java開(kāi)發(fā)的秒殺系統(tǒng),項(xiàng)目比較大,所以本篇只實(shí)現(xiàn)了用戶(hù)驗(yàn)證模塊,代碼非常詳盡,感興趣的朋友快來(lái)看看2022-03-03
Java基礎(chǔ)之List內(nèi)元素的排序性能對(duì)比
這篇文章主要介紹了Java基礎(chǔ)之List內(nèi)元素的排序性能對(duì)比,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java基礎(chǔ)的小伙伴們有非常好的幫助,需要的朋友可以參考下2021-04-04
Java Process與Runtime()的使用及調(diào)用cmd命令阻塞的解決方案
這篇文章主要介紹了Java Process與Runtime()的使用及調(diào)用cmd命令阻塞的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06
Java實(shí)現(xiàn)HTML轉(zhuǎn)PDF的兩款工具(itext-pdfhtml和x-easypdf)介紹與使用
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)HTML轉(zhuǎn)PDF的兩款工具(itext-pdfhtml和x-easypdf)的介紹與使用,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解下2025-11-11
Java責(zé)任鏈模式的實(shí)現(xiàn)方法詳解
責(zé)任鏈模式是一種設(shè)計(jì)模式,在責(zé)任鏈模式里,很多對(duì)象由每一個(gè)對(duì)象對(duì)其下家的引用而連接起來(lái)形成一條鏈,這篇文章主要介紹了Java責(zé)任鏈模式實(shí)現(xiàn)方法的相關(guān)資料,需要的朋友可以參考下2025-07-07
Java經(jīng)典排序算法之快速排序代碼實(shí)例
這篇文章主要介紹了Java經(jīng)典排序算法之快速排序代碼實(shí)例,快速排序?qū)崿F(xiàn)的思想是指通過(guò)一趟排序?qū)⒁判虻臄?shù)據(jù)分割成獨(dú)立的兩部分,其中一部分的所有數(shù)據(jù)都比另外一部分的所有數(shù)據(jù)都要小,然后再按此方法對(duì)這兩部分?jǐn)?shù)據(jù)分別進(jìn)行快速排序,需要的朋友可以參考下2023-10-10
SpringBoot錯(cuò)誤處理機(jī)制以及自定義異常處理詳解
這篇文章主要為大家詳細(xì)介紹了SpringBoot錯(cuò)誤處理機(jī)制以及自定義異常處理,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-05-05

