首页 > 编程语言 >Spring源码分析(十)Spring中Bean的生命周期(下)

Spring源码分析(十)Spring中Bean的生命周期(下)

时间:2023-09-07 14:06:42浏览次数:55  
标签:pvs 生命周期 mbd Spring beanName bean Bean 源码 BeanDefinition

在上篇文章中,写了bean的生命周期的简单介绍,主要介绍了整个生命周期中的初始化阶段以及基于容器启动停止时LifeCycleBean的回调机制。另外对bean的销毁过程也做了简单介绍,但是对于整个bean的生命周期,这还只是一小部分,在这篇文章中,我将继续完成剩下部分的内容,同时对之前的内容做一次付息。整个bean的生命周期,按照之前的介绍,可以分为四部分:

  • 实例化
  • 属性注入
  • 初始化
  • 销毁

本文主要介绍实例化和属性注入阶段

生命周期概念补充

虽然我们一直说整个bean的生命周期分为四个部分,但是相信很多人一直对bean的生命周期到底从哪里开始,到哪里结束没有一个清晰的概念。可能你会说,不就是从实例化开始,到销毁结束吗?当然,这并没有错,但是具体什么时候算开始实例化呢?什么时候又算销毁呢?

我认为,整个Spring中的bean的生命周期,从第一次调用后置处理器的applyBeanPostProcessorBeforInstantiation方法开始的,这个方法见名知意,翻译过来就是在实例化之前调用后置处理器。而applyBeanPostProcessorAfterInitialization方法的调用,意味着bean的生命周期从创建阶段的结束。对于销毁没什么歧义,就是在调用对应bean的销毁方法就意味着这个bean走到了生命的尽头,标志和bean生命周期的结束,结合上篇文章的结论,现在把bean的生命周期的范围界定如下:

Spring源码分析(十)Spring中Bean的生命周期(下)_属性注入

需要注意的是,对于BeanDefinition的扫描,解析,验证,并不属于bean的生命周期的一部分。这样清晰的界定bean的生命周期的概念是很有必要的,业余刚刚开始对我们而言,bean的生命周期就是一团乱麻,但是至少我们现在已经抓到了线头。而整个bean的生命周期,我将其分为两部分:

  • 创建
  • 销毁

对于销毁阶段,不需要太多关注,对于创建阶段,开始的标志位:applyBeanPostProcessorBeforInstantiation方法执行,结束的标志位:applyBeanPostProcessorAfterInitialization方法执行。

基于上面的结论,下面开始进行对于本文中代码的分析,我们还是参考下面这个图:

Spring源码分析(十)Spring中Bean的生命周期(下)_生命周期_02

实例化

整个实例化的过程主要对于上图的3-11-6-4(createBean)和3-11-6-4-1(doCreateBean)

createBean流程分析:

Spring源码分析(十)Spring中Bean的生命周期(下)_实例化_03

Spring源码分析(十)Spring中Bean的生命周期(下)_实例化_04

可以看到,第一步和第二步还是对BeanDefinition中的一些属性做处理,它并不属于bean生命周期的一部分,直接跳过,看第三步的代码:

Spring源码分析(十)Spring中Bean的生命周期(下)_实例化_05

对于AbstractAutoProxyCreator中的applyBeanPostProcessorsBeforeInstantiation这个方法的分析暂且不管,等到AOP阶段在进行详细分析。这里暂且只需要指定这个方法会觉得在后续中要不要为这个bean产生代理

doCreateBean流程分析:

Spring源码分析(十)Spring中Bean的生命周期(下)_实例化_06

Spring源码分析(十)Spring中Bean的生命周期(下)_实例化_07

Spring源码分析(十)Spring中Bean的生命周期(下)_属性注入_08

第一步:factoryBeanInstanceCache什么时候不为空?

// 第一步:如果是单例的,就把这个factoryBeanInstanceCache删掉
if (mbd.isSingleton()) {
   instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}

这段代码很复杂,这里就单纯用理论解释下:

假设我们现在有一个IndexService,它有一个属性A,代码如下:

@Component
public class IndexService {
   @Autowired
   A a;


   public A getA() {
      return a;
   }
}

而这个A又是采用FactoryB的形式配置的,如下:

@Component
public class MyFactoryBean implements SmartFactoryBean {
   @Override
   public A getObject() throws Exception {
      return new A();
   }
   @Override
   public Class<?> getObjectType() {
      return A.class;
   }
   // 这个地方并不一定要配置成懒加载,这里只是为了让MyFactoryBean这个Bean在IndexService之后实例化
   @Override
   public boolean isEagerInit() {
      return false;
   }
}

