最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring中Bean的生命周期原理解析:實例化、屬性賦值、初始化、運行期和銷毀

 更新時間:2026年01月17日 08:54:02   作者:中環(huán)留念  
本文詳細介紹了Spring IOC容器中Bean的生命周期管理,包括實例化、屬性賦值、初始化方法、Bean運行期和銷毀方法,通過示例展示了如何使用XML配置和注解方式來定義初始化和銷毀方法,并解釋了這些方法的特性和使用規(guī)則

1. 理解Bean的生命周期

在Spring IOC容器中,Bean的生命周期大致如下:

  1. 實例化:當(dāng)啟動Spring應(yīng)用時,IOC容器就會為在配置文件中聲明的每個<bean>創(chuàng)建一個實例。

  2. 屬性賦值:實例化后,Spring就通過反射機制給Bean的屬性賦值。

  3. 調(diào)用初始化方法:如果Bean配置了初始化方法,Spring就會調(diào)用它。初始化方法是在Bean創(chuàng)建并賦值之后調(diào)用,可以在這個方法里面寫一些業(yè)務(wù)處理代碼或者做一些初始化的工作。

  4. Bean運行期:此時,Bean已經(jīng)準(zhǔn)備好被程序使用了,它已經(jīng)被初始化并賦值完成。

  5. 應(yīng)用程序關(guān)閉:當(dāng)關(guān)閉IOC容器時,Spring會處理配置了銷毀方法的Bean。

  6. 調(diào)用銷毀方法:如果Bean配置了銷毀方法,Spring會在所有Bean都已經(jīng)使用完畢,且IOC容器關(guān)閉之前調(diào)用它,可以在銷毀方法里面做一些資源釋放的工作,比如關(guān)閉連接、清理緩存等。

這就是Spring IOC容器管理Bean的生命周期,幫助我們管理對象的創(chuàng)建和銷毀,以及在適當(dāng)?shù)臅r機做適當(dāng)?shù)氖虑椤?/p>

我們可以將生命周期的觸發(fā)稱為回調(diào),因為生命周期的方法是我們自己定義的,但方法的調(diào)用是由框架內(nèi)部幫我們完成的,所以可以稱之為“回調(diào)”。

2. 理解init-method和destroy-method

讓我們先了解一種最容易理解的生命周期階段:初始化和銷毀方法。這些方法可以在Bean的初始化和銷毀階段起作用,我們通過示例來演示這種方式。

為了方便演示XML和注解的方式,接下來我們會創(chuàng)建兩個類來分別進行演示,分別為LionElephant,讓我們一步一步對比觀察。

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>標(biāo)簽來注冊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容器準(zhǔn)備關(guān)閉");
        context.close();
        System.out.println("Spring容器已關(guān)閉。");
    }
}

運行結(jié)果:

在上述 XML 配置文件中,使用 <bean> 標(biāo)簽向 Spring IOC 容器中注冊了一個 Lion 類型的 Bean。

  1. <bean> 標(biāo)簽的核心作用

<bean> 標(biāo)簽用于聲明一個由 Spring 容器管理的對象,其中:

  • class 屬性用于指定 Bean 的全限定類名;

  • Spring 在容器啟動時,會根據(jù)該配置創(chuàng)建并管理該對象的生命周期

  • init-methoddestroy-method 的含義

<bean> 標(biāo)簽中,定義了兩個非常關(guān)鍵的屬性:

init-method="init"
destroy-method="destroy"
  • init-method 指定 Bean 初始化完成后要執(zhí)行的方法 → 當(dāng) Bean 被實例化并完成屬性注入后,Spring 會自動調(diào)用 init() 方法

  • destroy-method 指定 Bean 被銷毀前要執(zhí)行的方法 → 當(dāng) IOC 容器關(guān)閉時,Spring 會自動調(diào)用 destroy() 方法

這兩個方法無需實現(xiàn)任何接口,只需在類中定義普通方法即可。

  • property 標(biāo)簽的作用

<property name="name" value="simba"/>
  • 用于給 Bean 的屬性進行依賴注入

  • Spring 在調(diào)用初始化方法之前,會先完成屬性賦值

  • 這也是為什么在 init() 方法中可以直接使用 name 屬性

  • 控制臺輸出與生命周期驗證

當(dāng) IOC 容器啟動時,如果在 init() 方法中輸出類似:

simba has been initialized...

說明 初始化方法已成功被 Spring 調(diào)用。

當(dāng)程序執(zhí)行 context.close() 或應(yīng)用正常關(guān)閉時,如果看到:

simba has been destroyed...

