Spring中Bean的生命周期原理解析:實例化、屬性賦值、初始化、運行期和銷毀
1. 理解Bean的生命周期
在Spring IOC容器中,Bean的生命周期大致如下:
實例化:當啟動
Spring應(yīng)用時,IOC容器就會為在配置文件中聲明的每個<bean>創(chuàng)建一個實例。屬性賦值:實例化后,
Spring就通過反射機制給Bean的屬性賦值。調(diào)用初始化方法:如果
Bean配置了初始化方法,Spring就會調(diào)用它。初始化方法是在Bean創(chuàng)建并賦值之后調(diào)用,可以在這個方法里面寫一些業(yè)務(wù)處理代碼或者做一些初始化的工作。Bean運行期:此時,Bean已經(jīng)準備好被程序使用了,它已經(jīng)被初始化并賦值完成。應(yīng)用程序關(guān)閉:當關(guān)閉
IOC容器時,Spring會處理配置了銷毀方法的Bean。調(diào)用銷毀方法:如果
Bean配置了銷毀方法,Spring會在所有Bean都已經(jīng)使用完畢,且IOC容器關(guān)閉之前調(diào)用它,可以在銷毀方法里面做一些資源釋放的工作,比如關(guān)閉連接、清理緩存等。
這就是Spring IOC容器管理Bean的生命周期,幫助我們管理對象的創(chuàng)建和銷毀,以及在適當?shù)臅r機做適當?shù)氖虑椤?/p>
我們可以將生命周期的觸發(fā)稱為回調(diào),因為生命周期的方法是我們自己定義的,但方法的調(diào)用是由框架內(nèi)部幫我們完成的,所以可以稱之為“回調(diào)”。
2. 理解init-method和destroy-method
讓我們先了解一種最容易理解的生命周期階段:初始化和銷毀方法。這些方法可以在Bean的初始化和銷毀階段起作用,我們通過示例來演示這種方式。
為了方便演示XML和注解的方式,接下來我們會創(chuàng)建兩個類來分別進行演示,分別為Lion和Elephant,讓我們一步一步對比觀察。
2.1 從XML配置創(chuàng)建Bean看生命周期
先創(chuàng)建一個類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>標簽來注冊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容器初始化開始");
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println("Spring容器初始化完成。");
System.out.println("==================");
System.out.println("Spring容器準備關(guān)閉");
context.close();
System.out.println("Spring容器已關(guān)閉。");
}
}運行結(jié)果:

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

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

3. @PostConstruct和@PreDestroy
在JSR250規(guī)范中,有兩個與Bean生命周期相關(guān)的注解,即@PostConstruct和@PreDestroy。這兩個注解對應(yīng)了Bean的初始化和銷毀階段。
@PostConstruct注解標記的方法會在bean屬性設(shè)置完畢后(即完成依賴注入),但在bean對外暴露(即可以被其他bean引用)之前被調(diào)用,這個時機通常用于完成一些初始化工作。
@PreDestroy注解標記的方法會在Spring容器銷毀bean之前調(diào)用,這通常用于釋放資源。
3.1 示例:@PostConstruct和@PreDestroy的使用
我們這里還是用Lion類來創(chuàng)建這個例子,將Lion類修改為使用@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類加上@Component注解,讓IOC容器去管理這個類,我們這里就不把Elephant類加進來增加理解難度了。
被 @PostConstruct 和 @PreDestroy 注解標注的方法與 init-method / destroy-method 方法的初始化和銷毀的要求是一樣的,訪問修飾符沒有限制,private也可以。
我們可以注釋掉之前的配置類和XML配置,因為和這里的例子沒有關(guā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容器初始化開始");
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.example.demo.bean");
System.out.println("Spring容器初始化完成。");
System.out.println("==================");
System.out.println("Spring容器準備關(guān)閉");
context.close();
System.out.println("Spring容器已關(guān)閉。");
}
}運行結(jié)果

