Spring IoC(Inversion of Control雹拄,控制反轉(zhuǎn))容器管理著Spring中的bean森篷。一個(gè)"Spring bean"只是一個(gè)由Spring管理的Java類的實(shí)例桃煎。
Spring的IoC容器負(fù)責(zé)實(shí)例化、初始化并寫入bean辑舷,同時(shí)也管理這些bean的生命周期砾莱。
Spring提供了一些方法,你可以通過它們來進(jìn)入bean的生命周期胰丁。 例如随橘,一旦一個(gè)bean被實(shí)例化,你可能就需要執(zhí)行一些初始化操作來使這個(gè)bean進(jìn)入可用狀態(tài)锦庸。類似地机蔗,你可能需要在一個(gè)bean被從容器移除之前進(jìn)行資源的清理。
在這篇文章中,我們會(huì)研究Spring bean生命周期的各個(gè)階段萝嘁。這就是Spring框架創(chuàng)建和銷毀Spring bean的方式梆掸。
Spring Bean 生命周期概覽
下面的圖片展示了Spring bean生命周期的兩個(gè)部分:
Part 1:展示了一個(gè)bean在實(shí)例化之后到準(zhǔn)備就緒可用前所經(jīng)歷的不同階段。
Part 2:展示了當(dāng)一個(gè)Spring Ioc容器關(guān)閉牙言,一個(gè)bean會(huì)經(jīng)歷些什么酸钦。
正如你在前面圖片的Part 1中所看見的,容器通過調(diào)用它的構(gòu)造器來實(shí)例化一個(gè)bean然后填充它的屬性咱枉。
之后對(duì)bean進(jìn)行多次調(diào)用卑硫,直到bean處于就緒狀態(tài)。
類似地蚕断,像Part 2中展示的那樣欢伏,當(dāng)容器關(guān)閉,容器調(diào)用bean來使其能夠在銷毀bean之前執(zhí)行所有需要的任務(wù)亿乳。
Aware接口
Spring提供了一些aware接口硝拧,他們用來進(jìn)入Spring框架的基礎(chǔ)層。aware接口主要在框架中使用葛假,很少被Spring程序員使用障陶。
作為一個(gè)Spring程序員應(yīng)該熟悉下面三個(gè)aware接口:
- BeanFactoryAware:提供setBeanFactory(),一個(gè)提供bean實(shí)例所屬工廠的回調(diào)聊训。
- BeanNameAware: 此接口的ThesetBeanName()回調(diào)提供了bean的名稱咸这。
-
ApplicationContextAware:此接口的ThesetApplicationContext()方法提供了此bean的ApplicationContext對(duì)象
使用上述aware接口的代碼如下:
package guru.springframework.springbeanlifecycle.awareinterfaces.domain;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import java.util.Arrays;
public class AwareBeanImpl implements ApplicationContextAware, BeanNameAware, BeanFactoryAware {
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("setBeanFactory method of AwareBeanImpl is called");
System.out.println("setBeanFactory:: AwareBeanImpl singleton= "
+ beanFactory.isSingleton("awareBean"));
}
@Override
public void setBeanName(String beanName) {
System.out.println("setBeanName method of AwareBeanImpl is called");
System.out.println("setBeanName:: Bean Name defined in context= "
+ beanName);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("setApplicationContext method of AwareBeanImpl is called");
System.out.println("setApplicationContext:: Bean Definition Names= "
+ Arrays.toString(applicationContext.getBeanDefinitionNames()));
}
}
前面的bean實(shí)現(xiàn)了ApplicationContextAware、BeanNameAware和BeanFactoryAware接口魔眨。
代碼行13-18:代碼重寫了BeanFactoryAware接口的setBeanFactory()方法。在運(yùn)行期間酿雪,Spring傳遞創(chuàng)建bean的BeanFactory對(duì)象遏暴。該代碼使用這個(gè)BeanFactory對(duì)象來打印這個(gè)bean是否是一個(gè)單例。
代碼行20-25:重寫了BeanNameAware接口的setBeanName()方法指黎。在運(yùn)行期間朋凉,Spring以一個(gè)String來傳遞這個(gè)bean的名字,被該代碼打印了出來醋安。代碼使用了beanName來打印上下文中定義的bean的名字杂彭。
代碼行27-32:重寫了ApplicationContextAware接口的setApplicationContext()方法,在運(yùn)行期間吓揪,Spring傳遞創(chuàng)建了這個(gè)bean的ApplicationContext對(duì)象亲怠。代碼用這個(gè)ApplicationContext對(duì)象來打印bean定義的名字。
接下來柠辞,我們將編寫bean的配置來定義AwareBeanImpl团秽。beans.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-3.0.xsd">
<!-- awareinterfaces-->
<bean id="awareBean" class="guru.springframework.springbeanlifecycle.awareinterfaces.domain.AwareBeanImpl">
</bean>
</beans>
最后,讓我們編寫主類,它將加載bean .xml并測(cè)試相關(guān)接口方法:
package guru.springframework.springbeanlifecycle;
import guru.springframework.springbeanlifecycle.awareinterfaces.domain.AwareBeanImpl;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
@SpringBootApplication
public class SpringBeanLifecycleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBeanLifecycleApplication.class, args);
// -------awareinterfaces---------
ApplicationContext context1 =
new ClassPathXmlApplicationContext("beans.xml");
AwareBeanImpl awareBeanImpl = (AwareBeanImpl) context1.getBean("awareBean");
((AbstractApplicationContext) context1).registerShutdownHook();
}
}
運(yùn)行主類的輸入如下:
setBeanName method of AwareBeanImpl is called
setBeanName:: Bean Name defined in context= awareBean
setBeanFactory method of AwareBeanImpl is called
setBeanFactory:: AwareBeanImpl singleton= true
setApplicationContext method of AwareBeanImpl is called
setApplicationContext:: Bean Definition Names= [awareBean]
Bean后置處理
Spring提供了BeanPostProcessor接口习勤,為你提供了進(jìn)入Spring上下文生命周期和在處理bean時(shí)和他們交互的方法踪栋。
BeanPostProcessor接口包含兩個(gè)方法:
- postProcessBeforeInitialization:Spring在調(diào)用aware接口的方法之后和任何bean的初始化回調(diào)之前調(diào)用此方法,例如InitializingBean的afterPropertiesSet()或者任何自定義的初始化方法图毕。
- postProcessAfterInitialization:Spring在任何bean的初始化回調(diào)之后調(diào)用此方法夷都。
讓我們從創(chuàng)建一個(gè)叫BookName的bean開始:
package guru.springframework.springbeanlifecycle.beanpostprocessor.domain;
public class BookBean {
private String bookName;
public BookBean() {
System.out.println("Constructor of BookBean called !! ");
}
public BookBean(String bookName) {
this.bookName = bookName;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
@Override
public String toString() {
return "BookBean{" +
"bookName='" + bookName + '\'' +
'}';
}
}
接下來,我們創(chuàng)建一個(gè)BookBeanPostProcessor予颤。
BookBeanPostProcessor代碼如下:
package guru.springframework.springbeanlifecycle.beanpostprocessor.domain;
import guru.springframework.springbeanlifecycle.beanpostprocessor.domain.BookBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class BookBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Post Process Before Initialization method is called : Bean Name " + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Post Process After Initialization method is called : Bean Name " + beanName);
return bean;
}
}
上述代碼實(shí)現(xiàn)了BeanPostProcessor接口并覆蓋了postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法囤官。
Spring在調(diào)用了aware接口的方法之后。調(diào)用postProcessBeforeInitialization()方法荣瑟。
Spring在所有的bean初始化回調(diào)之后調(diào)用postProcessAfterInitialization()方法治拿,例如InitializingBean的afterPropertiesSet()或者任何自定義的初始化方法。我們接下來兩個(gè)都會(huì)討論笆焰。
在運(yùn)行時(shí)劫谅,Spring將向這兩個(gè)方法注入新的bean實(shí)例和bean名稱。
接下來嚷掠,我們會(huì)在XML配置中定義BookBean和BookBeanProcessor作為bean捏检。
配置代碼如下:
<?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-3.0.xsd">
<bean id="bookBeanPost" class="guru.springframework.springbeanlifecycle.beanpostprocessor.domain.BookBean">
<property name="bookName" value="Gone with the Wind"></property>
</bean>
<bean id="bookBeanPostProcessor"
class="guru.springframework.springbeanlifecycle.beanpostprocessor.domain.BookBeanPostProcessor"/>
</beans>
測(cè)試我們的BeanPostProcessor的代碼如下:
package guru.springframework.springbeanlifecycle;
import guru.springframework.springbeanlifecycle.beanpostprocessor.domain.BookBean;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
@SpringBootApplication
public class SpringBeanLifecycleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBeanLifecycleApplication.class, args);
// -------beanpostprocessor------
ApplicationContext context4 =
new ClassPathXmlApplicationContext("beans.xml");
BookBean bookBean = (BookBean) context4.getBean("bookBeanPost");
((AbstractApplicationContext) context4).registerShutdownHook();
}
}
輸出如下:
- Constructor of BookBean called !!
- Post Process After Initialization method is called: Bean Name bookBeanPost
- Post Process Before Initialization method is called: Bean Name bookBeanPost
InitializingBean和DisposableBean回調(diào)接口
Spring提供下列兩個(gè)回調(diào)接口:
- InitializingBean:聲明了afterPropertiesSet()方法,能夠用來編寫初始化邏輯不皆。容器在屬性設(shè)置完成后調(diào)用這個(gè)方法贯城。
-
DisposableBean:聲明了destroy()方法,能夠用來編寫清理代碼霹娄。容器在關(guān)閉的bean銷毀期間調(diào)用此方法
讓我們來編寫一個(gè)實(shí)現(xiàn)了InitializingBean和DisposableBean接口的bean能犯。
bean Book的代碼如下:
package guru.springframework.springbeanlifecycle.callbackinterfaces.domain;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
public class Book implements InitializingBean, DisposableBean {
private String bookName;
public Book() {
System.out.println("Constructor of Book bean is called !! ");
}
@Override
public void destroy() throws Exception {
System.out.println("Destroy method of Book bean called !! ");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("afterPropertiesSet method of Book bean is called !! ");
}
public Book(String bookName) {
this.bookName = bookName;
}
public String getBookName() {
eturn bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
@Override
public String toString() {
return "Book{" +
"bookName='" + bookName + '\'' +
'}';
}
}
前面的Book bean實(shí)現(xiàn)了InitializingBean和DisposableBean接口,并覆蓋了它們的afterPropertiesSet()和destroy()方法犬耻。
接下來踩晶,我們要編寫bean配置來定義Book的bean。
beans.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-3.0.xsd">
<!-- callbackinterfaces-->
<bean id="bookBean" class="guru.springframework.springbeanlifecycle.callbackinterfaces.domain.Book">
<property name="bookName" value="Believe in Yourself"/>
</bean>
</beans>
main類如下
package guru.springframework.springbeanlifecycle;
import guru.springframework.springbeanlifecycle.callbackinterfaces.domain.Book;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
@SpringBootApplication
public class SpringBeanLifecycleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBeanLifecycleApplication.class, args);
// -------callbackinterfaces-------
ApplicationContext context =
new ClassPathXmlApplicationContext("beans.xml");
Book book = (Book) context.getBean("bookBean");
System.out.println(book.getBookName());
((AbstractApplicationContext) context).registerShutdownHook();
}
}
前面的代碼從ApplicationContext中取出了Book的bean枕磁,并打印出了bookName屬性渡蜻。
運(yùn)行主類的輸出如下:
- The constructor of Book bean is called !!
- afterPropertiesSet method of Book bean is called !!
- Believe in Yourself
- destroy method of Book bean is called !!
正如您在輸出中所注意到的,afterPropertiesSet()方法首先被調(diào)用计济。
自定義初始化和銷毀方法
當(dāng)在XML配置中聲明bean時(shí)茸苇,你可以在tag中指定init-method和destroy-method屬性。這兩個(gè)屬性都指定了bean類中的自定義方法沦寂。
init-method屬性中聲明的方法是在Spring通過setter或構(gòu)造函數(shù)參數(shù)初始化bean屬性之后調(diào)用的学密。您可以使用此方法來驗(yàn)證注入的屬性或執(zhí)行任何其他任務(wù)。
Spring在銷毀bean之前調(diào)用destroy-method屬性中聲明的方法传藏。
讓我們使用一個(gè)名為BookCustomBean的bean中的自定義init和destroy方法则果。
BookCustomBean的代碼如下:
package guru.springframework.springbeanlifecycle.custominitanddestroy.domain;
public class BookCustomBean {
private String bookName;
public BookCustomBean() {
System.out.println("Constructor of BookCustomBean bean is called !! ");
}
public void customDestroy() throws Exception {
System.out.println("Custom destroy method of BookCustomBean called !! ");
}
public void customInit() throws Exception {
System.out.println("Custom Init method of BookCustomBean called !! ");
}
public BookCustomBean(String bookName) {
this.bookName = bookName;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
@Override
public String toString() {
return "Book{" +
"bookName='" + bookName + '\'' +
'}';
}
}
在前面的代碼中幔翰,customInit和customDestroy是打印輸出消息的常規(guī)方法。
接下來西壮,我們將編寫bean的配置遗增,bean.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-3.0.xsd">
<!-- Declare custom init and destroy methods-->
<bean id="customLifeCycleBookBean"
class="guru.springframework.springbeanlifecycle.custominitanddestroy.domain.BookCustomBean"
init-method="customInit"
destroy-method="customDestroy">
<property name="bookName" value="Life and Laughing"></property>
</bean>
</beans>
在前面的代碼中款青,第11行-第12行使用值customInit和customDestroy的init-method和destroy-method屬性做修。
main類的代碼如下:
package guru.springframework.springbeanlifecycle;
import guru.springframework.springbeanlifecycle.custominitanddestroy.domain.BookCustomBean;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
@SpringBootApplication
public class SpringBeanLifecycleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBeanLifecycleApplication.class, args);
// -------custominitanddestroy------
ApplicationContext context3 =
new ClassPathXmlApplicationContext("beans.xml");
BookCustomBean bookCustomBean = (BookCustomBean) context3.getBean("customLifeCycleBookBean");
((AbstractApplicationContext) context3).registerShutdownHook();
}
}
前面的代碼加載XML配置并測(cè)試init-method和destroy-method。
運(yùn)行代碼輸出如下:
- Constructor of BookCustomBean bean is called !!
- Custom Init method of BookCustomBean called !!
- Custom destroy method of BookCustomBean called !!
總結(jié)
所有Spring bean都要經(jīng)歷一個(gè)特定的生命周期抡草,正如我們所看到的饰及,在底層實(shí)際上有很多事情要做。這些大部分都是由框架來處理的康震,作為一個(gè)Spring開發(fā)人員燎含,你很少需要經(jīng)常使用它。然而腿短,當(dāng)你使用Spring框架進(jìn)入越來越復(fù)雜的應(yīng)用程序時(shí)屏箍,有時(shí)您必須了解bean生命周期中發(fā)生的事情。
我個(gè)人不喜歡使用InitializingBean和DisposableBean接口橘忱。主要是因?yàn)樗鼘⒋a與Spring緊密地耦合在一起赴魁。更好的方法是在bean配置文件中指定init-method和destroy-method屬性。