spring之bean的生命周期

时间:2022-07-15 19:05:02

上篇文章中,详细介绍了spring中bean的scope,scope既是bean在spring容器中的存在方式,如prototype和singleton,且也带了一些存在周期的属性,如 session和request等。spring中 bean从实例化,到依赖注入、再到初始化、到最后消亡,有和完整的生命周期。它和scope一起构成bean完整的生命周期。本篇文章主要简单的描述下bean生命周期中的初始化方法(init())和消亡前(ondestroy(),以后称之为析构)的方法。本文主要讨论scope为prototype和singleton的bean,且由于prototype的bean在返回给客户端后,spring不在由spring进行管理,所以其销毁前的方法不会执行。

spring中bean的生命周期简单的如下图所示:

spring之bean的生命周期


 我们可以在生命周期函数中加入自己的一些定制化处理,比如说在初始化阶段,加载相关的资源,启动线程等,在析构阶段释放资源,关闭线程。需要牢记一点,初始化函数是在所有的依赖已经注入后才开始执行的。

生命周期回调

其中中bean的初始化和消亡前主要由以下几种方式:

1.Spring的接口InitializingBean和DisposableBean。示例如下:

/**  
   *   InitializingBean 接口从名字可以看出此方法在 bean的属性被spring赋值之后执行,但是和spring的接口耦合在了一起
   */
   @Override
   public void afterPropertiesSet() throws Exception {
      System.out.println(this+" i am  afterPropertiesSet");
   }
  
  
   /* *
    * DisposableBean 接口定义的
    */
   @Override
   public void destroy() throws Exception {
      System.out.println(this+" i am  destroy");
   }

2.采用元数据配置,即在bean的定义中配置,如 init-method destroy-method属性,直接定义相关方法,另外在根标签<beans>可以配置default-init-method 和default-destroy-method 配置默认的 方法,如果<bean>中有定义,则覆盖默认的定义:

<bean id="user0" class="com.test.service.UserServiceIml" init-method="testInit" destroy-method="testBeforeDesstroy">
        <property name="userDao" ref="userDao"></property>
</bean>
 
/**  
   * @Description: 采用元数据配置,在xml中配置
   * @param  
   * @return void
   */
   public void testInit(){
      System.out.println(this+" i am  testInit");
   }
  
   /**  
   * @Description: 采用元数据配置,在xml中配置
   * @param  
   * @return void
   */
   public void testBeforeDesstroy(){
      System.out.println(this+" i am  testBeforeDesstroy");
   }


3.采用JSR注解

/**  
   * @Description: 采用jsr注解
   * @param  
   * @return void
   */
   @PostConstruct
   public void testPostConstruct(){
      System.out.println(this+" i am  testPostConstruct");
   }
  
   /**  
   * @Description: 采用JSR的注解
   * @param  
   * @return void
   */
   @PreDestroy
   public void testPreDesstroy(){
      System.out.println(this+" i am  testPreDesstroy");
   }

混合机制

以上有三种回调函数实现的方式,他们可以单独使用,也可以混合使用,混合使用的时候,他们的顺序如下:

对于初始化函数:

1.     @PostConstruct 注解的方法

2.     InitializingBean接口定义的回调afterPropertiesSet()

3.     Bean配置中自定义的初始化函数

 

对于析构则与上相同:

1.     @PreDestroy注解的方法

2.     DisposableBean接口定义的回调destroy()

3.     Bean配置中自定义析构函数

详细的测试见后面。

 

startUp和shutDown回调

有时候我们可能需要一些组件的功能随着spring容器的启动而启动(如新开启一个线程来进行监听),容器的销毁而销毁。为此spring提供了一个接口,从方法名字很容易看到方法的作用:

public interface Lifecycle {
 
    void start();
 
    void stop();
 
    boolean isRunning();
 
}

当启动的时候,由于组件之间的依赖,启动的顺序是相当重要的。depends-on属性可以决定多个lifecycle的实现的顺序,但是有时候依赖是未知的。为此spring定义可一个新的接口,SmartLifecycle:

public interface Phased {
 
    int getPhase();
 
}
 
public interface SmartLifecycle extends Lifecycle, Phased {
 
    boolean isAutoStartup();
 
    void stop(Runnable callback);
 
}


isAutoStartup决定是随着容器的启动自启动,Phased接口定义的方法则决定了启动顺序,其返回值越小启动越早,越大启动越晚。如果一个Lifecycle未实现Phased接口,则默认其值为0.

Stop方法可以异步的执行析构函数,spring默认会为每一个Phase等待30秒钟。这个时间是可以配置的。

测试

在非web环境下,要使析构函数执行,需要执行AbstractApplicationContext的registerShutdownHook()方法,见本文的示例:

本测试准备说明以下:

1.     混合生命周期回调的执行顺序

2.     LifeCycle也可以看做为周期,其中start方法在初始化函数执行后被调用,stop方法在析构函数执行前被调用。Prototype 不会被调用start和 stop方法

3.     Depend-on决定bean加载的顺序

4.     <bean>定义的回调会覆盖<beans>定义的默认的回调。

代码

以下是测试程序,本程序依赖Spring之容器和配置初识这篇文章中的代码,并对其稍加改动:

代码结构如下:

首先是allbean.xml配置文件:

     <!--使JSR注解生效  -->
    <bean id="postsss" class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/>
   <!-- 也可以使用这个配置  使JSR生效  <context:annotation-config/> -->
  
  
   <!-- 改变在容器关闭阶段,smartlifecycle 每个阶段    等待的时间 -->
      <bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
    <!-- timeout value in milliseconds -->
    <property name="timeoutPerShutdownPhase" value="3000"/>
   </bean>
    <import resource="com/test/dao/dao.xml"/>
   
    <import resource="com/test/service/service.xml"/>
   
  
  
 

