Spring Bean的生命周期过程
写在前面
The Spring IoC (Inversion of Control) container manages Spring beans. A “Spring bean” is just a Spring-managed instantiation of a Java class.
The Spring IoC container is responsible for instantiating, initializing, and wiring beans. The container also manages the life cycle of beans.
You may also like: Playing Around With Spring Bean Configuration
Spring provides several ways through which you can tap into the bean lifecycle. For example, once a bean is instantiated, you might need to perform some initialization to get the bean into a usable state. Similarly, you might need to clean up resources before a bean is removed from the container.
In this post, we will examine the steps of Spring bean lifecycle. This is how the Spring Framework creates and destroys Spring beans.
Spring中IoC容器负责管理Spring Bean对象,bean只是Spring实例的Java类。
IoC容器负责实例化,初始化和接线bean。 容器还管理bean的生命周期。
Spring提供了几种进入bean生命周期的方法。 例如,实例化一个bean之后,您可能需要执行一些初始化操作以使bean进入可用状态。 同样,在从容器中删除Bean之前,您可能需要清理资源。
在本文中,我们将研究Spring bean生命周期的步骤。 Spring框架就是这样创建和销毁Spring Bean的。
一、Spring Bean Lifecycle Overview
The figure below shows two parts of the Spring bean lifecycle:
Part 1: Shows the different stages a bean goes through after instantiation until it is ready for use.
Part 2: Shows what happens to a bean once the Spring IoC container shuts down.
As you can see in Part 1 of the preceding figure, the container instantiates a bean by calling its constructor and then populates its properties.
This is followed by several calls to the bean until the bean is in the ready state.
Similarly, as shown in Part 2, when the container shuts down, the container calls the bean to enable it to perform any required tasks before the bean is destroyed.
如您在上图的第1部分中看到的,容器通过调用其构造函数实例化一个bean,然后填充其属性。
接下来是对bean的多次调用,直到bean处于就绪状态。
类似地,如第2部分中所示,当容器关闭时,容器调用Bean以使其能够在销毁Bean之前执行任何必需的任务。
二、Aware Interfaces
Spring provides several aware interfaces. These are used to access the Spring Framework infrastructure. The aware interfaces are largely used within the framework and rarely used by Spring programmers.
You as Spring programmers should be familiar with the following three aware interfaces.
BeanFactoryAware: Provides setBeanFactory(), a callback that supplies the owning factory to the bean instance.
BeanNameAware: ThesetBeanName() callback of this interface supplies the name of the bean.
ApplicationContextAware: ThesetApplicationContext() method of this interface provides the ApplicationContext object of this bean.
Spring提供了几个感知接口。 用于访问Spring框架中的基础结构。 感知接口主要在框架内使用,一般开发中也用不到。
作为Spring程序员,您应该熟悉以下三个感知接口。
- BeanFactoryAware:提供setBeanFactory(),该回调将拥有工厂的实例提供给Bean实例。
- BeanNameAware:此接口的ThesetBeanName()回调提供Bean的名称。
- ApplicationContextAware:此接口的ThesetApplicationContext()方法提供此bean的ApplicationContext对象。
The code to use the preceding aware interfaces is this.
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()));
}
}
The preceding bean implements the ApplicationContextAware, BeanNameAware, and BeanFactoryAware interfaces. In the preceding code:
Line 13 – Line 18: The code overrides the setBeanFactory() method of the BeanFactoryAware interface. During runtime, Spring passes the BeanFactory object that created the bean. The code uses the BeanFactory object to print whether or not this bean is a singleton.
Line 20 – Line 25 overrides the setBeanName() method of the BeanNameAware interface. During runtime, Spring passes the name of the bean as a String that the code prints out. The code uses the beanName to print the bean name defined in context.
In Line 27 – Line 32, the code overrides the setApplicationContext() method of the ApplicationContextAware interface. During runtime, Spring passes the ApplicationContext object that created the bean. The code uses the ApplicationContext object to print the bean definition names.
前面的bean实现了ApplicationContextAware,BeanNameAware和BeanFactoryAware接口。 在前面的代码中:
第13行-第18行:该代码重写BeanFactoryAware接口的setBeanFactory()方法。 在运行时,Spring传递创建Bean的BeanFactory对象。 该代码使用BeanFactory对象来打印此bean是否为单例。
第20行-第25行覆盖BeanNameAware接口的setBeanName()方法。 在运行时,Spring将bean的名称作为String传递,代码将输出。 该代码使用beanName来打印在上下文中定义的bean名称。
在第27行–第32行中,代码覆盖了ApplicationContextAware接口的setApplicationContext()方法。 在运行时,Spring传递创建Bean的ApplicationContext对象。 该代码使用ApplicationContext对象打印Bean定义名称。
Next, we will write the bean configuration to define the AwareBeanImpl. The code of the beans.xml is this.
<?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="spring.lifecycle.AwareBeanImpl"></bean>
</beans>
Finally, let us write the main class, which will load the beans.xml and test the aware interface methods.
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();
}
}
The output on running the main class is this:
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 Post Processor
Spring provides the BeanPostProcessor interface that gives you the means to tap into the Spring context lifecycle and interact with beans as they are processed.
The BeanPostProcessor interface contains two methods:
postProcessBeforeInitialization: Spring calls this method after calling the methods of the aware interfaces and before any bean initialization callbacks, such as InitializingBean’s afterPropertiesSet or a custom init-method.
postProcessAfterInitialization: Spring calls this method after any bean initialization callbacks.
Spring提供了BeanPostProcessor接口,该接口使您可以利用Spring上下文生命周期并在处理bean时与之交互。
BeanPostProcessor接口包含两种方法:
postProcessBeforeInitialization:Spring在调用感知接口的方法之后,在任何bean初始化回调(例如InitializingBean的afterPropertiesSet或自定义的init方法)之前调用此方法。
postProcessAfterInitialization:Spring在任何bean初始化回调之后调用此方法。
Let us start by creating a bean, named BookBean.
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 + '\'' +
'}';
}
}
Next, we will create the BookBeanPostProcessor.
The code for BookBeanPostProcessor is this:
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;
}
}
The preceding code implements the BeanPostProcessor interface and overrides the postProcessBeforeInitialization() and postProcessAfterInitialization() methods.
Spring calls the postProcessBeforeInitialization() method after calling the methods of the aware interfaces.
Spring calls the postProcessAfterInitialization() method after any bean initialization callbacks, such as InitializingBean’s afterPropertiesSet or a custom init-method. We will discuss both going ahead.
At runtime, Spring will inject the new bean instance and the name of the bean to both the methods.
Next, we will define BookBean and BookBeanProcessor as beans in the XML configuration.
前面的代码实现了BeanPostProcessor接口,并覆盖了postProcessBeforeInitialization()和postProcessAfterInitialization()方法。
Spring在调用感知接口的方法之后调用postProcessBeforeInitialization()方法。
Spring在任何bean初始化回调(例如InitializingBean的afterPropertiesSet或自定义的init-method)之后调用postProcessAfterInitialization()方法。 我们将讨论两者。
在运行时,Spring会将新的bean实例和bean的名称注入这两种方法。
接下来,我们将在XML配置中将BookBean和BookBeanProcessor定义为bean。
The configuration code is this.
<?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>
The main class to test our BeanPostProcessor is this.
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();
}
}
The output on running the main class is this.
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 and DisposableBean Callback Interfaces
Spring provides the following two callback interfaces:
InitializingBean: Declares the afterPropertiesSet() method which can be used to write the initialization logic. The container calls the method after properties are set.
DisposableBean: Declares the destroy() method which can be used to write any clean up code. The container calls this method during bean destruction in shutdown.
InitializingBean和DisposableBean回调接口
Spring提供了以下两个回调接口:
-
InitializingBean:声明afterPropertiesSet()方法,该方法可用于编写初始化逻辑。 设置属性后,容器将调用该方法。
-
DisposableBean:声明可以用于编写任何清理代码的destroy()方法。 容器在关闭状态下销毁bean时调用此方法。
Let’s write a bean that implements the InitalizingBean and DisposableBean interfaces.
The code of the Book bean is this.
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() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
@Override
public String toString() {
return "Book{" +
"bookName='" + bookName + '\'' +
'}';
}
}
The preceding Book bean implements the InitializingBean and DisposableBean interfaces and overrides their afterPropertiesSet() and destroy() method.
Next, we will write the bean configuration to define the Book bean.
The code of the beans.xml file is this.
<?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>
The main class is this.
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();
}
}
The preceding code retrieves the Book bean from the ApplicationContext and prints out the value of the bookName property.
The output on running the main class is this.
The constructor of Book bean is called !!
afterPropertiesSet method of Book bean is called !!
Believe in Yourself
destroy method of Book bean is called !!
As you can note in the output, the afterPropertiesSet() method got called first.
四、Custom Init and Destroy Method
While declaring bean in XML configuration, you can specify the init-method and destroy-method attributes in the tag. Both the attributes specify custom methods in the bean class.
The method declared in the init-method attribute is called after Spring initializes bean properties through setter or constructor arguments. You can use this method to validate the injected properties or perform any other tasks.
Spring calls the method declared in the destroy-method attribute just before the bean is destroyed.
在XML配置中声明bean时,可以在标记中指定init-method和destroy-method属性。 这两个属性都在bean类中指定了自定义方法。
在Spring通过setter或构造函数参数初始化bean属性之后,将调用init-method属性中声明的方法。 您可以使用此方法来验证注入的属性或执行任何其他任务。
Spring会在销毁bean之前调用在destroy-method属性中声明的方法。
Let’s use the custom init and destroy methods in a bean, named BookCustomBean.
The code for BookCustomBean is this.
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 + '\'' +
'}';
}
}
In the preceding code, customInit and customDestroy are regular methods that prints output messages.
在前面的代码中,customInit和customDestroy是打印输出消息的常规方法。
Next, we’ll write the bean configuration, 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">
<!-- 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>
In the preceding code, Line 11 – Line 12 uses the init-method and destroy-method attributes with the values customInit and customDestroy.
The code of the main class is this.
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();
}
}
The preceding code loads the XML configuration and tests the init-method and destroy-method.
The output on running the preceding code is this.
Constructor of BookCustomBean bean is called !!
Custom Init method of BookCustomBean called !!
Custom destroy method of BookCustomBean called !!
五、Summary
All Spring beans go through a specific lifecycle, and as we have seen, there’s actually a lot that goes on under the hood. Most of this is handled by the framework, and as a Spring developer, you will seldom require to get into it that often. However, as you get into more and more complex applications with the Spring Framework, at times, you have to be aware of what goes on during a bean lifecycle.
I personally don’t prefer using the InitializingBean and DisposableBean interfaces. Primarily because it tightly couples your code to Spring. A better approach is specifying the init-method and destroy-method attributes in your bean configuration file.
所有的Spring Bean都经历特定的生命周期,而且正如我们所看到的,实际上还有很多事情要做。 其中大部分由框架处理,作为Spring开发人员,您几乎不需要经常进入该框架。 但是,随着使用Spring Framework进入越来越复杂的应用程序,有时,您必须了解bean生命周期中发生的事情。
我个人不喜欢使用InitializingBean和DisposableBean接口。 主要是因为它将代码紧密地耦合到Spring。 更好的方法是在bean配置文件中指定init-method和destroy-method属性。