详解Spring中bean生命周期回调方法

所属分类: 软件编程 / java 阅读数: 28
收藏 0 赞 0 分享

生命周期回调方法

对于spring bean来讲,我们默认可以指定两个生命周期回调方法。一个是在ApplicationContext将bean初始化,包括注入对应的依赖后的回调方法;另一个是在ApplicationContext准备销毁之前的回调方法。要实现这种回调主要有三种方式:实现特定的接口、在XML配置文件中指定回调方法和使用JSR-250标准的注解。

1 实现特定接口

针对bean初始化后的回调和ApplicationContext销毁前的回调,Spring分别为我们了提供了InitializingBean和DisposableBean接口供用户实现,这样Spring在需要进行回调时就会调用对应接口提供的回调方法。

1.1 InitializingBean

InitializingBean是用来定义ApplicationContext在完全初始化一个bean以后需要需要回调的方法的,其中只定义了一个afterPropertiesSet()方法。如其名称所描述的那样,该方法将在ApplicationContext将一个bean完全初始化,包括将对应的依赖项都注入以后才会被调用。InitializingBean的完全定义如下。

public interface InitializingBean {

 void afterPropertiesSet() throws Exception;

}

由于InitializingBean的afterPropertiesSet()方法会在依赖项都进行注入以后再回调,所以该方法通常会用来检查必要的依赖注入,以使我们能够在bean被初始化时就发现其中的错误,而不是在很长时间使用以后才发现。如果你去看Spring的源码,你就会发现源码中有很多InitializingBean的使用,而且基本都是用来检查必要的依赖项是否为空的。

public class Hello implements InitializingBean {
 
 private World world;
 
 /**
 * 该方法将在当前bean被完全初始化后被调用
 */
 public void afterPropertiesSet() throws Exception {
 Assert.notNull(world, "world should not be null.");
 }

 public void setWorld(World world) {
 this.world = world;
 }
 
}

9.1.2 DisposableBean

DisposableBean是用来定义在ApplicationContext销毁之前需要回调的方法的。DisposableBean接口中只定义了一个destroy()方法,在ApplicationContext被销毁前,Spring将依次调用bean容器中实现了DisposableBean接口的destroy()方法。所以,我们可以通过实现该接口的destroy()方法来达到在ApplicationContext销毁前释放某些特定资源的目的。

public interface DisposableBean {

 void destroy() throws Exception;

}

在Spring的源码中,也有很多实现了DisposableBean接口的类,如我们熟悉的ApplicationContext实现类、SingleConnectionDataSource等。

2 在XML中配置回调方法

在XML配置文件中通过bean元素定义一个bean时,我们可以通过bean元素的init-method属性和destroy-method属性来指定当前bean在初始化以后和ApplicationContext销毁前的回调方法。需要注意的是所指定的回调方法必须是没有参数的。

通过init-method属性来指定初始化方法时所对应的方法必须是该bean中所拥有的方法,所以首先我们需要在对应的bean中定义对应的初始化方法,这里假设我们需要在bean中定义一个init()方法作为该bean的初始化方法,那么我们可以对我们的bean进行类似如下定义。

public class Hello {
 
 private World world;
 
 /**
 * 该方法将被用来作为初始化方法,在当前bean被完全初始化后被调用
 */
 public void init() {
 Assert.notNull(world, "world should not be null.");
 }

 public void setWorld(World world) {
 this.world = world;
 }
 
}

接下来就是在XML配置文件中定义该bean时通过init-method属性定义对应的初始化方法为init()方法,init-method属性的属性值就对应初始化方法的名称,所以我们的bean应该是如下定义。

 <bean name="world" class="com.app.World"/>
 <!-- 通过init-method属性指定初始化方法名称 -->
 <bean id="hello" class="com.app.Hello" init-method="init">
 <property name="world" ref="world"/>
 </bean>

init-method和destroy-method的用法和配置等基本上都是一样的,所以对于使用destroy-method来指定ApplicationContext销毁前的回调方法的用法就不再赘述了。

如果我们的初始化方法或销毁方法的名称大都是一样的,在通过init-method和destroy-method进行指定的时候我们就没有必要一个个bean都去指定了,Spring允许我们在最顶级的beans元素上指定默认的初始化后回调方法和销毁前的回调方法名称,这样对于没有指定init-method或destroy-method的bean将默认将其中default-init-method或default-destroy-method属性值对应名称的方法(如果存在的话)视为初始化后的回调方法或销毁前的回调方法。这是通过default-init-method和default-destroy-method属性来定义的。

<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"
  default-init-method="init" default-destroy-method="destroy">
 
</beans>

以上表示定义默认的初始化后回调方法名称为init,默认的销毁前回调方法名称为destroy。

当定义了default-init-method或default-destroy-method以后,如果我们的某个bean对应的初始化后回调方法名称或销毁前的回调方法名称与默认定义的不一样,则我们可以在对应的bean上通过init-method或destroy-method指定该bean自身的回调方法名称,即bean上定义的回调方法名称将会比默认定义拥有更高的优先级。

3 使用JSR-250标准的注解

关于bean的生命周期回调方法,Spring也会JSR-250标准注解做了支持,即在bean完全初始化后将回调使用@PostConstruct标注的方法,在销毁ApplicationContext前将回调使用@PreDestroy标注的方法。
针对之前的示例,如果我们现在把定义的bean定义成如下这样,即没有在bean上通过init-method和destroy-method指定初始化方法和销毁方法。

 <bean name="world" class="com.app.World"/>
 <bean id="hello" class="com.app.Hello">
 <property name="world" ref="world"/>
 </bean>

