今天我們將深入研究Spring Bean的生命周期。 Spring Beans 是任何Spring應用程序最重要的部分。Spring ApplicationContext負責初始化在Spring bean配置文件中定義的Spring Beans。
Spring Bean的生命周期
Spring上下文還負責通過setter或構造函數方法,或通過spring自動裝配來注入依賴項到bean中。有時,我們希望在bean類中初始化資源,例如在任何客戶端請求之前初始化數據庫連接或驗證第三方服務。Spring框架提供了不同的方法,通過這些方法,我們可以在Spring Bean生命周期中提供後初始化和銷毀前的方法。
- 通過實現InitializingBean和DisposableBean接口 – 這兩個接口都聲明了一個方法,我們可以在該bean中初始化/關閉資源。對於後初始化,我們可以實現
InitializingBean
接口並提供afterPropertiesSet()
方法的實現。對於預銷毀,我們可以實現DisposableBean
接口並提供destroy()
方法的實現。這些方法是回調方法,與servlet監聽器實現相似。這種方法簡單易用,但是不推薦使用,因為它會在我們的bean實現中與Spring框架緊密耦合。 - 在spring bean配置文件中為bean提供init-method和destroy-method屬性值。這是推薦的方法,因為沒有直接依賴於spring框架,我們可以創建自己的方法。
請注意,post-init和pre-destroy方法應該沒有參數,但是它們可以拋出異常。我們還需要從spring應用上下文中獲取這些方法調用的bean實例。
Spring Bean生命週期 – @PostConstruct, @PreDestroy註解
Spring框架也支援@PostConstruct
和@PreDestroy
註釋,用於定義後初始化和前銷毀方法。這些註釋是javax.annotation
套件的一部分。但是,為了使這些註釋起作用,我們需要配置我們的Spring應用程序來查找註釋。我們可以通過定義類型為org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
的bean,或者通過在Spring bean配置文件中使用context:annotation-config
元素來完成這一點。讓我們編寫一個簡單的Spring應用程序來展示如上配置用於Spring bean生命周期管理。在Spring Tool Suite中創建一個Spring Maven項目,最終項目將如下圖所示。
Spring Bean生命周期 – Maven依賴項
我們不需要包含任何額外的依賴項來配置Spring bean生命周期方法,我們的pom.xml文件就像任何其他標準的Spring Maven項目一樣。
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.samples</groupId>
<artifactId>SpringBeanLifeCycle</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<!-- Generic properties -->
<java.version>1.7</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- Spring -->
<spring-framework.version>4.0.2.RELEASE</spring-framework.version>
<!-- Logging -->
<logback.version>1.0.13</logback.version>
<slf4j.version>1.7.5</slf4j.version>
</properties>
<dependencies>
<!-- Spring and Transactions -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<!-- Logging with SLF4J & LogBack -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>
Spring Bean生命周期 – 模型類
讓我們創建一個簡單的Java bean類,將在服務類中使用。
package com.journaldev.spring.bean;
public class Employee {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Spring Bean生命週期 – InitializingBean,DisposableBean
讓我們創建一個服務類,在其中實現後初始化和銷毀前的接口方法。
package com.journaldev.spring.service;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import com.journaldev.spring.bean.Employee;
public class EmployeeService implements InitializingBean, DisposableBean{
private Employee employee;
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
}
public EmployeeService(){
System.out.println("EmployeeService no-args constructor called");
}
@Override
public void destroy() throws Exception {
System.out.println("EmployeeService Closing resources");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("EmployeeService initializing to dummy value");
if(employee.getName() == null){
employee.setName("Pankaj");
}
}
}
Spring Bean生命週期 – 自定義後初始化,銷毀前
由於我們不希望我們的服務直接依賴於Spring框架,讓我們創建另一種形式的Employee Service類,在其中有後初始化和銷毀前的Spring生命週期方法,並將它們配置在Spring bean配置文件中。
package com.journaldev.spring.service;
import com.journaldev.spring.bean.Employee;
public class MyEmployeeService{
private Employee employee;
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
}
public MyEmployeeService(){
System.out.println("MyEmployeeService no-args constructor called");
}
//銷毀前的方法
public void destroy() throws Exception {
System.out.println("MyEmployeeService Closing resources");
}
//後初始化的方法
public void init() throws Exception {
System.out.println("MyEmployeeService initializing to dummy value");
if(employee.getName() == null){
employee.setName("Pankaj");
}
}
}
稍後我們將查看Spring bean配置文件。在此之前,讓我們創建另一個使用@PostConstruct和@PreDestroy註解的服務類。
Spring Bean生命周期 – @PostConstruct、@PreDestroy
以下是一個簡單的類別,將被配置為Spring Bean,並使用@PostConstruct和@PreDestroy註釋來進行後初始化和前銷毀方法。
package com.journaldev.spring.service;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
public class MyService {
@PostConstruct
public void init(){
System.out.println("MyService init method called");
}
public MyService(){
System.out.println("MyService no-args constructor called");
}
@PreDestroy
public void destory(){
System.out.println("MyService destroy method called");
}
}
Spring Bean生命周期 – 配置檔案
讓我們看看如何在Spring上下文檔案中配置我們的Beans。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- Not initializing employee name variable-->
<bean name="employee" class="com.journaldev.spring.bean.Employee" />
<bean name="employeeService" class="com.journaldev.spring.service.EmployeeService">
<property name="employee" ref="employee"></property>
</bean>
<bean name="myEmployeeService" class="com.journaldev.spring.service.MyEmployeeService"
init-method="init" destroy-method="destroy">
<property name="employee" ref="employee"></property>
</bean>
<!-- initializing CommonAnnotationBeanPostProcessor is same as context:annotation-config -->
<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />
<bean name="myService" class="com.journaldev.spring.service.MyService" />
</beans>
請注意,我們在bean定義中未初始化員工名稱。由於EmployeeService使用接口,我們在這裡不需要任何特殊的配置。對於MyEmployeeService bean,我們使用init-method和destroy-method屬性讓Spring框架知道要執行的自定義方法。MyService bean配置沒有任何特殊之處,但是你可以看到我在這裡啟用了基於註釋的配置。我們的應用程序已經準備就緒,讓我們撰寫一個測試程序來看看不同的方法如何執行。
Spring Bean Life Cycle – Test Program
package com.journaldev.spring.main;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.journaldev.spring.service.EmployeeService;
import com.journaldev.spring.service.MyEmployeeService;
public class SpringMain {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
System.out.println("Spring Context initialized");
//MyEmployeeService service = ctx.getBean("myEmployeeService", MyEmployeeService.class);
EmployeeService service = ctx.getBean("employeeService", EmployeeService.class);
System.out.println("Bean retrieved from Spring Context");
System.out.println("Employee Name="+service.getEmployee().getName());
ctx.close();
System.out.println("Spring Context Closed");
}
}
執行上述測試程序時,我們獲得以下輸出。
Apr 01, 2014 10:50:50 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@c1b9b03: startup date [Tue Apr 01 22:50:50 PDT 2014]; root of context hierarchy
Apr 01, 2014 10:50:50 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring.xml]
EmployeeService no-args constructor called
EmployeeService initializing to dummy value
MyEmployeeService no-args constructor called
MyEmployeeService initializing to dummy value
MyService no-args constructor called
MyService init method called
Spring Context initialized
Bean retrieved from Spring Context
Employee Name=Pankaj
Apr 01, 2014 10:50:50 PM org.springframework.context.support.ClassPathXmlApplicationContext doClose
INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@c1b9b03: startup date [Tue Apr 01 22:50:50 PDT 2014]; root of context hierarchy
MyService destroy method called
MyEmployeeService Closing resources
EmployeeService Closing resources
Spring Context Closed
Spring Bean Life Cycle 重要點:
- 從控制台輸出清楚地顯示,Spring 上下文首先使用無參數構造函數初始化 bean 對象,然後調用 post-init 方法。
- bean 初始化的順序與其在 spring bean 配置文件中的定義相同。
- 僅當所有 spring bean 使用 post-init 方法正確初始化後,上下文才會返回。
- 員工名稱顯示為“Pankaj”,因為它是在 post-init 方法中初始化的。
- 當上下文被關閉時,bean 將以它們初始化的相反順序(即後進先出,LIFO)銷毀。
您可以取消註釋代碼以獲取類型為MyEmployeeService
的 bean,並確認輸出將類似並遵循上述所有要點。
Spring Aware 介面
有时候我们需要在我们的bean中使用Spring Framework对象执行一些操作,例如读取ServletConfig和ServletContext参数,或者了解由ApplicationContext加载的bean定义。这就是为什么Spring Framework提供了一堆*Aware接口,我们可以在我们的bean类中实现这些接口。`org.springframework.beans.factory.Aware`是所有这些Aware接口的根标记接口。所有*Aware接口都是Aware的子接口,并声明一个由bean实现的单个setter方法。然后,Spring上下文使用基于setter的依赖注入将相应的对象注入bean,并使其可供我们使用。Spring Aware接口类似于servlet监听器,具有回调方法,并实现观察者设计模式。一些重要的Aware接口包括:
- ApplicationContextAware – 用于注入ApplicationContext对象,示例用法是获取bean定义名称的数组。
- BeanFactoryAware – 用于注入BeanFactory对象,示例用法是检查bean的作用域。
- BeanNameAware – 用于了解在配置文件中定义的bean名称。
- ResourceLoaderAware – 注入ResourceLoader對象,示例用法是獲取類路徑中文件的輸入流。
- ServletContextAware – 在MVC應用程序中注入ServletContext對象,示例用法是讀取上下文參數和屬性。
- ServletConfigAware – 在MVC應用程序中注入ServletConfig對象,示例用法是獲取Servlet配置參數。
讓我們通過在一個類中實現其中幾個 Aware 接口來看看它們的用法,我們將該類配置為 Spring Bean。
package com.journaldev.spring.service;
import java.util.Arrays;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
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 org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
public class MyAwareService implements ApplicationContextAware,
ApplicationEventPublisherAware, BeanClassLoaderAware, BeanFactoryAware,
BeanNameAware, EnvironmentAware, ImportAware, ResourceLoaderAware {
@Override
public void setApplicationContext(ApplicationContext ctx)
throws BeansException {
System.out.println("setApplicationContext called");
System.out.println("setApplicationContext:: Bean Definition Names="
+ Arrays.toString(ctx.getBeanDefinitionNames()));
}
@Override
public void setBeanName(String beanName) {
System.out.println("setBeanName called");
System.out.println("setBeanName:: Bean Name defined in context="
+ beanName);
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
System.out.println("setBeanClassLoader called");
System.out.println("setBeanClassLoader:: ClassLoader Name="
+ classLoader.getClass().getName());
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
System.out.println("setResourceLoader called");
Resource resource = resourceLoader.getResource("classpath:spring.xml");
System.out.println("setResourceLoader:: Resource File Name="
+ resource.getFilename());
}
@Override
public void setImportMetadata(AnnotationMetadata annotationMetadata) {
System.out.println("setImportMetadata called");
}
@Override
public void setEnvironment(Environment env) {
System.out.println("setEnvironment called");
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("setBeanFactory called");
System.out.println("setBeanFactory:: employee bean singleton="
+ beanFactory.isSingleton("employee"));
}
@Override
public void setApplicationEventPublisher(
ApplicationEventPublisher applicationEventPublisher) {
System.out.println("setApplicationEventPublisher called");
}
}
Spring *Aware Example Configuration File
非常簡單的Spring Bean配置文件。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="employee" class="com.journaldev.spring.bean.Employee" />
<bean name="myAwareService" class="com.journaldev.spring.service.MyAwareService" />
</beans>
Spring *Aware Test Program
package com.journaldev.spring.main;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.journaldev.spring.service.MyAwareService;
public class SpringAwareMain {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring-aware.xml");
ctx.getBean("myAwareService", MyAwareService.class);
ctx.close();
}
}
現在當我們執行上述類時,我們會得到以下輸出。
Apr 01, 2014 11:27:05 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@60a2f435: startup date [Tue Apr 01 23:27:05 PDT 2014]; root of context hierarchy
Apr 01, 2014 11:27:05 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring-aware.xml]
setBeanName called
setBeanName:: Bean Name defined in context=myAwareService
setBeanClassLoader called
setBeanClassLoader:: ClassLoader Name=sun.misc.Launcher$AppClassLoader
setBeanFactory called
setBeanFactory:: employee bean singleton=true
setEnvironment called
setResourceLoader called
setResourceLoader:: Resource File Name=spring.xml
setApplicationEventPublisher called
setApplicationContext called
setApplicationContext:: Bean Definition Names=[employee, myAwareService]
Apr 01, 2014 11:27:05 PM org.springframework.context.support.ClassPathXmlApplicationContext doClose
INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@60a2f435: startup date [Tue Apr 01 23:27:05 PDT 2014]; root of context hierarchy
測試程序的控制台輸出很容易理解,我不會詳細介紹。這就是 Spring Bean 生命週期方法和將框架特定對象注入 Spring Beans 的全部內容。請從下面的鏈接下載示例項目並進行分析以了解更多信息。
Source:
https://www.digitalocean.com/community/tutorials/spring-bean-life-cycle