說明 銷毀方法已成功被 Spring 調(diào)用。

  • 生命周期整體過程總結(jié)

整個 Bean 生命周期可以總結(jié)為:

  1. IOC 容器啟動

  2. Spring 根據(jù) XML 配置創(chuàng)建 Bean 實例

  3. 完成屬性注入(如 name = simba

  4. 調(diào)用 init-method 指定的初始化方法

  5. Bean 進入可使用狀態(tài)

  6. IOC 容器關(guān)閉

  7. 調(diào)用 destroy-method 指定的銷毀方法

  8. 所有 Bean 銷毀完成,IOC 容器關(guān)閉

小結(jié)

在 IOC 容器初始化完成之前,Bean 默認(rèn)已經(jīng)被創(chuàng)建并完成了初始化操作; 當(dā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注解中也有類似的屬性:initMethoddestroyMethod,這兩個屬性的作用與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容器準(zhǔn)備關(guān)閉");
        context.close();
        System.out.println("Spring容器已關(guān)閉。");
    }
}

運行結(jié)果:

注意:在Spring中,如果在Java配置中定義了一個Bean,并在XML中定義了一個相同idnameBean,那么最后注冊的那個Bean會覆蓋之前注冊的,這取決于配置文件加載順序,無論在Java配置中還是XML配置中定義的initMethoddestroyMethod,最后生效的總是后加載的配置中定義的。

init-method”是指定初始化回調(diào)方法的屬性的統(tǒng)稱,無論它是在XML配置還是Java配置中使用。同樣地,“destroy-method”是指定銷毀回調(diào)方法的屬性的統(tǒng)稱。后文我們講解多種聲明的周期共存的時候,將延續(xù)這種說法。

2.3 初始化方法與銷毀方法的特性說明

在 Spring 框架中,為 Bean 配置初始化方法和銷毀方法時,需要遵循一定的規(guī)范,否則 Spring 可能無法按預(yù)期觸發(fā)生命周期回調(diào)。下面從幾個常見特性出發(fā),對這些方法的使用規(guī)則進行說明,并配合示例進行解釋。

  1. 方法的訪問權(quán)限不受限制

初始化方法和銷毀方法在訪問權(quán)限上沒有強制要求,無論是 public、protectedprivate,還是默認(rèn)(包私有)權(quán)限,Spring 都可以正常調(diào)用。

這是因為 Spring 底層是通過反射機制來執(zhí)行這些方法的,因此不會受到 Java 訪問控制符的限制。

示例:

public class MyBean {
private void init() {// 初始化代碼
    }
}

在上述示例中,即使 init() 方法被定義為 private,Spring 依然能夠在 Bean 初始化階段正確調(diào)用該方法。

  • 方法通常不應(yīng)包含參數(shù)

在默認(rèn)情況下,Spring 并不知道應(yīng)該向初始化或銷毀方法傳遞哪些參數(shù),因此這些方法通常不應(yīng)定義參數(shù)

示例:

public class MyBean {
public void init() {// 初始化代碼
    }
}

需要注意的是,Spring 并非完全禁止帶參數(shù)的方法。如果方法定義了參數(shù),Spring 容器會嘗試?yán)米詣友b配機制,按照類型或名稱為參數(shù)匹配對應(yīng)的 Bean 并進行注入。

但如果:

  • 無法找到匹配的 Bean,或

  • 存在多個符合條件的 Bean,

那么 Spring 會在應(yīng)用啟動階段拋出異常,導(dǎo)致容器初始化失敗。因此在實際開發(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.");
        }
    }
}

當(dāng)初始化或銷毀方法拋出異常(無論是檢查型異常還是運行時異常)時,Spring 容器都會捕獲該異常,并將其封裝為 BeanCreationExceptionBeanDestructionException 后再次拋出,從而導(dǎo)致 Bean 的創(chuàng)建或銷毀過程失敗。

  • 方法不應(yīng)定義為靜態(tài)方法

初始化方法和銷毀方法本質(zhì)上是作用于 Bean 實例生命周期的,而靜態(tài)方法屬于類級別,不依賴于具體實例,因此并不適合作為生命周期回調(diào)方法。

示例:

public class MyBean {
public static void init() {// 初始化代碼
    }
}

如果將初始化或銷毀方法定義為 static,Spring 并不會立即報錯,但這種做法違背了生命周期方法應(yīng)當(dāng)綁定到 Bean 實例的設(shè)計原則,也不符合實際使用場景。