当然,这里也不考虑全局性的init-method和destroy-method方法,如果我们希望在id为“hello”的bean被初始化后回调其中的init()方法,在销毁前回调其中的destroy()方法,我们就可以通过@PostConstruct和@PreDestroy进行如下定义。

public class Hello {
 
 private World world;
 
 /**
 * 该方法将被用来作为初始化方法,在当前bean被完全初始化后被调用
 */
 @PostConstruct
 public void init() {
 Assert.notNull(world, "world should not be null.");
 }
 
 @PreDestroy
 public void destroy() {
 System.out.println("---------destroy-----------");
 }

 public void setWorld(World world) {
 this.world = world;
 }
 
}

使用JSR-250标准指定初始化后的回调方法以及销毁前的回调方法时,如果我们希望将多个方法都作为对应的回调方法进行回调,则可以在多个方法上同时使用对应的注解进行标注,Spring将依次执行对应的方法。

public class Hello {
 
 private World world;

 @PostConstruct
 public void init() {
 System.out.println("-----------init-------------");
 }
 
 /**
 * 该方法将被用来作为初始化方法,在当前bean被完全初始化后被调用
 */
 @PostConstruct
 public void init2() {
 Assert.notNull(world, "world should not be null.");
 }
 
 @PreDestroy
 public void destroy() {
 System.out.println("------------destroy----------------");
 }
 
 @PreDestroy
 public void destroy2() {
 System.out.println("---------destroy2-----------");
 }

 public void setWorld(World world) {
 this.world = world;
 }
 
}

4 混合使用三种方式

Spring允许我们混合使用上述介绍的三种方式来指定对应的回调方法。当对于同一个bean使用三种方式指定了同一个方法作为初始化后的回调方法或销毁前的回调方法,则对应的回调方法只会被执行一次。然而,当对于同一个bean使用两种或三种方式指定的回调方法不是同一个方法时,Spring将依次执行使用不同的方式指定的回调方法。对于初始化后的回调方法而言,具体规则如下:

  1. 使用@PostConstruct标注的方法。
  2. 实现InitializingBean接口后的回调方法afterPropertiesSet()方法。
  3. 通过init-method或default-init-method指定的方法。

对于销毁前的回调方法而言,其规则是一样的:

  1. 使用@PreDestroy标注的方法。
  2. 实现DisposableBean接口后的回调方法destroy()方法。
  3. 通过destroy-method或default-destroy-method指定的方法。

(注:本文是基于Spring4.1.0所写)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

更多精彩内容其他人还在看

Java的面向对象编程基本概念学习笔记整理

这篇文章主要介绍了Java的面向对象编程基本概念学习笔记整理,包括类与方法以及多态等支持面向对象语言中的重要特点,需要的朋友可以参考下
收藏 0 赞 0 分享

Eclipse下编写java程序突然不会自动生成R.java文件和包的解决办法

这篇文章主要介绍了Eclipse下编写java程序突然不会自动生成R.java文件和包的解决办法 的相关资料,需要的朋友可以参考下
收藏 0 赞 0 分享

基于Java实现杨辉三角 LeetCode Pascal's Triangle

这篇文章主要介绍了基于Java实现杨辉三角 LeetCode Pascal's Triangle的相关资料,需要的朋友可以参考下
收藏 0 赞 0 分享

Java中Spring获取bean方法小结

Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架,如何在程序中获取Spring配置的bean呢?下面通过本文给大家介绍Java中Spring获取bean方法小结,对spring获取bean方法相关知识感兴趣的朋友一起学习吧
收藏 0 赞 0 分享

如何计算Java对象占用了多少空间?

在Java中没有sizeof运算符,所以没办法知道一个对象到底占用了多大的空间,但是在分配对象的时候会有一些基本的规则,我们根据这些规则大致能判断出来对象大小,需要的朋友可以参考下
收藏 0 赞 0 分享

剖析Java中的事件处理与异常处理机制

这篇文章主要介绍了Java中的事件处理与异常处理机制,讲解Java是如何对事件或者异常作出响应以及定义异常的一些方法,需要的朋友可以参考下
收藏 0 赞 0 分享

详解Java的Struts2框架的结构及其数据转移方式

这篇文章主要介绍了详解Java的Struts2框架的结构及其数据转移方式,Struts框架是Java的SSH三大web开发框架之一,需要的朋友可以参考下
收藏 0 赞 0 分享

Java封装好的mail包发送电子邮件的类

本文给大家分享了2个java封装好的mail包发送电子邮件的类,并附上使用方法,小伙伴们可以根据自己的需求自由选择。
收藏 0 赞 0 分享

在Java的Struts中判断是否调用AJAX及用拦截器对其优化

这篇文章主要介绍了在Java的Struts中判断是否调用AJAX及用拦截器对其优化的方法,Struts框架是Java的SSH三大web开发框架之一,需要的朋友可以参考下
收藏 0 赞 0 分享

java多线程Future和Callable类示例分享

JAVA多线程实现方式主要有三种:继承Thread类、实现Runnable接口、使用ExecutorService、Callable、Future实现有返回结果的多线程。其中前两种方式线程执行完后都没有返回值,只有最后一种是带返回值的。今天我们就来研究下Future和Callab
收藏 0 赞 0 分享
查看更多