上篇文章中,详细介绍了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的生命周期简单的如下图所示:
我们可以在生命周期函数中加入自己的一些定制化处理,比如说在初始化阶段,加载相关的资源,启动线程等,在析构阶段释放资源,关闭线程。需要牢记一点,初始化函数是在所有的依赖已经注入后才开始执行的。
生命周期回调
其中中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()); } }
测试结果
以上测试结果就不分析了。需要注意的的上面的测试结果第一行由于截图的关系空缺了。
结束语
Bean的生命周期spring是采用beanpostProcessor(称之为spring扩展点)来进行管理和扩展的,spring定义了很多内置的beanpostProcessor,它们的一些回调在bean的实例化(这里指返回客户端调用前,而不是new)过程中被调用,且如果bean实现了一些aware接口(如ApplicationContextAware 和 beanNameAwre
),这些接口的回调也会进入到生命周期中,这里暂且不讨论。见文章:http://developer.51cto.com/art/201104/255961.htm。而且,spring广泛采用代理机制,需要注意的是 生命周期函数运行在代理创建之前。本文的测试代码下载:本文代码完整下载