2.4 探究Bean的初始化流程順序

  在上面的代碼中,我們可以看出BeanIOC容器初始化階段就已經(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容器準(zhǔn)備關(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注解標(biāo)記的方法會在bean屬性設(shè)置完畢后(即完成依賴注入),但在bean對外暴露(即可以被其他bean引用)之前被調(diào)用,這個時機通常用于完成一些初始化工作。

@PreDestroy注解標(biāo)記的方法會在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 注解標(biāo)注的方法與 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容器準(zhǔn)備關(guān)閉");
        context.close();
        System.out.println("Spring容器已關(guān)閉。");
    }
}

運行結(jié)果

這里可以看到@PostConstruct@PreDestroy注解正確地應(yīng)用在了Lion的初始化和銷毀過程中。

注意:   @PostConstruct@PreDestroy可用于任何Java類,初始化和銷毀方法與init-methoddestroy-method類似,但也有一定的區(qū)別。

  1. 這些方法必須是非靜態(tài)的,否則Spring容器在啟動或銷毀時會拋出BeanCreationExceptionBeanDestructionException異常,導(dǎo)致創(chuàng)建或銷毀bean失敗。

  2. 這些方法推薦無參數(shù),與init-methoddestroy-method的行為類似。

  3. 這些方法推薦無返回值,與init-methoddestroy-method的行為類似。

  4. 可以是任何訪問級別,與init-methoddestroy-method的行為類似。

  5. 可以拋出異常,與init-methoddestroy-method的行為類似。

  6. 不能被final修飾,如果使用final修飾這兩個注解的方法,在編譯時不會報錯,可以正常編譯。但是在運行時,Spring容器在解析和設(shè)置注解時,會嘗試使用CGLIBJDK動態(tài)代理生成子類,由于方法被final修飾,子類無法覆蓋該方法,所以Spring容器會拋出異常,表示無法為生命周期方法生成代理,這會導(dǎo)致標(biāo)注了final的生命周期方法無法被Spring調(diào)用。

  注意:init-methoddestroy-method方法被final修飾也無影響,因為Spring通過反射機制來調(diào)用init-methoddestroy-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容器準(zhǔn)備關(guān)閉");
        context.close();
        System.out.println("Spring容器已關(guān)閉。");
    }
}

運行結(jié)果

這里可以看到@PostConstruct@PreDestroy注解的優(yōu)先級始終高于配置類中@Bean注解的initMethoddestroyMethod屬性。

4. 實現(xiàn)InitializingBean和DisposableBean接口

  這兩個接口是 Spring 預(yù)定義的兩個關(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框架中,當(dāng)一個bean的所有屬性都已經(jīng)被設(shè)置完畢后,這個方法就會被調(diào)用。也就是說,這個bean一旦被初始化,Spring就會調(diào)用這個方法。我們可以在bean的所有屬性被設(shè)置后,進行一些自定義的初始化工作。

  DisposableBean接口也只有一個方法:destroy()。當(dāng)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容器準(zhǔn)備關(guān)閉");
        context.close();
        System.out.println("Spring容器已關(guān)閉。");
    }
}
?

運行結(jié)果:

4.2 三種生命周期并存

Spring框架中,控制Bean生命周期的三種方式是:

  1. 使用Springinit-methoddestroy-method(在XML配置或者Java配置中自定義的初始化和銷毀方法);

  2. 使用JSR-250規(guī)范的@PostConstruct@PreDestroy注解;

  3. 實現(xiàn)SpringInitializingBeanDisposableBean接口。

  接下來我們測試一下,一個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 - 獅子準(zhǔn)備行動。。。");
    }
?
    @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容器準(zhǔn)備關(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生命周期的流程:

  1. 實例化(通過構(gòu)造器方法);

  2. 設(shè)置Bean的屬性(通過setter方法);

  3. 調(diào)用Bean的初始化方法(@PostConstructafterPropertiesSet方法或者init-method指定的方法);

  4. Bean可以被應(yīng)用程序使用;

  5. 當(dā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 - 獅子準(zhǔn)備行動。。。");
    }
?
    @Override
    public void destroy() throws Exception {
   
        System.out.println("DisposableBean - 獅子行動結(jié)束。。。");
    }
}