這里可以看到@PostConstruct和@PreDestroy注解正確地應(yīng)用在了Lion的初始化和銷毀過程中。
注意: @PostConstruct和@PreDestroy可用于任何Java類,初始化和銷毀方法與init-method和destroy-method類似,但也有一定的區(qū)別。
這些方法必須是非靜態(tài)的,否則
Spring容器在啟動或銷毀時會拋出BeanCreationException或BeanDestructionException異常,導致創(chuàng)建或銷毀bean失敗。這些方法推薦無參數(shù),與
init-method和destroy-method的行為類似。這些方法推薦無返回值,與
init-method和destroy-method的行為類似。可以是任何訪問級別,與
init-method和destroy-method的行為類似。可以拋出異常,與
init-method和destroy-method的行為類似。不能被final修飾,如果使用
final修飾這兩個注解的方法,在編譯時不會報錯,可以正常編譯。但是在運行時,Spring容器在解析和設(shè)置注解時,會嘗試使用CGLIB或JDK動態(tài)代理生成子類,由于方法被final修飾,子類無法覆蓋該方法,所以Spring容器會拋出異常,表示無法為生命周期方法生成代理,這會導致標注了final的生命周期方法無法被Spring調(diào)用。
注意:init-method和destroy-method方法被final修飾也無影響,因為Spring通過反射機制來調(diào)用init-method和destroy-method,不需要生成代理子類,并沒有試圖覆蓋這些方法。不過生命周期方法都不被建議設(shè)計為final的,這需要注意。
3.2 初始化和銷毀——注解和init-method共存對比
@PostConstruct和@PreDestroy注解與init-method/destroy-method屬性如何共存呢?我們來看看
我們只用Lion類來舉例子,在Lion類中添加新的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("配置類initMethod - 打開Lion。。。");
}
?
public void close() {
System.out.println("配置類destroyMethod - 關(guān)閉Lion。。。");
}
?
@PostConstruct
public void init() {
System.out.println("@PostConstruct - Lion正在進行初始化。。。");
}
?
@PreDestroy
public void destroy() {
System.out.println("@PreDestroy - Lion正在進行銷毀。。。");
}
?
@Override
public String toString() {
return "Lion{" + "name=" + name + '}';
}
}配置類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容器初始化開始");
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AnimalConfig.class);
System.out.println("Spring容器初始化完成。");
System.out.println("==================");
System.out.println("Spring容器準備關(guān)閉");
context.close();
System.out.println("Spring容器已關(guān)閉。");
}
}運行結(jié)果

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

4.2 三種生命周期并存
在Spring框架中,控制Bean生命周期的三種方式是:
使用
Spring的init-method和destroy-method(在XML配置或者Java配置中自定義的初始化和銷毀方法);使用
JSR-250規(guī)范的@PostConstruct和@PreDestroy注解;實現(xiàn)
Spring的InitializingBean和DisposableBean接口。
接下來我們測試一下,一個Bean同時定義init-method、destroy-method方法,使用@PostConstruct、@PreDestroy注解,以及實現(xiàn)InitializingBean、DisposableBean接口,執(zhí)行順序是怎樣的。
我們創(chuàng)建一個新的類Lion2,并同時進行三種方式的生命周期控制:
需要運行的全部代碼如下:
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 - 獅子開始行動。。。");
}
?
public void close() {
System.out.println("destroy-method - 獅子結(jié)束行動。。。");
}
?
@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 - 獅子準備行動。。。");
}
?
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean - 獅子行動結(jié)束。。。");
}
}
?接著,我們注冊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ū)動這個配置類,主程序如下:
package com.example.demo.application;
?
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
?
public class DemoApplication {
public static void main(String[] args) {
System.out.println("Spring容器初始化開始");
AnnotationConfigApplicationContext context
= new AnnotationConfigApplicationContext("com.example.demo");
System.out.println("Spring容器初始化完成。");
System.out.println("==================");
System.out.println("Spring容器準備關(guān)閉");
context.close();
System.out.println("Spring容器已關(guān)閉。");
}
}運行結(jié)果:

