[译]Spring Bean的生命周期

原文: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生命周期的两个部分:

[译]Spring Bean的生命周期_第1张图片
image

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实现了ApplicationContextAwareBeanNameAwareBeanFactoryAware接口。
代码行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的初始化回调之前调用此方法,例如InitializingBeanafterPropertiesSet()或者任何自定义的初始化方法。
  • 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配置中定义BookBeanBookBeanProcessor作为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实现了InitializingBeanDisposableBean接口,并覆盖了它们的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属性。

你可能感兴趣的:([译]Spring Bean的生命周期)