我们思考一个问题,在上面这种场景下,当IndexService要完成属性注入时,Spring会怎么做?

Spring知道IndexService要注入一个类型为A的属性 ,所以它会遍历所有解析出来的BeanDefinition,然后每一个BeanDefinition中的类型是不是A类型,类似下面这样:

for (String beanName : this.beanDefinitionNames) {
   // 1.获取BeanDefinition
   // 2.根据BeanDefinition中的定义判断是否是一个A
}

上面这种判断大部分情况下是成立的,但是对于一种特殊的bean是不行的,就是我们之前说的FactoryBean,因为配置FactoryBean的目的并不是直接使用FactoryBean这个bean自身,而是想要通过它的getObject方法把一个对象方法放到Spring容器中,所以当我们遍历到一个BeanDefinition,并且这个BeanDefinition是一个FactoryBean时就需要做特殊处理,我们知道FactoryBean中有一个getObjectType方法,通过这个方法可以得到要被这个FactoryBean创建对象的类型,如果我们能调用这个方法的话,那儿就可以来判断这个类是不是一个A了。

但是,子我们上面的例子中,这个时候MyFactoryBean还没有被创建出来,所以Spring这个时候回去实例化这个MyFactoryBean,然后调用其getObjectType方法,再去做类型判断,最后进行属性注入,伪代码如下:

for (String beanName : this.beanDefinitionNames) {
   // 1.获取BeanDefinition
   // 2.如果不是一个FactoacryBean,直接根据BeanDefinition中的属性判断
   if(不是一个FactoacryBean){
      //直接根据BeanDefinition中的属性判断是不是A
   }
   // 3.如果是一个FactoacryBean
   if(是一个FactoacryBean){
      // 先创建这个FactoacryBean,然后再调用getObjectType方法了
   }
}

第二步:创建对象(createBeanInstance)

Spring源码分析(十)Spring中Bean的生命周期(下)_属性注入_09

Spring源码分析(十)Spring中Bean的生命周期(下)_实例化_10

在创建对象的时候,其余代码暂且不做过多关注,只需要知道在创建对象的过程中,Spring会调用一个后置处理器来推断构造函数。

第三步:applyMergedBeanDefinitionPostProcessors

应用合并后的BeanDefinition,Spring自身利用这点做了一些注解元数据的缓存。

就以AutowiredAnnotationBeanPostProcessor这个类的对应方法看大概作用:

Spring源码分析(十)Spring中Bean的生命周期(下)_属性注入_11

第四步:getEarlyBeanReference

Spring源码分析(十)Spring中Bean的生命周期(下)_实例化_12