然后是service.xml

<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="close"
        >
 
    <!-- 配置自己的初始化和析构,覆盖默认配置 -->
    <bean id="user0" class="com.test.service.UserServiceIml" init-method="testInit" destroy-method="testBeforeDesstroy">
        <property name="userDao" ref="userDao"></property>
    </bean>
     <!-- 采用默认配置的初始化和析构 -->
     <bean id="user1" class="com.test.service.UserServiceIml">
        <property name="userDao" ref="userDao"></property>
    </bean>
   
     <!-- 配置scope为 prototype,测试只执行初始化函数,不执行析构 -->
     <bean id="user2" class="com.test.service.UserServiceIml" scope="prototype">
        <property name="userDao" ref="userDao"></property>
    </bean>
 
     <!-- 以下测试 depend-on -->
    <bean id="t1" class="com.test.service.Test1" depends-on="t2">
    </bean>
   
       <bean id="t2" class="com.test.service.Test2" >
    </bean>
    
</beans>


然后是UserServiceIml实现了相关的接口,使用了jsr注解和实现了bean中配置的初始化和析构函数:

package com.test.service;
 
import java.util.List;
 
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
 
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.SmartLifecycle;
 
import com.test.bo.User;
import com.test.dao.UserDao;
 
/**  
 *实现类,有个字段引用了UserDao接口,其具体的业务逻辑调用  {@link UserDao#getUser()}实现
 *
 *且有一个set方法,此set方法由spring 容器注入使用,如果没有回报错,
 *这是spring容器依赖注入的一种方法--setter注入
 */
public class UserServiceIml implements UserService,InitializingBean,DisposableBean,SmartLifecycle{
  
   private volatile boolean isRunning = false;
   private UserDao userDao;
   @Override
   public List<User> getUser() {
      return userDao.getUser();
   }
   public void setUserDao(UserDao userDao) {
      this.userDao = userDao;
      System.out.println(this+" i am  setMethod");
   }
  
  
 
   /**  
   *   InitializingBean 接口从名字可以看出此方法在 bean的属性被spring赋值之后执行,但是和spring的接口耦合在了一起
   */
   @Override
   public void afterPropertiesSet() throws Exception {
      System.out.println(this+" i am  afterPropertiesSet");
   }
  
  
   /* *
    * DisposableBean 接口定义的
    */
   @Override
   public void destroy() throws Exception {
      System.out.println(this+" i am  destroy");
   }
  
  
  
  
   /**  
   * @Description: 采用jsr注解
   * @param  
   * @return void
   */
   @PostConstruct
   public void testPostConstruct(){
      System.out.println(this+" i am  testPostConstruct");
   }
  
   /**  
   * @Description: 采用JSR的注解
   * @param  
   * @return void
   */
   @PreDestroy
   public void testPreDesstroy(){
      System.out.println(this+" i am  testPreDesstroy");
   }
  
  
   /**  
   * @Description: 采用元数据配置,在xml中配置
   * @param  
   * @return void
   */
   public void testInit(){
      System.out.println(this+" i am  testInit");
   }
  
   /**  
   * @Description: 采用元数据配置,在xml中配置
   * @param  
   * @return void
   */
   public void testBeforeDesstroy(){
      System.out.println(this+" i am  testBeforeDesstroy");
   }
  
  
   public void init(){
      System.out.println(this+" i am  init");
   }
  
   public void close(){
      System.out.println(this+" i am  close");
   }
  
  
 
   @Override
   public boolean isRunning() {
      returnisRunning;
   }
  
  
   @Override
   public void start() {
      System.out.println(this+"spring容器启动啦");
      isRunning = true;
   }
  
   /**  
   *   
   */
   @Override
   public void stop() {
      System.out.println(this+"spring容器关闭啦");
      isRunning = false;
   }
     
   @Override
   public int getPhase() {
      return 0;
   }
  
  
   @Override
   public boolean isAutoStartup() {
      return true;
   }
  
  
   @Override
   public void stop(Runnable arg0) {
      arg0.run();
      isRunning = false;
      System.out.println(this+"  this is stop");
   }
}

主程序:

public class TestMain {
   public static void main(String[] args) {
     
      AbstractApplicationContext  context = new ClassPathXmlApplicationContext("allbean.xml");
      context.registerShutdownHook();
 
UserService userService0 = context.getBean("user0", UserService.class);
      System.out.println(userService0.getUser());
      System.out.println("--------------user1采用默认的初始化函数--------------");
//
      UserService userService1 = context.getBean("user1", UserService.class);
//
      System.out.println(userService1.getUser());
//   
      UserService userService2 = context.getBean("user2", UserService.class);
//
      System.out.println(userService2.getUser());
  
 
 
   }
 
}

测试结果

spring之bean的生命周期

以上测试结果就不分析了。需要注意的的上面的测试结果第一行由于截图的关系空缺了。

结束语

Bean的生命周期spring是采用beanpostProcessor(称之为spring扩展点)来进行管理和扩展的,spring定义了很多内置的beanpostProcessor,它们的一些回调在bean的实例化(这里指返回客户端调用前,而不是new)过程中被调用,且如果bean实现了一些aware接口(如ApplicationContextAware 和 beanNameAwre

),这些接口的回调也会进入到生命周期中,这里暂且不讨论。见文章:http://developer.51cto.com/art/201104/255961.htm。而且,spring广泛采用代理机制,需要注意的是 生命周期函数运行在代理创建之前。本文的测试代码下载:本文代码完整下载