上面我们只是关心如何正确的将bean装配到IoC容器中,而没有关心IoC容器如何装配和销毁Bean的过程。有时候我们也需要自定义初始化或者销毁Bean的过程,以满足一些Bean特殊初始化和销毁的要求。例如,上面的数据源,我们希望在其关闭的时候调用其close方法,以释放数据库的链接资源,这是在项目过程中很常见的要求。为了解决这些问题,我们有必要了解Spring IoC初始化和销毁Bean的过程,这就是Bean的生命周期的过程,他大致分为Bean定义,Bean的初始化, Bean的生存期,和Bean的销毁4个部分。其中Bean定义过程大致如下。
- Spring通过我们的配置,如@ComponentScan定义的扫描路径去找带有@Component的类,这个过程就是一个资源定位的过程。
- 一旦找到资源,那么他就开始解析,并且将定义的信息保存起来。注意此时此时还没有开始初始化Bean也就没有Bean实例,他有的仅仅是Bean的定义。
- 然后就会将Bean定义发布到Spring IoC容器中。此时,IoC容器也只有Bean的定义,还是没有Bean的实例生成。
完成这三步,只是将一个资源定位并将Bean的定义发布到IoC容器的过程。还没有Bean的实例生成,更没有完成依赖注入。在默认情况下,Spring会继续去完成Bean的实例化和依赖注入,这样在IoC容器中就可以得到一个依赖注入完成的Bean,但是,有些Bean会收到变化因素的影响,这时候,我们倒是希望在取出bean的时候完成初始化和依赖注入。换句话说,就是让那些Bean只是将定义发布到IoC容器而不是做实例化和依赖注入,当我们取出的时候才进行初始化和依赖注入等操作。
下面我们先来了解一下Spring Bean的初始化流程
ComponentScan还有一个配置项lazyinit,只可以配置Boolean的值,且默认值为false,也就是默认不进行延迟初始化,因此在默认的情况下Spring会对bean进行实例化和依赖注入对应的属性值。
为了进行测试,先改造BussinessPerson
/**
* 人的实现类
*/
@Component
public class BussinessPersion implements Person {
private Animal animal = null;
@Override
public void servie() {
this.animal.use();
}
@Override
@Autowired @Qualifier("cat")
public void setAnimanl(Animal animanl) {
System.out.println("延迟依赖注入");
this.animal=animanl;
}
}
然后我们在没有配置lazyInit的情况下进行断点测试
20:09:00.998 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bussinessPersion'
20:09:01.021 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'cat'
延迟依赖注入
20:09:01.022 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory
在断点处我们并没有获得bean实例,而日志就已经打出了,可见他是在Spring IoC容器初始化时就执行了实例化和依赖注入。为了改变这个情况,我们在配置类Appconfig的@ComponentScan中加入lazyInit配置,如下代码:
@ComponentScan(value = "cn.hctech2006.boot.bootall.*",excludeFilters = {@ComponentScan.Filter(classes = {Service.class})},
lazyInit = true)
然后进行测试,就会发现延迟依赖注入不会出现在日志中,只有运行到断点才会出现日志。
如果仅仅是实例化和依赖注入还是比较简单的,还不能完成进行自定义的要求。为了完成依赖注入的功能,Spring在完成依赖注入之后,还提供了一系列的接口和配置来完成Bean初始化的过程,让我们学习这个过程。Spring在完成依赖注入后,还会进行如下流程完成他的生命周期。
图中描述的是整个IoC容器初始化Bean的流程,作为开发者,需要注意这些流程。除此之外,还需要注意一下两点。
- 这些方法和接口是针对什么而言,如图,在没有注释的情况下的流程节点都是针对单个Bean而言的,但是BeanPostProcessor是针对所有Bean而言的,这是我们需要注意的地方。
- 只有实现了ApplicationContext接口的容器,才会在生命周期调用ApplicationContextAware所定义的setApplicationContext方法。
为了测试生命周期,先来改造BussinessPerson
/**
* 人的实现类
* 加入生命接口和自定义
*/
@Component
public class BussinessPersion implements Person , BeanNameAware,
BeanFactoryAware, ApplicationContextAware, InitializingBean, DisposableBean
{
private Animal animal = null;
@Override
public void servie() {
this.animal.use();
}
@Override
@Autowired @Qualifier("cat")
public void setAnimanl(Animal animanl) {
System.out.println("延迟依赖注入");
this.animal=animanl;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("["+this.getClass().getSimpleName()+"]调用BeanFactoryAware的setBeanFactory");
}
@Override
public void setBeanName(String s) {
System.out.println("["+this.getClass().getSimpleName()+"]调用BeanNameAware的setBeanName");
}
@Override
public void destroy() throws Exception {
System.out.println("["+this.getClass().getSimpleName()+"]调用DisposableBean的destory方法");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("["+this.getClass().getSimpleName()+"]调用InitializingBean的afterPropertiesSet");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("["+this.getClass().getSimpleName()+"]调用ApplicationContextAware的setApplicationCpntext");
}
@PostConstruct
public void init(){
System.out.println("["+this.getClass().getSimpleName()+"]调用PostContructor定义的自定义初始化方法");
}
@PreDestroy
public void destory1(){
System.out.println("["+this.getClass().getSimpleName()+"]调用PrePost定义的自定义销毁方法");
}
}
这样这个Bean就实现了生命周期中单个Bean可以实现的所有接口,并且通过注解@PostConstruct定义了初始化方法,通过注解@PreDestory定义了销毁方法。为了测试Bean的后置处理器,这里创建一个类BeanPostProcessExample
package cn.hctech2006.boot.bootall.life;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
/**
* 后置Bean初始化器
*/
public class BeanPostProcessorExample implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanPostProcessor调用"+
"postProcessorBeforeInitialization方法,参数["+
bean.getClass().getSimpleName()+"]["+beanName+"]");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanPostProcessor调用"+
"postProcessorAfterInitialization方法,参数["+
bean.getClass().getSimpleName()+"]["+beanName+"]");
return bean;
}
}
注意,这个后置处理器将对所有的Bean有效,然后我们用一下代码进行测试
20:57:44.728 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
20:57:44.771 [main] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [/home/lidengyin/Downloads/code/boot-all/target/classes/cn/hctech2006/boot/bootall/bean/BussinessPersion.class]
20:57:44.771 [main] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [/home/lidengyin/Downloads/code/boot-all/target/classes/cn/hctech2006/boot/bootall/bean/Cat.class]
20:57:44.771 [main] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [/home/lidengyin/Downloads/code/boot-all/target/classes/cn/hctech2006/boot/bootall/bean/Dog.class]
20:57:44.771 [main] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [/home/lidengyin/Downloads/code/boot-all/target/classes/cn/hctech2006/boot/bootall/bean/User.class]
20:57:44.776 [main] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [/home/lidengyin/Downloads/code/boot-all/target/classes/cn/hctech2006/boot/bootall/controller/ControllerTest.class]
20:57:44.776 [main] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [/home/lidengyin/Downloads/code/boot-all/target/classes/cn/hctech2006/boot/bootall/life/BeanPostProcessorExample.class]
20:57:44.830 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
20:57:44.831 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
20:57:44.832 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
20:57:44.833 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
20:57:44.835 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'beanPostProcessorExample'
20:57:44.841 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'appConfig'
BeanPostProcessor调用postProcessorBeforeInitialization方法,参数[AppConfig$$EnhancerBySpringCGLIB$$f9c28b92][appConfig]
BeanPostProcessor调用postProcessorAfterInitialization方法,参数[AppConfig$$EnhancerBySpringCGLIB$$f9c28b92][appConfig]
20:57:44.842 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bussinessPersion'
20:57:44.863 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'cat'
BeanPostProcessor调用postProcessorBeforeInitialization方法,参数[Cat][cat]
BeanPostProcessor调用postProcessorAfterInitialization方法,参数[Cat][cat]
延迟依赖注入
[BussinessPersion]调用BeanFactoryAware的setBeanName
[BussinessPersion]调用BeanNameAware的setBeanFactory
[BussinessPersion]调用ApplicationContextAware的setApplicationCpntext
BeanPostProcessor调用postProcessorBeforeInitialization方法,参数[BussinessPersion][bussinessPersion]
[BussinessPersion]调用PostContructor定义的自定义初始化方法
[BussinessPersion]调用InitializingBean的afterPropertiesSet
BeanPostProcessor调用postProcessorAfterInitialization方法,参数[BussinessPersion][bussinessPersion]
20:57:44.864 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'dog'
BeanPostProcessor调用postProcessorBeforeInitialization方法,参数[Dog][dog]
BeanPostProcessor调用postProcessorAfterInitialization方法,参数[Dog][dog]
20:57:44.865 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'user'
BeanPostProcessor调用postProcessorBeforeInitialization方法,参数[User][user]
BeanPostProcessor调用postProcessorAfterInitialization方法,参数[User][user]
20:57:44.871 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'controllerTest'
BeanPostProcessor调用postProcessorBeforeInitialization方法,参数[ControllerTest][controllerTest]
BeanPostProcessor调用postProcessorAfterInitialization方法,参数[ControllerTest][controllerTest]
20:57:44.873 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'dataSource'
BeanPostProcessor调用postProcessorBeforeInitialization方法,参数[BasicDataSource][dataSource]
BeanPostProcessor调用postProcessorAfterInitialization方法,参数[BasicDataSource][dataSource]
20:57:44.901 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@2e5c649, started on Thu Apr 09 20:57:44 CST 2020
[BussinessPersion]调用PrePost定义的自定义销毁方法
[BussinessPersion]调用DisposableBean的setBeanFactory
Process finished with exit code 0
从日志可以看出,对于Bean后置处理器(BeanPostProcess)而言,他对所有的Bean都起作用,而其他接口则是对单个Bean起作用。我们还可以注意到BussinessPerson执行的流程是上图所画的流程。有时候Bean的定义可能使用第三方的类,此时可以使用注解@Bean来自定义初始化和销毁工作,如下所示:
@Bean(initMethod="init", destoryMethod="destory")