Spring Bean生命周期
今天我们将研究Spring Bean生命周期。Spring Bean是任何Spring应用程序中最重要的部分。Spring ApplicationContext负责初始化spring bean配置文件中定义的Spring Beans。
目录[ 隐藏 ]
- 1 Spring Bean生命周期
- 1.1 Spring Bean生命周期 - @ PostConstruct,@ PreDestroy注释
- 1.2 Spring Bean生命周期 - Maven依赖
- 1.3 Spring Bean生命周期 - 模型类
- 1.4 Spring Bean生命周期 - InitializingBean,DisposableBean
- 1.5 Spring Bean生命周期 - 自定义post-init,pre-destroy
- 1.6 Spring Bean生命周期 - @ PostConstruct,@ PreDestroy
- 1.7 Spring Bean生命周期 - 配置文件
- 1.8 Spring Bean生命周期 - 测试程序
- 2个 Spring Aware接口
Spring Bean生命周期
Spring上下文还负责注射的依赖 bean中,无论是通过制定者或构造方法或通过弹簧自动装配。
有时我们想要初始化bean类中的资源,例如在任何客户端请求之前初始化时创建数据库连接或验证第三方服务。Spring框架提供了不同的方法,通过它我们可以在spring bean生命周期中提供后初始化和预破坏方法。
- 通过实现InitializingBean和DisposableBean接口 - 这两个接口都声明了一个方法,我们可以在其中初始化/关闭bean中的资源。对于后期初始化,我们可以实现
InitializingBean
接口并提供afterPropertiesSet()
方法的实现。对于pre-destroy,我们可以实现DisposableBean
接口并提供destroy()
方法的实现。这些方法是回调方法,类似于servlet侦听器实现。这种方法易于使用,但不推荐使用,因为它会在我们的bean实现中与Spring框架建立紧密耦合。
- 在spring bean配置文件中为bean 提供init-method和destroy-method属性值。这是推荐的方法,因为没有直接依赖于spring框架,我们可以创建自己的方法。
请注意,init-init和pre-destroy方法都应该没有参数,但是它们可以抛出异常。我们还需要从spring应用程序上下文中获取bean实例以进行这些方法调用。
Spring Bean生命周期 - @ PostConstruct,@ PreDestroy注释
Spring框架还支持@PostConstruct
和@PreDestroy
注释用于定义post-init和pre-destroy方法。这些注释是javax.annotation
包的一部分。但是,要使这些注释起作用,我们需要配置spring应用程序以查找注释。我们可以org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
通过context:annotation-config
在spring bean配置文件中定义bean类型或元素来实现。
让我们编写一个简单的Spring应用程序来展示上述配置对spring bean生命周期管理的使用。在Spring Tool Suite中创建一个Spring Maven项目,最终项目将如下图所示。
Spring Bean生命周期 - Maven依赖
我们不需要包含任何额外的依赖项来配置spring bean生命周期方法,我们的pom.xml文件就像任何其他标准的spring maven项目一样。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://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
让我们创建一个服务类,我们将实现post-init和pre-destroy方法的接口。
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生命周期 - 自定义post-init,pre-destroy
由于我们不希望我们的服务具有直接的spring框架依赖性,让我们创建另一种形式的Employee Service类,我们将使用post-init和pre-destroy 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");
}
//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("Pankaj");
}
}
}
我们将稍微研究一下spring bean配置文件。在此之前,让我们创建另一个将使用@PostConstruct和@PreDestroy注释的服务类。
Spring Bean生命周期 - @ PostConstruct,@ PreDestroy
下面是一个简单的类,它将配置为spring bean,对于post-init和pre-destroy方法,我们使用@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上下文文件中配置我们的bean。
<?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.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生命周期 - 测试计划
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生命周期重点:
- 从控制台输出中可以清楚地看到,Spring Context首先使用no-args构造函数来初始化bean对象,然后调用post-init方法。
- bean初始化的顺序与spring bean配置文件中定义的顺序相同。
- 仅当使用post-init方法执行正确初始化所有spring bean时才返回上下文。
- 员工姓名打印为“Pankaj”,因为它是在post-init方法中初始化的。
- 当上下文被关闭时,bean按照它们被初始化的相反顺序被销毁,即以LIFO(后进先出)顺序。
您可以取消注释代码以获取类型的bean MyEmployeeService
并确认输出将类似并遵循上面提到的所有要点。
Spring Aware接口
有时我们需要在bean中使用Spring Framework对象来执行某些操作,例如读取ServletConfig和ServletContext参数或者知道ApplicationContext加载的bean定义。这就是为什么spring框架提供了一堆我们可以在我们的bean类中实现的* Aware接口。
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.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示例配置文件
非常简单的spring bean配置文件。
<?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.xsd">
<bean name="employee" class="com.journaldev.spring.bean.Employee" />
<bean name="myAwareService" class="com.journaldev.spring.service.MyAwareService" />
</beans>
Spring * Aware测试程序
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 bean中。请从下面链接下载示例项目并进行分析以了解有关它们的更多信息。