Spring中Bean的生命周期
前言
你有了解Spring中Bean的生命周期吗?可能大多数的人给我一样,对于Spring本身知之甚少。
我们仅限于在工作中如何使用Spring的Bean,使用起来Bean 是如何如何好用?但是我们好像很少去思考它都做了些什么?
当然学习是一个漫长且持久的过程,我们只能一点点积累,从而积少成多。
本章节主要来学习下Spring中Bean的生命周期。
Spring Bean的生命周期
我们在具体聊Spring Bean的生命周期之前,我们先聊一个概念:Spring Bean的作用域。它是什么?能被用来干嘛?不懂就查。
Bean 的作用域是指 Bean 在 Spring 整个框架中的某种行为模式。
比如 singleton 单例作用域,就表示 Bean 在整个 Spring 中只有一份,它是全局共享的,当有人修改了这个值之后,那么另一个人读取到的就是被修改后的值。
Spring框架支持以下五种Bean的作用域:
- singleton : 唯一 bean 实例,Spring 中的 bean 默认都是单例的。
- prototype : 每次请求都会创建一个新的 bean 实例。
- request : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP request 内有效
- session : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP session 内有效。
- global-session:在一个全局的HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。缺省的Spring bean 的作用域是Singleton。
而我们今天要聊的Spring Bean的生命周期,主要是指 singleton bean。
那什么是Spring Bean的生命周期呢?
对于普通的 Java 对象,当 new 的时候创建对象,然后该对象就能够使用了。一旦该对象不再被使用,则由 Java 自动进行垃圾回收。
那它的生命周期就是:实例化、不被使用时被垃圾机制回收。
而 Spring 中的对象是 bean,bean 和普通的 Java 对象没什么区别,只不过 Spring 不再自己去 new 对象了,而是由 IOC 容器去帮助我们实例化对象并且管理它,我们需要哪个对象,去问 IOC 容器要即可,Spring Bean 的生命周期完全由容器控制。
它分为四个阶段:实例化 -> 属性赋值 -> 初始化 -> 销毁
1)实例化 Instantiation
2)属性赋值 Populate
3)初始化 Initialization
4)销毁 Destruction
接下来,我们结合源码来对Bean的生命周期这个过程进行探究。
org.springframework.context.support.AbstractApplicationContext.refresh()
--> org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization // 初始化bean(延迟加载除外)
--> org.springframework.beans.factory.config.ConfigurableListableBeanFactory.preInstantiateSingletons()
--> org.springframework.beans.factory.support.AbstractBeanFactory.getBean
--> org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean
--> org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean // 创建 bean(实例化 bean)
--> org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean
Spring Bean的初始化是在Spring 容器 refresh() 时进行的,Spring 容器 refresh 时会调用 finishBeanFactoryInitialization() 来初始化所有非延迟加载的 bean。
我们启动本地项目,在上述层级结构上打上端点,结合源码,我们来看下Bean的创建过程。
你会发现,在真正createBean之前,还有一个方法doGetBean,它是干嘛的?
我们进一步深入分析看下:
1、doGetBean 获取Bean
以上doGetBean方法简单的流程图,结合源码具体说明如下:
1、获取真正的BeanName:方法transformedBeanName()
参数 name,不一定是 beanName,可能是 aliasName,也有可能是 FactoryBean(带“&”前缀),所以这里需要调用 transformedBeanName()方法对 name 进行一番转换。
- 去除 FactoryBean 的修饰符。如果 name 以 “&” 为前缀,那么会去掉该 “&”,例如,name = “&studentService”,则会是 name = "studentService
- canonicalName方法:取指定的 alias 所表示的最终 beanName。
这里引入一个扩展点,Bean的alias是如何生成?key为别名、value是beanName ,而beanName的设置,源码如下:
如果类whiteApple的Bean 存在 别名:whiteApple3、whiteApple2、whiteApple1
那么alias最终会是:{“whiteApple3”:“whiteApple2”} {“whiteApple3”:“whiteApple1”}
2、获取缓存中的单例:getSingleton()
- 如果在缓存中可以单例, 有则进一步判断这个Bean是不是在创建中,如果是的就等待创建完毕,否则直接调用**getObjectForBeanInstance()**方法返回
- 如果在缓存中未存在单例,则继续下一步
3、isPrototypeCurrentlyInCreation方法:如果scope是prototype 并且还在创建中,直接抛出异常
4、getParentBeanFactory() 获取当前BeanFactory的parentBeanFactory
- parentBeanFactory不为空,并且containsBeanDefinition(beanName) 为false 则调用parentBeanFactory.doGetBean 返回Bean
- 反之 继续下一步
5、getMergedLocalBeanDefinition() 将父类的BeanDefinition与子类的BeanDefinition进行合并覆盖
6、如果Bean是单例(singleton),则调用createBean() 创建bean
接下来,我们继续看 createBean。
2、doCreateBean 创建Bean
以上doCreateBean方法简单的流程图,结合源码具体说明如下:
1、mbd.isSingleton() 判断是否是单例,如果是,则需要在缓存factoryBeanInstanceCache 中把beanName对应的数据remove掉。
2、BeanWrapper为空的情况下 调用createBeanInstance创建bean实例。
- 如果是@Bean方式声明bean的创建,详见createBeanInstance – instantiateUsingFactoryMethod 方法。
- bean如果没有有参构造器,默认使用无参的构造方法反射创建实例,详情见createBeanInstance – instantiateBean方法。
-
默认使用无参的构造方法:clazz.getDeclaredConstructor()
-
默认使用无参的构造方法:clazz.getDeclaredConstructor()
- bean如果有唯一的有参构造器,并且参数能在Spring容器中找到,则通过有参构造器反射。详情见createBeanInstance – autowireConstructor方法。
3、applyMergedBeanDefinitionPostProcessors:Bean后置处理器
在debug过程中,我们发现了三个后置处理器,那他们分别都干什么了呢?注解扫描 - CommonAnnotationBeanPostProcessor:负责@PostConstruct、@PreDestroy
- AutowiredAnnotationBeanPostProcessor :@Autowired、@Value
- ApplicationListenerDetector 暂不分析
如下图所示:
查到指定的注解注释的属性和方法,最后都封装到InjectionMetadata返回。
4、如果earlySingletonExposure为true,调用addSingletonFactory缓存数据。哈哈哈哈哈哈哈哈哈哈哈哈哈
-
为了解决循环引用问题,后续在单写章节聊这个点。
5、调用populateBean方法进行属性赋值 -
因为步骤3中applyMergedBeanDefinitionPostProcessors方法中,已经完成了属性数据的解析,当前此方法直接获取封装好的数据,进行处理即可。
例如:CommonAnnotationBeanPostProcessor.postProcessProperties()
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
//获取类中封装好的属性元数据
InjectionMetadata metadata = this.findResourceMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
return pvs;
} catch (Throwable var6) {
throw new BeanCreationException(beanName, "Injection of resource dependencies failed", var6);
}
}
6、调用initializeBean方法进行初始化
7、bean的销毁:ConfigurableApplicationContext.close()
public void close() {
synchronized(this.startupShutdownMonitor) {
this.doClose();
if (this.shutdownHook != null) {
try {
Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
} catch (IllegalStateException var4) {
}
}
}
}
8、扩展点:BeanPostProcessor
我们在学习的过程中,发现了很多继承BeanPostProcessor类的子类,因为spring bean在创建过程中不同的阶段都会回调BeanPostProcessor组件的方法,这样就可以达到扩展的目的。因为只要你自己实现了BeanPostProcessor组件,就可以在生命周期的不同阶段可以对你的bean进行不同的操作,达到自己的目的。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!