spring 中bean的生命周期 (以springboot 为演示)
说明:本文主要的参考为《精通spring 4.x 企业开发应用实战》陈雄华 林开雄 文建国 编著
一 理论先行
先看一张图,也是上面那本书上搬来的
书中把bean的整个生命周期可能会调用的方法分为4类,分别是:
1.Bean自身的方法。比如构造方法,getter setter方法,其他的自定方法
2.Bean级的生命周期方法。要调用这些方法需要由Bean来实现Bean级的生命周期方法接口:比如BeanNameAware,BeanFactoryAware,InitializingBean,DisposableBean,这些接口中的方法是自动调用的,作用范围只是针对实现了前面所说的4个接口的类型的Bean
3.容器级的生命周期接口方法。图中带有☆的方法就是容器级的方法,容器中所有的bean都要被容器级的生命周期方法处理,而且这个级别的接口是单独实现的,独立于Bean之外。上图中的容器及生命周期接口为:InstantiationAwareBeanPostProcessor 和BeanPostProcessor
4.工厂后处理器接口方法:包括AspectJWeavingEnabler,CustomeAutowireConfigurer,ConfigurationClassPostProcessor等方法,在应用上下文装配文件后立即调用
二 开始实战
1.先实现Bean的生命周期接口,四个接口,要实现其中的四个方法,实例化和初始化完成后会自动被调用
package com.example.demo.beanlife; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; public class A implements BeanNameAware,BeanFactoryAware,InitializingBean,ApplicationContextAware{ private String name; public A() { System.out.println("A对象正在实例化 name="+ name); } public String getName() { return name; } public void setName(String name) { System.out.println(">>>开始初始化 调用A setName()方法设置对象属性"); this.name = name; } @Override public void setBeanName(String s) { System.out.println("A实例属性设置完成,调用setBeanName() beanName=" + s); } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { System.out.println("调用 setBeanFactory() 设置beanFactory实例到A对象中"); this.beanFactory = beanFactory; System.out.println("a >>>>>>>>"+ beanFactory.getBean("a").toString()); } @Override
//同一级的生命周期方法中最后一个被调用的,但是只会调用一次,之后在调用bean的setxx()方法更改属性时将不会再被被调用到 public void afterPropertiesSet() throws Exception { System.out.println("调用afterPropertiesSet() A 对象的属性设置已经完成了 name=" + name); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { System.out.println("调用setApplicationContext ApplicationContext的值为" + applicationContext.toString()); } }
配置类(重新写一个类):
@Configuration
public class BeanConfig {
@Bean
public A a(){
A a = new A();
a.setName("007");
return a;
}
-------输出如下-------------------
A对象正在实例化 name=null
>>>开始初始化 调用A setName()方法设置对象属性
A实例属性设置完成,调用setBeanName() beanName=a
调用 setBeanFactory() 设置beanFactory实例到A对象中
b >>>>>>>>com.example.demo.beanlife.B@5cefde48
a >>>>>>>>com.example.demo.beanlife.A@5557431b
调用setApplicationContext ApplicationContext的值为org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@42e20967, started on Sun Aug 23 14:07:04 CST 2020
调用afterPropertiesSet() A 对象的属性设置已经完成了 name=007
2.实现容器级的生命周期接口 Bean:InstantiationAwareBeanPostProcessor 和 BeanPostProcessor接口
package com.example.demo.beanlife; import org.springframework.beans.BeansException; import org.springframework.beans.PropertyValues; import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor; import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter; import org.springframework.stereotype.Component; //如果后处理器没有以组件的方式加装到ioc容器中,处理器将不会发生作用 @Component public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor { //在调用bean的构造方法实例化bean之前,都会先调用这个方法 @Override public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException { System.out.println( beanName + "实例化之前 调用 postProcessBeforeInstantiation()"); return null; } @Override
//bean实例化完成之后,会调用这个方法:注意,根据我的实践来看,实例化bean的时候会把我们在配置类中指定的属性设置好之后才会调用这个方法,此时bean对象中的属性已经哟值了 public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { if (bean instanceof A){ A a = (A) bean; System.out.println("A实例对象此时的属性 name = " + a.getName()); } System.out.println( beanName+ "对象实例化完成 调用postProcessAfterInstantiation 对象类型为:" + bean.getClass().getName() ); return false; } }
-----输出如下-----------------------
a实例化之前 调用 postProcessBeforeInstantiation()
A对象正在实例化 name=null
>>>开始初始化 调用A setName()方法设置对象属性
>>>开始初始化 调用A setB()方法设置对象属性
A实例对象此时的属性 name = 007
a对象实例化完成 调用postProcessAfterInstantiation 对象类型为:com.example.demo.beanlife.A
A实例属性设置完成,调用setBeanName() beanName=a
调用 setBeanFactory() 设置beanFactory实例到A对象中
b >>>>>>>>com.example.demo.beanlife.B@636cc54d
a >>>>>>>>com.example.demo.beanlife.A@58b2a8bc
调用setApplicationContext ApplicationContext的值为org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@33741750, started on Sun Aug 23 14:31:00 CST 2020
调用afterPropertiesSet() A 对象的属性设置已经完成了 name=007
package com.example.demo.beanlife; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.stereotype.Component; @Component public class MyBeanProcesssor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("调用postProcessBeforeInitialization() 对"+beanName+"进行加工"); if (bean instanceof A){ A a = (A)bean; a.setName("008"); } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("调用postProcessAfterInitialization() 再次获得"+beanName+"加工机会"); return bean; } }
---------------输出结果--------------------
a实例化之前 调用 postProcessBeforeInstantiation()
A对象正在实例化 name=null
>>>开始初始化 调用A setName()方法设置对象属性
>>>开始初始化 调用A setB()方法设置对象属性
A实例对象此时的属性 name = 007
a对象实例化完成 调用postProcessAfterInstantiation 对象类型为:com.example.demo.beanlife.A
A实例属性设置完成,调用setBeanName() beanName=a
调用 setBeanFactory() 设置beanFactory实例到A对象中
b >>>>>>>>com.example.demo.beanlife.B@55e2cac3
a >>>>>>>>com.example.demo.beanlife.A@4aeb6fa7
调用setApplicationContext ApplicationContext的值为org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@7317f8f0, started on Sun Aug 23 14:52:05 CST 2020
调用postProcessBeforeInitialization() 对a进行加工
>>>开始初始化 调用A setName()方法设置对象属性
调用afterPropertiesSet() A 对象的属性设置已经完成了 name=008
调用postProcessAfterInitialization() 再次获得a加工机会
3.工厂后置处理器,这个处理器只会执行一次,而且针对的是容器,开始实例化bean放入Spring-Ioc容器之前,这个方法会被调用
@Component public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { System.out.println(" 工厂后置处理器 postProcessBeanFactory()"); } }
--------------输出结果------------------
工厂后置处理器 postProcessBeanFactory()
tomcatServletWebServerFactory实例化之前 调用 postProcessBeforeInstantiation().........实例化其他的bean
总结:虽然在spring中通过xml文件的形式配置装配bean时,实例化和初始化是泾渭分明的,调用InstantiationAwarePostProcess的 postProcessAfterInstantiation方法是在bean的构造方法被调用之后立即被调用,但是spring-boot中通过配置类装配bean时,实例化和初始化好像是连接在一起了,构造方法被调用之后紧接着调用setXX()方法,而后在调用postProcessAfterInstantiation()方法。在springBoot中BeanPostPrecessor的postProcessBeforeInitialization()中可以插足bean的初始过程,再一次修改bean的属性值, 也可以进行其他操作。
spring中的bean是被Ioc容器((BeanFactory)管理的,所以从bean开始实例化的时候,就要接受容器级的针对bean进行处理的后处理器的处理,这个过程是被动的,没得选,接下来要调用bean自己的方法进行实例化,同时还要受到Bean的生命周期级接口的管理(如果实现了bean级生命周期接口),这是可选的。