【Spring註解驅動開發】如何使用@Bean註解指定初始化和銷毀的方法?看這一篇就夠了!!

寫在前面

在【String註解驅動開發專題】中,前面的文章我們主要講了有關於如何向Spring容器中註冊bean的知識,大家可以到【String註解驅動開發專題】中系統學習。接下來,我們繼續肝Spring,只不過從本篇文章開始,我們就進入Spring容器中有關Bean的生命周期的學習。

項目工程源碼已經提交到GitHub:https://github.com/sunshinelyz/spring-annotation

Bean的生命周期

通常意義上講的bean的名稱周期,指的是bean從創建到初始化,經過一系列的流程,最終銷毀的過程。只不過,在Spring中,bean的生命周期是由Spring容器來管理的。在Spring中,我們可以自己來指定bean的初始化和銷毀的方法。當我們指定了bean的初始化和銷毀方法時,當容器在bean進行到當前生命周期的階段時,會自動調用我們自定義的初始化和銷毀方法。

如何定義初始化和銷毀方法?

我們已經知道了由Spring管理bean的生命周期時,我們可以指定bean的初始化和銷毀方法,那具體該如何定義這些初始化和銷毀方法呢?接下來,我們就介紹第一種定義初始化和銷毀方法的方式: 通過@Bean註解指定初始化和銷毀方法。

如果是使用XML文件的方式配置bean的話,可以在 標籤中指定bean的初始化和銷毀方法,如下所示。

<bean id = "person" class="io.mykit.spring.plugins.register.bean.Person" init-method="init" destroy-method="destroy">
    <property name="name" value="binghe"></property>
    <property name="age" value="18"></property>
</bean>

這裏,需要注意的是,在我們寫的Person類中,需要存在init()方法和destroy()方法。而且Spring中規定,這裏的init()方法和destroy()方法必須是無參方法,但可以拋異常。

如果我們使用註解的方式,該如何實現指定bean的初始化和銷毀方法呢?接下來,我們就一起來搞定它!!

首先,創建一個名稱為Student的類,這個類的實現比較簡單,如下所示。

package io.mykit.spring.plugins.register.bean;
/**
 * @author binghe
 * @version 1.0.0
 * @description 測試bean的初始化和銷毀方法
 */
public class Student {
    
    public Student(){
        System.out.println("Student類的構造方法");
    }

    public void init(){
        System.out.println("初始化Student對象");
    }

    public void destroy(){
        System.out.println("銷毀Student對象");
    }
}

接下來,我們將Student類對象通過註解的方式註冊到Spring容器中,具體的做法就是新建一個LifeCircleConfig類作為Spring的配置類,將Student類對象通過LifeCircleConfig類註冊到Spring容器中,LifeCircleConfig類的代碼如下所示。

package io.mykit.spring.plugins.register.config;

import io.mykit.spring.plugins.register.bean.Student;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author binghe
 * @version 1.0.0
 * @description Bean的生命周期
 */
@Configuration
public class LifeCircleConfig {
    @Bean
    public Student student(){
        return new Student();
    }
}

接下來,我們就新建一個BeanLifeCircleTest類來測試容器中的Student對象,BeanLifeCircleTest類的部分代碼如下所示。

package io.mykit.spring.test;

import io.mykit.spring.plugins.register.config.LifeCircleConfig;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @author binghe
 * @version 1.0.0
 * @description 測試bean的生命周期
 */
public class BeanLifeCircleTest {

    @Test
    public void testBeanLifeCircle01(){
        //創建IOC容器
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(LifeCircleConfig.class);
        System.out.println("容器創建完成...");
    }
}

在前面的文章中,我們說過:對於單實例bean對象來說,在Spring容器創建完成后,就會對單實例bean進行實例化。那麼,我們先來運行下BeanLifeCircleTest類中的testBeanLifeCircle01()方法,輸出的結果信息如下所示。

Student類的構造方法
容器創建完成...

可以看到,在Spring容器創建完成時,自動調用單實例bean的構造方法,對單實例bean進行了實例化操作。

總之:對於單實例bean來說,在Spring容器啟動的時候創建對象;對於多實例bean來說,在每次獲取bean的時候創建對象。

現在,我們在Student類中指定了init()方法和destroy()方法,那麼,如何讓Spring容器知道Student類中的init()方法是用來執行對象的初始化操作,而destroy()方法是用來執行對象的銷毀操作呢?如果是使用XML文件配置的話,我們可以使用如下配置來實現。

<bean id="student" class="io.mykit.spring.plugins.register.bean.Student" init-method="init" destroy-method="destroy"></bean>

如果我們在@Bean註解中該如何實現呢?其實就更簡單了,我們來看下@Bean註解的源碼,如下所示。

package org.springframework.context.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {

	@AliasFor("name")
	String[] value() default {};

	@AliasFor("value")
	String[] name() default {};

	@Deprecated
	Autowire autowire() default Autowire.NO;

	boolean autowireCandidate() default true;

	String initMethod() default "";

	String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;

}

