寫在前面
在【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 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註解驅動開發不再迷茫。