原文:Spring Bean Lifecycle
Spring IoC(Inversion of Control,控制反转)容器管理着Spring中的bean。一个"Spring bean"只是一个由Spring管理的Java类的实例。
Spring的IoC容器负责实例化、初始化并写入bean,同时也管理这些bean的生命周期。
Spring提供了一些方法,你可以通过它们来进入bean的生命周期。 例如,一旦一个bean被实例化,你可能就需要执行一些初始化操作来使这个bean进入可用状态。类似地,你可能需要在一个bean被从容器移除之前进行资源的清理。
在这篇文章中,我们会研究Spring bean生命周期的各个阶段。这就是Spring框架创建和销毁Spring bean的方式。
Spring Bean 生命周期概览
下面的图片展示了Spring bean生命周期的两个部分:
Part 1:展示了一个bean在实例化之后到准备就绪可用前所经历的不同阶段。
Part 2:展示了当一个Spring Ioc容器关闭,一个bean会经历些什么。
正如你在前面图片的 Part 1中所看见的,容器通过调用它的构造器来实例化一个bean然后填充它的属性。
之后对bean进行多次调用,直到bean处于就绪状态。
类似地,像 Part 2中展示的那样,当容器关闭,容器调用bean来使其能够在销毁bean之前执行所有需要的任务。
Aware接口
Spring提供了一些aware接口,他们用来进入Spring框架的基础层。aware接口主要在框架中使用,很少被Spring程序员使用。
作为一个Spring程序员应该熟悉下面三个aware接口:
- BeanFactoryAware:提供setBeanFactory(),一个提供bean实例所属工厂的回调。
- BeanNameAware: 此接口的ThesetBeanName()回调提供了bean的名称。
- ApplicationContextAware:此接口的ThesetApplicationContext()方法提供了此bean的ApplicationContext对象
使用上述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实现了ApplicationContextAware、BeanNameAware和BeanFactoryAware接口。
代码行13-18:代码重写了BeanFactoryAware接口的setBeanFactory()方法。在运行期间,Spring传递创建bean的BeanFactory对象。该代码使用这个BeanFactory对象来打印这个bean是否是一个单例。
代码行20-25:重写了BeanNameAware接口的setBeanName()方法。在运行期间,Spring以一个String来传递这个bean的名字,被该代码打印了出来。代码使用了beanName来打印上下文中定义的bean的名字。
代码行27-32:重写了ApplicationContextAware接口的setApplicationContext()方法,在运行期间,Spring传递创建了这个bean的ApplicationContext对象。代码用这个ApplicationContext对象来打印bean定义的名字。
接下来,我们将编写bean的配置来定义AwareBeanImpl。beans.xml的代码如下:
最后,让我们编写主类,它将加载bean .xml并测试相关接口方法:
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();
}
}
运行主类的输入如下:
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接口,为你提供了进入Spring上下文生命周期和在处理bean时和他们交互的方法。
BeanPostProcessor接口包含两个方法:
- postProcessBeforeInitialization:Spring在调用aware接口的方法之后和任何bean的初始化回调之前调用此方法,例如InitializingBean的afterPropertiesSet()或者任何自定义的初始化方法。
- postProcessAfterInitialization:Spring在任何bean的初始化回调之后调用此方法。
让我们从创建一个叫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 + '\'' +
'}';
}
}
接下来,我们创建一个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;
}
}
上述代码实现了BeanPostProcessor接口并覆盖了postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法。
Spring在调用了aware接口的方法之后。调用postProcessBeforeInitialization()方法。
Spring在所有的bean初始化回调之后调用postProcessAfterInitialization()方法,例如InitializingBean的afterPropertiesSet()或者任何自定义的初始化方法。我们接下来两个都会讨论。
在运行时,Spring将向这两个方法注入新的bean实例和bean名称。
接下来,我们会在XML配置中定义BookBean和BookBeanProcessor作为bean。
配置代码如下:
测试我们的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回调接口
Spring提供下列两个回调接口:
- InitializingBean:声明了afterPropertiesSet()方法,能够用来编写初始化逻辑。容器在属性设置完成后调用这个方法。
- DisposableBean:声明了destroy()方法,能够用来编写清理代码。容器在关闭的bean销毁期间调用此方法
让我们来编写一个实现了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实现了InitializingBean和DisposableBean接口,并覆盖了它们的afterPropertiesSet()和destroy()方法。
接下来,我们要编写bean配置来定义Book的bean。
beans.xml的代码如下:
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属性。
运行主类的输出如下:
- 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()方法首先被调用。
自定义初始化和销毁方法
当在XML配置中声明bean时,你可以在tag中指定init-method和destroy-method属性。这两个属性都指定了bean类中的自定义方法。
init-method属性中声明的方法是在Spring通过setter或构造函数参数初始化bean属性之后调用的。您可以使用此方法来验证注入的属性或执行任何其他任务。
Spring在销毁bean之前调用destroy-method属性中声明的方法。
让我们使用一个名为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是打印输出消息的常规方法。
接下来,我们将编写bean的配置,bean.xml。
在前面的代码中,第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配置并测试init-method和destroy-method。
运行代码输出如下:
- Constructor of BookCustomBean bean is called !!
- Custom Init method of BookCustomBean called !!
- Custom destroy method of BookCustomBean called !!
总结
所有Spring bean都要经历一个特定的生命周期,正如我们所看到的,在底层实际上有很多事情要做。这些大部分都是由框架来处理的,作为一个Spring开发人员,你很少需要经常使用它。然而,当你使用Spring框架进入越来越复杂的应用程序时,有时您必须了解bean生命周期中发生的事情。
我个人不喜欢使用InitializingBean和DisposableBean接口。主要是因为它将代码与Spring紧密地耦合在一起。更好的方法是在bean配置文件中指定init-method和destroy-method属性。