Spring中Bean的生命周期

2023-12-25 17:41:41

前言

你有了解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进行不同的操作,达到自己的目的。

文章来源:https://blog.csdn.net/weixin_44831330/article/details/135203211
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。