Spring Bean 生命周期

时间:2020-02-23 14:35:45  来源:igfitidea点击:

今天,我们将探讨Spring Bean生命周期。
Spring Bean是任何Spring应用程序中最重要的部分。
Spring ApplicationContext负责初始化spring bean配置文件中定义的Spring Bean。

Spring Bean 生命周期

Spring Context还负责通过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方法和销毁前方法均不应具有参数,但它们可以引发Exceptions。
对于这些方法调用,我们还需要从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.theitroad.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.theitroad.spring.service;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

import com.theitroad.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("hyman");
		}
	}
}

Spring Bean生命周期–自定义初始化后,销毁前

由于我们不希望我们的服务具有直接的Spring框架依赖关系,因此我们创建另一种形式的Employee Service类,其中将具有后初始化和销毁前的Spring生命周期方法,我们将在spring bean配置文件中对其进行配置。

package com.theitroad.spring.service;

import com.theitroad.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");
	}

	//pre-destroy method
	public void destroy() throws Exception {
		System.out.println("MyEmployeeService Closing resources");
	}

	//post-init method
	public void init() throws Exception {
		System.out.println("MyEmployeeService initializing to dummy value");
		if(employee.getName() == null){
			employee.setName("hyman");
		}
	}
}

我们将稍微研究一下spring bean配置文件。
在此之前,让我们创建另一个将使用@PostConstruct和@PreDestroy批注的服务类。

Spring Bean生命周期– @ PostConstruct,@ PreDestroy

下面是一个将配置为spring bean的简单类,对于后初始化和销毁前方法,我们使用@PostConstruct和@PreDestroy批注。

package com.theitroad.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上下文文件中配置我们的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">

<!-- Not initializing employee name variable-->
<bean name="employee" class="com.theitroad.spring.bean.Employee" 

<bean name="employeeService" class="com.theitroad.spring.service.EmployeeService">
	<property name="employee" ref="employee"></property>
</bean>

<bean name="myEmployeeService" class="com.theitroad.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.theitroad.spring.service.MyService" 
</beans>

请注意,我没有在bean定义中初始化员工姓名。
由于EmployeeService正在使用接口,因此我们在这里不需要任何特殊配置。

对于MyEmployeeService bean,我们使用init-method和destroy-method属性来让spring框架知道要执行的自定义方法。

MyService bean配置没有什么特别的,但是您可以看到我正在为此启用基于注释的配置。

我们的应用程序已准备就绪,让我们编写一个测试程序,以了解如何执行不同的方法。

Spring Bean 生命周期–测试程序

package com.theitroad.spring.main;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.theitroad.spring.service.EmployeeService;
import com.theitroad.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=hyman
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 生命周期要点:

  • 从控制台输出中可以明显看出,Spring Context首先使用no-args构造函数来初始化bean对象,然后调用post-init方法。

  • bean初始化的顺序与spring bean配置文件中定义的顺序相同。

  • 仅当使用post-init方法执行正确地初始化了所有spring bean时,才返回上下文。

  • 员工名称打印为" hyman",因为它是在post-init方法中初始化的。

  • 当上下文关闭时,将按照初始化时的相反顺序(即,按LIFO(后进先出)的顺序)破坏bean。

您可以取消注释代码,以获取类型为MyEmployeeService的bean,并确认输出将相似并遵循上述所有要点。

Spring Aware接口

有时,我们需要在bean中使用Spring Framework对象来执行某些操作,例如读取ServletConfig和ServletContext参数或者了解ApplicationContext加载的bean定义。
这就是为什么spring框架提供了一堆* 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配置参数。

通过在我们将配置为Spring Bean的类中实现其中的少数几个,让我们看看这些Aware接口的实际使用情况。

package com.theitroad.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示例配置文件

非常简单的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.theitroad.spring.bean.Employee" 

<bean name="myAwareService" class="com.theitroad.spring.service.MyAwareService" 

</beans>

Spring*意识测试程序

package com.theitroad.spring.main;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.theitroad.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