然后在SpringJava配置中聲明并設(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é)果:

  當(dāng)我們明確請求一個Lion2的實例時,我們會看到所有的初始化方法按照預(yù)定的順序執(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): 當(dāng)通過 IOC 容器的 destroyBean() 方法手動銷毀原型 Bean 時,只有使用 @PreDestroy 注解標(biāo)注的方法以及實現(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中的HashMap集合源碼詳細解讀

    Java中的HashMap集合源碼詳細解讀

    這篇文章主要介紹了Java中的HashMap集合源碼詳細解讀,hash表是一種數(shù)據(jù)結(jié)構(gòu),它擁有驚人的效率,它的時間復(fù)雜度低到接近O(1)這樣的常數(shù)級,需要的朋友可以參考下
    2023-11-11
  • 超詳細講解Java秒殺項目用戶驗證模塊的實現(xiàn)

    超詳細講解Java秒殺項目用戶驗證模塊的實現(xiàn)

    這是一個主要使用java開發(fā)的秒殺系統(tǒng),項目比較大,所以本篇只實現(xiàn)了用戶驗證模塊,代碼非常詳盡,感興趣的朋友快來看看
    2022-03-03
  • Java代碼讀取properties配置文件的示例代碼

    Java代碼讀取properties配置文件的示例代碼

    這篇文章主要介紹了Java代碼讀取properties配置文件,本文通過示例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-05-05
  • Java基礎(chǔ)之List內(nèi)元素的排序性能對比

    Java基礎(chǔ)之List內(nèi)元素的排序性能對比

    這篇文章主要介紹了Java基礎(chǔ)之List內(nèi)元素的排序性能對比,文中有非常詳細的代碼示例,對正在學(xué)習(xí)java基礎(chǔ)的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-04-04
  • Java Process與Runtime()的使用及調(diào)用cmd命令阻塞的解決方案

    Java Process與Runtime()的使用及調(diào)用cmd命令阻塞的解決方案

    這篇文章主要介紹了Java Process與Runtime()的使用及調(diào)用cmd命令阻塞的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • Java實現(xiàn)HTML轉(zhuǎn)PDF的兩款工具(itext-pdfhtml和x-easypdf)介紹與使用

    Java實現(xiàn)HTML轉(zhuǎn)PDF的兩款工具(itext-pdfhtml和x-easypdf)介紹與使用

    這篇文章主要為大家詳細介紹了Java實現(xiàn)HTML轉(zhuǎn)PDF的兩款工具(itext-pdfhtml和x-easypdf)的介紹與使用,文中的示例代碼講解詳細,感興趣的小伙伴可以了解下
    2025-11-11
  • Java責(zé)任鏈模式的實現(xiàn)方法詳解

    Java責(zé)任鏈模式的實現(xiàn)方法詳解

    責(zé)任鏈模式是一種設(shè)計模式,在責(zé)任鏈模式里,很多對象由每一個對象對其下家的引用而連接起來形成一條鏈,這篇文章主要介紹了Java責(zé)任鏈模式實現(xiàn)方法的相關(guān)資料,需要的朋友可以參考下
    2025-07-07
  • Java經(jīng)典排序算法之快速排序代碼實例

    Java經(jīng)典排序算法之快速排序代碼實例

    這篇文章主要介紹了Java經(jīng)典排序算法之快速排序代碼實例,快速排序?qū)崿F(xiàn)的思想是指通過一趟排序?qū)⒁判虻臄?shù)據(jù)分割成獨立的兩部分,其中一部分的所有數(shù)據(jù)都比另外一部分的所有數(shù)據(jù)都要小,然后再按此方法對這兩部分?jǐn)?shù)據(jù)分別進行快速排序,需要的朋友可以參考下
    2023-10-10
  • SpringBoot錯誤處理機制以及自定義異常處理詳解

    SpringBoot錯誤處理機制以及自定義異常處理詳解

    這篇文章主要為大家詳細介紹了SpringBoot錯誤處理機制以及自定義異常處理,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-05-05
  • Windows 下安裝配置 Eclipse詳細教程

    Windows 下安裝配置 Eclipse詳細教程

    Eclipse是一款非常優(yōu)秀的開源IDE,非常適合Java開發(fā),由于支持插件技術(shù),受到了越來越多的開發(fā)者的歡迎。配合眾多令人眼花繚亂的插件,完全可以滿足從企業(yè)級Java應(yīng)用到手機終端Java游戲的開發(fā)。本文將帶您手把手步入Eclipse的廣闊天地
    2016-09-09

最新評論

彭山县| 西乌珠穆沁旗| 永年县| 阜南县| 同心县| 辽宁省| 运城市| 竹北市| 武汉市| 阿勒泰市| 辉南县| 济宁市| 清丰县| 什邡市| 城固县| 蓬安县| 来宾市| 新乡市| 泰安市| 绥芬河市| 含山县| 惠水县| 双桥区| 安化县| 阜南县| 岐山县| 乡城县| 安乡县| 平凉市| 衡阳县| 萝北县| 哈密市| 邵东县| 华亭县| 辛集市| 庆阳市| 上饶县| 邢台县| 呼玛县| 遵义市| 比如县|