春季豆子种植全指南:从播种到收获的生命周期详解
Spring Bean的生命周期
这是文章《Spring Bean的生命周期》的第1部分(共5部分)。
今天我们将研究Spring Bean的生命周期。Spring的Bean是任何Spring应用程序中最重要的部分。Spring的ApplicationContext负责初始化在Spring Bean配置文件中定义的Spring Beans。
Spring Bean的生命周期
Spring Context负责在bean中注入依赖项,可以通过setter方法、构造方法或者Spring自动装配来完成。有时候我们希望在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>
<!-- 通用属性 -->
<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>
<!-- 日志 -->
<logback.version>1.0.13</logback.version>
<slf4j.version>1.7.5</slf4j.version>
</properties>
<dependencies>
<!-- Spring和事务 -->
<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>
<!-- 使用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.Olivia.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.Olivia.spring.service;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import com.Olivia.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无参数构造函数被调用");
}
@Override
public void destroy() throws Exception {
System.out.println("EmployeeService正在关闭资源");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("EmployeeService正在初始化为默认值");
if(employee.getName() == null){
employee.setName("Pankaj");
}
}
}
Spring Bean生命周期 – 自定义的后初始化、前销毁
由于我们不希望服务直接依赖于Spring框架,因此让我们创建另一种形式的员工服务类,其中我们将使用Spring的初始化后和销毁前生命周期方法,并在Spring Bean配置文件中进行配置。
package com.Olivia.spring.service;
import com.Olivia.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无参构造函数被调用");
}
//销毁前方法
public void destroy() throws Exception {
System.out.println("MyEmployeeService正在关闭资源");
}
//初始化后方法
public void init() throws Exception {
System.out.println("MyEmployeeService正在初始化为默认值");
if(employee.getName() == null){
employee.setName("Pankaj");
}
}
}
我们稍后会查看Spring Bean配置文件。在此之前,让我们创建另一个服务类,它将使用@PostConstruct和@PreDestroy注解。
Spring Bean生命周期 – @PostConstruct和@PreDestroy
以下是一个简单的类,它将被配置为Spring Bean,并且我们使用了@PostConstruct和@PreDestroy注解来定义该类的初始化后和销毁前的方法。
package com.Olivia.spring.service;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
public class MyService {
@PostConstruct
public void init(){
System.out.println("MyService初始化方法被调用");
}
public MyService(){
System.out.println("MyService无参构造函数被调用");
}
@PreDestroy
public void destory(){
System.out.println("MyService销毁方法被调用");
}
}
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">
<!-- 未初始化员工名称变量-->
<bean name="employee" class="com.Olivia.spring.bean.Employee" />
<bean name="employeeService" class="com.Olivia.spring.service.EmployeeService">
<property name="employee" ref="employee"></property>
</bean>
<bean name="myEmployeeService" class="com.Olivia.spring.service.MyEmployeeService"
init-method="init" destroy-method="destroy">
<property name="employee" ref="employee"></property>
</bean>
<!-- 初始化CommonAnnotationBeanPostProcessor等同于使用context:annotation-config -->
<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />
<bean name="myService" class="com.Olivia.spring.service.MyService" />
</beans>
请注意,我在它的Bean定义中没有初始化员工名称。由于EmployeeService使用了接口,所以我们在这里不需要任何特殊的配置。对于MyEmployeeService Bean,我们使用init-method和destroy-method属性来让Spring框架知道我们要执行的自定义方法。MyService Bean的配置没有任何特殊之处,但是正如你所看到的,我启用了注解式配置。我们的应用程序已经准备就绪,让我们编写一个测试程序来查看不同的方法如何被执行。
Spring Bean生命周期 – 测试程序
package com.Olivia.spring.main;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.Olivia.spring.service.EmployeeService;
import com.Olivia.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
春天的Bean生命周期重要点:
- From the console output it’s clear that Spring Context is first using no-args constructor to initialize the bean object and then calling the post-init method.
- The order of bean initialization is same as it’s defined in the spring bean configuration file.
- The context is returned only when all the spring beans are initialized properly with post-init method executions.
- Employee name is printed as “Pankaj” because it was initialized in the post-init method.
- When context is getting closed, beans are destroyed in the reverse order in which they were initialized i.e in LIFO (Last-In-First-Out) order.
你可以取消代码的注释,以获取类型为MyEmployeeService的bean,并确认输出将类似并遵循以上提到的所有要点。
春季意识接口
这是文章《Spring Bean的生命周期》的第4部分(共5部分)。
有时候我们需要在我们的Spring Bean中使用Spring Framework对象来执行一些操作,比如读取ServletConfig和ServletContext参数,或者了解ApplicationContext加载的bean定义。这就是为什么Spring Framework提供了一系列我们可以在我们的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配置参数。
让我们通过在一个类中实现其中几个来实际了解Aware接口的用法,并将该类配置为Spring bean。
package com.Olivia.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 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">
<bean name="employee" class="com.Olivia.spring.bean.Employee" />
<bean name="myAwareService" class="com.Olivia.spring.service.MyAwareService" />
</beans>
Spring Aware测试计划
这是文章《春季豆子的生命周期》的第5部分(共5部分)。
package com.Olivia.spring.main;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.Olivia.spring.service.MyAwareService;
public class SpringAwareMain {
public static void main(String[] args) {
// 创建Spring上下文
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring-aware.xml");
// 获取Bean实例
ctx.getBean("myAwareService", MyAwareService.class);
// 关闭上下文
ctx.close();
}
}
现在当我们执行上述类时,我们会得到以下输出结果。
Apr 01, 2014 11:27:05 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: 正在刷新 org.springframework.context.support.ClassPathXmlApplicationContext@60a2f435: 启动日期 [Tue Apr 01 23:27:05 PDT 2014]; 上下文层次结构的根
Apr 01, 2014 11:27:05 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: 正在从类路径资源 [spring-aware.xml] 加载XML bean定义
setBeanName called
setBeanName:: 上下文中定义的Bean名称=myAwareService
setBeanClassLoader called
setBeanClassLoader:: 类加载器名称=sun.misc.Launcher$AppClassLoader
setBeanFactory called
setBeanFactory:: employee bean 单例=true
setEnvironment called
setResourceLoader called
setResourceLoader:: 资源文件名称=spring.xml
setApplicationEventPublisher called
setApplicationContext called
setApplicationContext:: Bean定义名称=[employee, myAwareService]
Apr 01, 2014 11:27:05 PM org.springframework.context.support.ClassPathXmlApplicationContext doClose
INFO: 正在关闭 org.springframework.context.support.ClassPathXmlApplicationContext@60a2f435: 启动日期 [Tue Apr 01 23:27:05 PDT 2014]; 上下文层次结构的根
测试程序的控制台输出很容易理解,我不会详细介绍。以上就是Spring Bean生命周期方法和将框架特定对象注入Spring Bean的内容。请从下面的链接下载示例项目并进行分析,以便更深入了解它们。