看到@Bean註解的源碼,相信小夥伴們會有種豁然開朗的感覺:沒錯,就是使用@Bean註解的initMethod屬性和destroyMethod屬性來指定bean的初始化方法和銷毀方法。

所以,我們在LifeCircleConfig類中的@Bean註解中指定initMethod屬性和destroyMethod屬性,如下所示。

@Bean(initMethod = "init", destroyMethod = "destroy")
public Student student(){
    return new Student();
}

此時,我們再來運行BeanLifeCircleTest類中的testBeanLifeCircle01()方法,輸出的結果信息如下所示。

Student類的構造方法
初始化Student對象
容器創建完成...

從輸出結果可以看出,在Spring容器中,先是調用了Student類的構造方法來創建Student對象,接下來調用了Student對象的init()方法來進行初始化。

那小夥伴們可能會問,運行上面的代碼沒有打印出bean的銷毀方法中的信息啊,那什麼時候執行bean的銷毀方法呢? 這個問題問的很好, bean的銷毀方法是在容器關閉的時候調用的。

接下來,我們在BeanLifeCircleTest類中的testBeanLifeCircle01()方法中,添加關閉容器的代碼,如下所示。

@Test
public void testBeanLifeCircle01(){
    //創建IOC容器
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(LifeCircleConfig.class);
    System.out.println("容器創建完成...");
    context.close();
}

我們再來運行BeanLifeCircleTest類中的testBeanLifeCircle01()方法,輸出的結果信息如下所示。

Student類的構造方法
初始化Student對象
容器創建完成...
銷毀Student對象

可以看到,此時輸出了對象的銷毀方法中的信息,說明執行了對象的銷毀方法。

指定初始化和銷毀方法的使用場景

一個典型的使用場景就是對於數據源的管理。例如,在配置數據源時,在初始化的時候,對很多的數據源的屬性進行賦值操作;在銷毀的時候,我們需要對數據源的連接等信息進行關閉和清理。此時,我們就可以在自定義的初始化和銷毀方法中來做這些事情!

初始化和銷毀方法調用的時機

  • bean對象的初始化方法調用的時機:對象創建完成,如果對象中存在一些屬性,並且這些屬性也都賦值好之後,會調用bean的初始化方法。對於單實例bean來說,在Spring容器創建完成后,Spring容器會自動調用bean的初始化和銷毀方法;對於單實例bean來說,在每次獲取bean對象的時候,調用bean的初始化和銷毀方法。
  • bean對象的銷毀方法調用的時機:對於單實例bean來說,在容器關閉的時候,會調用bean的銷毀方法;對於多實例bean來說,Spring容器不會管理這個bean,也不會自動調用這個bean的銷毀方法。不過,小夥伴們可以手動調用多實例bean的銷毀方法。

前面,我們已經說了單實例bean的初始化和銷毀方法。接下來,我們來說下多實例bean的初始化和銷毀方法。我們將Student對象變成多實例bean來驗證下。接下來,我們在LifeCircleConfig類的student()方法上通過@Scope註解將Student對象設置成多實例bean,如下所示。

@Scope("prototype")
@Bean(initMethod = "init", destroyMethod = "destroy")
public Student student(){
    return new Student();
}

接下來,我們再來運行BeanLifeCircleTest類中的testBeanLifeCircle01()方法,輸出的結果信息如下所示。

容器創建完成...

可以看到,當我們將Student對象設置成多實例bean,並且沒有獲取bean實例對象時,Spring容器並沒有執行bean的構造方法、初始化方法和銷毀方法。

說到這,我們就在BeanLifeCircleTest類中的testBeanLifeCircle01()方法中添加一行獲取Student對象的代碼,如下所示。

@Test
public void testBeanLifeCircle01(){
    //創建IOC容器
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(LifeCircleConfig.class);
    System.out.println("容器創建完成...");
    context.getBean(Student.class);
    context.close();
}

此時,我們再來運行BeanLifeCircleTest類中的testBeanLifeCircle01()方法,輸出的結果信息如下所示。

容器創建完成...
Student類的構造方法
初始化Student對象

可以看到,此時,結果信息中輸出了構造方法和初始化方法中的信息。但是當容器關閉時,並沒有輸出bean的銷毀方法中的信息。

這是因為 將bean設置成多實例時,Spring不會自動調用bean對象的銷毀方法。至於多實例bean對象何時銷毀,那就是程序員自己的事情了!!Spring容器不再管理多實例bean。

好了,咱們今天就聊到這兒吧!別忘了給個在看和轉發,讓更多的人看到,一起學習一起進步!!

項目工程源碼已經提交到GitHub:https://github.com/sunshinelyz/spring-annotation

寫在最後

如果覺得文章對你有點幫助,請微信搜索並關注「 冰河技術 」微信公眾號,跟冰河學習Spring註解驅動開發。公眾號回復“spring註解”關鍵字,領取Spring註解驅動開發核心知識圖,讓Spring註解驅動開發不再迷茫。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化