從上面的結(jié)果,我們可以得出以下結(jié)論,在Spring框架中單實例Bean的初始化和銷毀過程有這樣的執(zhí)行順序:
初始化順序:@PostConstruct → InitializingBean → init-method
銷毀順序:@PreDestroy → DisposableBean → destroy-method
在初始化Bean時,@PostConstruct注解方法會首先被執(zhí)行,然后是實現(xiàn)InitializingBean接口的afterPropertiesSet方法,最后是init-method指定的方法。
在銷毀Bean時,@PreDestroy注解方法會首先被執(zhí)行,然后是實現(xiàn)DisposableBean接口的destroy方法,最后是destroy-method指定的方法
結(jié)合前面說過的屬性賦值(構(gòu)造器方法和setter方法),簡單總結(jié)一下Spring Bean生命周期的流程:
實例化(通過構(gòu)造器方法);
設(shè)置
Bean的屬性(通過setter方法);調(diào)用
Bean的初始化方法(@PostConstruct、afterPropertiesSet方法或者init-method指定的方法);Bean可以被應(yīng)用程序使用;當容器關(guān)閉時,調(diào)用
Bean的銷毀方法(@PreDestroy、destroy方法或者destroy-method指定的方法)。
5. 原型Bean的生命周期
原型Bean的創(chuàng)建和初始化過程與單例Bean類似,但由于原型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 - 獅子準備行動。。。");
}
?
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean - 獅子行動結(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();
}
}
? 如果我們只是啟動了IOC容器,但并未請求Lion2的實例,Lion Bean的初始化不會立刻發(fā)生。也就是說,原型Bean不會隨著IOC容器的啟動而初始化。以下是啟動容器但并未請求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容器初始化開始");
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
PrototypeLifecycleConfiguration.class);
}
}
?運行結(jié)果:

當我們明確請求一個Lion2的實例時,我們會看到所有的初始化方法按照預定的順序執(zhí)行,這個順序跟單例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容器初始化開始");
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...");
}
}運行結(jié)果:
可以看出:
在對 prototype Bean(原型 Bean) 與 singleton Bean(單例 Bean) 的三種銷毀方式進行對比實驗后可以發(fā)現(xiàn): 當通過 IOC 容器的 destroyBean() 方法手動銷毀原型 Bean 時,只有使用 @PreDestroy 注解標注的方法以及實現(xiàn)了 DisposableBean 接口的 destroy() 方法會被正常調(diào)用,而通過 destroy-method 屬性指定的自定義銷毀方法并不會被執(zhí)行。
由此可以得出結(jié)論:在原型 Bean 的銷毀過程中,Spring 并不會觸發(fā)由 destroy-method 配置的自定義銷毀邏輯。這也說明,destroy-method 方式在原型 Bean 場景下存在一定的局限性。
因此,如果 Bean 在銷毀階段包含關(guān)鍵的資源釋放或清理邏輯,更穩(wěn)妥的做法是將這些邏輯放在 @PreDestroy 注解的方法中,或者實現(xiàn) DisposableBean 接口的 destroy() 方法,以確保在原型 Bean 被銷毀時能夠得到正確執(zhí)行。
6. Spring中控制Bean生命周期的三種方式總結(jié)
| 執(zhí)行順序 | 代碼依賴性 | 容器支持 | 單實例Bean | 原型Bean | |
|---|---|---|---|---|---|
| init-method & destroy-method | 最后 | 較低(依賴于Spring Bean配置,不侵入業(yè)務(wù)代碼) | xml、注解原生支持 | √ | 只支持 init-method |
| @PostConstruct & @PreDestroy | 最先 | 中等(需要在業(yè)務(wù)代碼中添加JSR規(guī)范的注解) | 注解原生支持,xml需開啟注解驅(qū)動 | √ | √ |
| InitializingBean & DisposableBean | 中間 | 較高(需要業(yè)務(wù)代碼實現(xiàn)Spring特定接口) | xml、注解原生支持 | √ | √ |
7. 總結(jié)
到此這篇關(guān)于Spring中Bean的生命周期原理解析:實例化、屬性賦值、初始化、運行期和銷毀的文章就介紹到這了,更多相關(guān)Spring中Bean的生命周期原理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java 數(shù)組元素倒序的三種方式(小結(jié))
這篇文章主要介紹了Java 數(shù)組元素倒序的三種方式(小結(jié)),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-09-09
java基礎(chǔ)知識之FileInputStream流的使用
這篇文章主要介紹了java基礎(chǔ)知識之FileInputStream流的使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12
Java中的ConcurrentLinkedQueue使用解析
這篇文章主要介紹了Java中的ConcurrentLinkedQueue使用解析,一個基于鏈接節(jié)點的無界線程安全隊列,此隊列按照 FIFO(先進先出)原則對元素進行排序,隊列的頭部是隊列中時間最長的元素,需要的朋友可以參考下2023-12-12