第五步:属性注入(populateBean)

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
   if (bw == null) {
      if (mbd.hasPropertyValues()) {
         // 省略异常
      }
      else {
         return;
      }
   }


   boolean continueWithPropertyPopulation = true;


   if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
      for (BeanPostProcessor bp : getBeanPostProcessors()) {
         if (bp instanceof InstantiationAwareBeanPostProcessor) {
            InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
            if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {         // 主要判断之后是否需要进行属性注入
               continueWithPropertyPopulation = false;
               break;
            }
         }
      }
   }


   if (!continueWithPropertyPopulation) {
      return;
   }


   PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);


   // 自动注入模型下,找到合适的属性,在后续方法中再进行注入
   if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
      MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
      // Add property values based on autowire by name if applicable.
      if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME) {
         autowireByName(beanName, mbd, bw, newPvs);
      }
      // Add property values based on autowire by type if applicable.
      if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
         autowireByType(beanName, mbd, bw, newPvs);
      }
      pvs = newPvs;
   }


   boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
   boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);


   PropertyDescriptor[] filteredPds = null;
   if (hasInstAwareBpps) {
      if (pvs == null) {
         pvs = mbd.getPropertyValues();
      }
      for (BeanPostProcessor bp : getBeanPostProcessors()) {
         if (bp instanceof InstantiationAwareBeanPostProcessor) {
            InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
            // 精确注入下,在这里完成属性注入
            PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
            // 一般不会进行这个方法
            if (pvsToUse == null) {
               if (filteredPds == null) {
                  filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
               }
               pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
               if (pvsToUse == null) {
                  return;
               }
            }
            pvs = pvsToUse;
         }
      }
   }
   if (needsDepCheck) {
      if (filteredPds == null) {
         filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
      }
      checkDependencies(beanName, mbd, filteredPds, pvs);
   }


   if (pvs != null) {
      // XML配置,或者自动注入,会将之前找到的属性在这里进行注入
      applyPropertyValues(beanName, mbd, bw, pvs);

在上面整个流程中,我们主要关注一个方法,postProcessProperties,这个方法会将之前通过postProcessMergedBeanDefinition方法找到的注入点,在这一步进行注入,完成属性注入后,就可以开始初始化了,初始化的流程在上篇文章。

总结

在这两篇文章中,已经对bean的全部的生命周期做了详细分析,当然,对于一些复杂的代码,暂时没空去深究,因为之后打算写一系列专门的源码分析文章,大家可以关注我后续的文章,对于整个bean的生命周期可以总结画图如下:

Spring源码分析(十)Spring中Bean的生命周期(下)_属性注入_13

首先,整个Bean的生命周期我们将其划分为两个部分

  1. 创建
  2. 销毁

对于创建阶段,我们又将其分为三步

  1. 实例化
  2. 属性注入
  3. 初始化

我们可以看到,在整个过程中BeanPostP穿插执行,辅助Spring完成了整个bean的生命周期。

标签:pvs,生命周期,mbd,Spring,beanName,bean,Bean,源码,BeanDefinition
From: https://blog.51cto.com/u_15668812/7396393

相关文章

  • Spring注入DAO之怪错
     spring配置:<beanname="searchKeywordDAO"class="com.miracle.dm.sdmgr.searchkeyword.dao.impl.SearchKeywordDAOImpl"><propertyname="sqlMapClient"ref="sqlMapClient"/></bean><......
  • springboot项目自动运行脚本
    注意文件格式unix格式(Windowscrlf换行符有不可见字符)#!/bin/sh#服务名(要与配置文件中的server名保持一致)APP_NAME=""#git本地仓库路径GIT_RESPOSITORY=""#配置文件存储位置PROFILE_LOCATION=""#配置文件名PROFILE_NAME=""#日志文件存储位置LOG_LOCATION="......
  • 一套成熟的实验室信息管理系统源码,集前处理、检验、报告、质控、统计分析、两癌等模块
    一套成熟的实验室信息管理系统,集前处理、检验、报告、质控、统计分析、两癌等模块为一体的实验室信息管理系统。在整个检验过程中实时对检验结果监控、评估、分析、统计并对操作规程进行严格规范。它的开发和应用将加快检验科管理的统一化、网络化、标准化的进程。技术架构:ASP.NET......
  • spring中的bean使用注解创建,applicationContext.xml中需要写的内容,以及dao,service实
    2023-09-07applicationContext.xml<?xmlversion="1.0"encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns......
  • springBoot long类型 长id 到前端丢失精度问题
    在SpringBoot中,如果将Long类型的id传递到前端,可能会遇到精度丢失的问题。这是因为JavaScript无法精确地表示大于Number.MAX_SAFE_INTEGER(即9007199254740991)的整数。为了解决这个问题,你可以采用以下方法之一:方式1:通过注解方式@Data@EqualsAndHashCode(callSuper=false)......
  • SpringBoot学习之配置MyBatis常见异常
    Invalidboundstatement(notfound)出现原因和解决方法: 常见原因:1.mapper.xml中namespace和实际mapper接口所在的位置不一致。2.mapper.xml中的id名称和maapper接口中的方法名称不一致。3.如果上述两点都没有问题,那么大概率是application配置文件有配置错误。 程序和S......
  • 直播系统源码,系统分析篇:不可或缺的云转码系统
    科技的进步发展让人们的生活越来越便利,而当今社会我们最常使用让我们生活变得更便利的方式,就是下载适合我们解决困难的相关直播系统源码搭建出来的APP,在一个完整的APP内,有着多种的功能强大的系统,从这篇文章开始,我就为大家一一介绍这些系统,今天我们先介绍第一个系统:云转码系统。云转......
  • 全新二开游戏支付通道/话费/电网、抖音、快手、紫水晶带云端源码
    更新日志2021-12-29优化抖音通道更新剑网三金山版通道2021-12-27新增LOL手游(微信H5)新增来疯星币(双端H5)修复YY紫宝石通道修复YY金钻通道2021-12-25更新联通话费通道新增花椒双端H5通道更新虎牙通道更新腾讯系列通道2021-12-12更新YY金钻通道新增YY紫宝石通道(双端H5)2021-12......
  • springmvc
    模型-视图-控制器的设计模式,将程序进行分层解耦springmvc执行流程(1)用户发送请求至前端控制器(DispatcherServlet)(2)DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handle;(3)处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则......
  • pringboot/springmvc 获取项目中的所有请求路径
    springboot/springmvc获取项目中的所有请求路径1.编写业务代码@Autowired privateWebApplicationContextapplicationContext;@GetMapping("/getAllURL") publicRestfulResultgetAllURL(){ //获取springmvc处理器映射器组件对象RequestMappingHandlerMappin......