Bean的生命周期
在传统的Java应用中,bean的生命周期很简单。使用Java关键字new进行bean实例化,然后该bean就可以使用了。一旦该bean不再被使用,则由Java自动进行垃圾回收。相比之下,Spring容器中的bean的生命周期就显得相对复杂多了。正确理解Spring bean的生命周期非常重要,因为你或许要利用Spring提供的扩展点来自定义bean的创建过程。
1. 什么是Bean的生命周期
Spring可以理解为一个管理Bean对象的工厂。它负责对象的创建,对象的销毁等。所谓的生命周期就是:对象从创建开始到最终销毁的整个过程。主要就是得知道创建Bean对象的时机,创建Bean对象的前后会调用什么方法,Bean对象销毁的时机以及Bean对象的销毁前后调用了什么方法。
生命周期的本质是Spring在哪个时间节点上调用了哪个类的哪个方法。只有充分的了解了特殊的时间节点在哪,才可以确定代码写到哪,我们可能需要在某个特殊的时间点上执行一段特定的代码,这段代码就可以放到这个节点上。当生命线走到这里的时候,自然会被调用。
2. Bean的生命周期之5步
Bean生命周期的管理,可以参考Spring的源码:AbstractAutowireCapableBeanFactory类的doCreateBean()方法 。
Bean生命周期可以粗略的划分为五大步:
- 第一步:实例化Bean,调用bean的构造方法
- 第二步:Bean属性赋值,调用bean的set方法(此时只能是使用set注入)
- 第三步:初始化Bean,需要在类里自己定义方法,然后再配置bean的时候进行指定。
- 第四步:使用Bean,这一步就是客户端程序从Spring容器里获取bean之后进行的使用的过程。
- 第五步:销毁Bean,同初始化一样,需要在类里自己定义方法,然后再配置bean的时候进行指定。
编写测试程序如下:
public class SpringBean { //定义一个类
private int a;
public SpringBean() {
System.out.println("第一步,执行无参构造");
}
public void setA(int a) {
System.out.println("第二步,给对象赋值");
this.a = a;
}
public void initBean(){
System.out.println("第三步,进行bean的初始化");
}
public void destroyBean(){
System.out.println("第五步,销毁bean");
}
}
<!--对bean进行配置,需要使用bean标签的init-method属性和destory-method方法,指定bean的初始化和销毁应该调用的方法-->
<bean id="springBean" class="com.qiuye.beanInstance.entity.SpringBean" init-method="initBean" destroy-method="destroyBean">
<property name="a" value="1"/>
</bean>
@Test
public void test() {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-instance.xml");
SpringBean springBean = applicationContext.getBean("springBean",SpringBean.class);
System.out.println("第四步,使用bean"+springBean);
applicationContext.close(); //需要关闭容器,才会执行bean的销毁方法
}
执行结果:
需要注意的:
- 只有正常关闭spring容器,bean的销毁方法才会被调用,而且只在ClassPathXmlApplicationContext类才有close()方法。
- 配置文件中的init-method指定初始化方法。destroy-method指定销毁方法。
3. Bean生命周期之7步
在将bean划分为五步的基础上,其中第3步是初始化Bean,如果继续细化声明周期可以在初始化前和初始化后添加代码,可以让某个类实现BeanPostProcessor类——Bean后处理器,并且重写before和after方法:
public class LogBeanPostProcess implements BeanPostProcessor {
@Override
/**
* bean 该参数表示当前bean本身,此时bean已经被构造出来并注入了属性
* beanName 在bean标签配置的bean的id
* 简称before方法
*/
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("在bean初始化之前"+ bean + ":" + beanName);
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
/**
* bean 该参数表示当前bean本身,此时bean已经被构造出来并注入了属性
* beanName 在bean标签配置的bean的id
* 简称after方法
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("在bean初始化之后");
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}
在spring.xml文件中配置“Bean后处理器”:
<!--配置Bean后处理器。这个后处理器将作用于当前配置文件中所有的bean。-->
<bean class="com.qiuye.beanInstance.entity.LogBeanPostProcess"/>
一定要注意:在spring.xml文件中配置的Bean后处理器将作用于当前配置文件中所有的Bean。,这个可以去测试一下,也可以看看在其他配置文件里面配置bean后处理器会不会影响当前配置文件里面的类。
执行测试程序:
如果加上Bean后处理器的话,Bean的生命周期就是7步了,在这个处理器里面可以对bean初始化前后做一些统一的处理。
4. Bean生命周期之10步
如果根据源码跟踪,可以划分更细粒度的步骤,可以分为十步,与七步相比多的三步是在检查是否实现了相关接口及其对应的方法,主要是三种接口:
- Aware相关的接口包括:BeanNameAware、BeanClassLoaderAware、BeanFactoryAware。在Bean后处理器的before方法执行之前检查。
当Bean实现了BeanNameAware,Spring会将Bean的名字传递给Bean。
当Bean实现了BeanClassLoaderAware,Spring会将加载该Bean的类加载器传递给Bean。
当Bean实现了BeanFactoryAware,Spring会将Bean工厂对象传递给Bean。
- InitializingBean,在Bean后处理器的before方法执行之后检查并执行afterPropertiesSet方法。
- DisposableBean,在销毁bean之前检查并执行destroy方法。
测试以上10步,可以让User类实现5个接口,并实现所有方,在七步的基础上进行修改,主要是SpringBean类,代码如下:
public class SpringBean implements BeanNameAware, BeanFactoryAware, BeanClassLoaderAware, InitializingBean, DisposableBean {
private int a;
public SpringBean() {
System.out.println("1,执行无参构造");
}
public void setA(int a) {
System.out.println("2,给对象赋值");
this.a = a;
}
public void initBean(){
System.out.println("6,进行bean的初始化");
}
public void destroyBean(){
System.out.println("10,销毁bean");
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
System.out.println("3,(BeanClassLoaderAware)类加载器:" + classLoader);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("3,(BeanFactoryAware)Bean工厂:" + beanFactory);
}
@Override
public void setBeanName(String name) {
System.out.println("3,(BeanNameAware)bean的名字:"+ name);
}
@Override
public void destroy() throws Exception {
System.out.println("9,(DisposableBean)的destroy执行");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("5,(InitializingBean)的afterPropertiesSet执行");
}
}
执行结果:
通过测试可以看出来:
- InitializingBean的方法早于init-method的执行。
- DisposableBean的方法早于destroy-method的执行。
总结下来就是:
- 1.Spring对bean进行实例化;
- 2.Spring将值和bean的引用注入到bean对应的属性中;
- 3.如果bean实现了BeanNameAware接口,Spring将bean的ID传递给 setBeanName()方法;
- 3.如果bean实现了BeanClassLoaderAware接口,Spring将调用setBeanClassLoader()方法,将bean所在的类加载器引用传入进来;
- 3.如果bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入;
- 4.如果bean实现了BeanPostProcessor接口,Spring将调用它的postProcessBeforeInitialization()方法;
- 5.如果bean实现了InitializingBean接口,Spring将调用它们的 afterPropertiesSet()方法。
- 6.如果bean使用initmethod声明了初始化方法,该方法也会被调用;
- 7.如果bean实现了BeanPostProcessor接口,Spring将调用它们 的postProcessAfterInitialization()方法;
- 8.此时,bean已经准备就绪,可以被应用程序使用了,它们将一直驻留在应用上下文中,直到该应用上下文被销毁;
- 9.如果bean实现了DisposableBean接口,Spring将调用它的destroy()接口方法。同样
- 10.如果bean使用destroy-method声明 了销毁方法,该方法也会被调用。
5. Bean的作用域不同,管理方式不同
Spring 根据Bean的作用域来选择管理方式。
- 对于singleton作用域的Bean,Spring 能够精确地知道该Bean何时被创建,何时初始化完成,以及何时被销毁;
- 而对于 prototype 作用域的 Bean,Spring 只负责创建,当容器创建了 Bean 的实例后,Bean 的实例就交给客户端代码管理,Spring 容器将不再跟踪其生命周期。
之前的SpringBean类是在默认作用域也就是singleton的情况下进行测试的,现在需要把spring.xml文件中的配置scope设置为prototype,其他的保持不变:
<bean id="springBean" class="com.qiuye.beanInstance.entity.SpringBean" scope="prototype" init-method="initBean" destroy-method="destroyBean">
<property name="a" value="1"/>
</bean>
执行测试程序:
通过测试一目了然。只执行了前8步,第9和10都没有执行。
6. 自己new的对象如何让Spring管理
有些时候可能会遇到这样的需求,某个java对象是我们自己new的,然后我们希望这个对象被Spring容器管理,应该怎么处理?直接这样测试,将SpringBean从配置文件里取消配置,然后编写测试程序如下:
public void test() {
SpringBean springBean = new SpringBean();
System.out.println(springBean);
//DefaultListableBeanFactory类可以向Spring注册自己new的实例
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
factory.registerSingleton("springBean", springBean);
SpringBean bean = factory.getBean("springBean", SpringBean.class);
System.out.println(bean);
}
执行结果:
从执行结果来看,实例化的对象确实被放在了Spring容器里,但是之后并没有对其的生命周期进行管理,这个还得看看源码才知道其中的缘由,简单地看了一下是将其放在了
DefaultSingletonBeanRegistry的一级缓存(singletonObjects)里,而不是放在DefaultListableBeanFactory的beanDefinitionMap里。