美文网首页spring
【Spring源码】7.IOC之单例Bean实例化大体流程

【Spring源码】7.IOC之单例Bean实例化大体流程

作者: 天还下着毛毛雨 | 来源:发表于2022-01-20 12:44 被阅读0次
    image

    前言

    当Spring经过注解的扫描或者 解析xml, 把所有需要注册的Bean 封装成BeanDefinition, 并且了把实现了BeanPostProcessor接口的类实例化,并且加入到BeanFactory中,接下来就可以 从BeanFactory 中取出BeanDefinition,进行实例化了。

    refresh.finishBeanFactoryInitialization(beanFactory)

    image

    LoadTimeWeaverAware接口

    image

    这段代码可以看到, 从beanFactory 拿出LoadTimeWeaverAware类型的 beanName,提前 getBean。getBean 其实就是 实例化。

    这说明 实现了LoadTimeWeaverAware 接口的Bean 比 其他普通的Bean 更先实例化。

    用途

    可以利用这个接口 优先实例化 某些 bean。

    场景 :

    比如mybatis里, 有一个公共的Mapper, 专门提供某些 公共的sql片段 给其他的Mapper文件 引用(include)。但是由于mybatis 解析xml文件 , 是按照 文件顺序的。公共的Mapper不一定在 所有Mapper之前 , 那么在公共的Mapper之前的Mapper被解析 到 要include 公共Mapper的sql 片段时,就会找不到报错。

    所以需要优先实例化 公共的Mapper.

    实现方式 :

    注册一个Bean, 实现LoadTimeWeaverAware接口,并且 拿到 ApplicationContext对象, 去 getBean(公共的Mapper)。 这样就能 在这个bean实例化的过程中, 再优先触发 我们想要优先实例化的bean 的 实例化。

    /**
     * 在所有mapper实例化之前,先实例化CommonMapper,让其他mapper的include可以引用到CommonMapper的sql片段
     */
    @Component
    public class CommonMapperLoadTimeAware implements LoadTimeWeaverAware, ApplicationContextAware {
    
        @Override
        public void setLoadTimeWeaver(LoadTimeWeaver loadTimeWeaver) {
    
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            applicationContext.getBean(ClassUtils.getShortNameAsProperty(CommonMapper.class));
        }
    
    }
    

    单例Bean的实例化

    实例化LoadTimeWeaverAware接口的接口之后,接下来就是实例化所有单例bean的方法beanFactory.preInstantiateSingletons();

    image image
    1. 取出beanFactory的内置容器中, 取出 所有BeanDefiniation的name集合, 然后遍历。

    2. 根据 beanName 获取到 对应的 beanDefinition。

    3. 从beanDefinition里判断,如果是非抽象的类 && 单例的, 和非懒加载的, 就走这里实例化。

      抽象类不能实例化,这种BeanDefinition主要用来被其他的beanDefinition集成。

      懒加载的不提前实例化, 只有 其他bean 需要注入这个bean的时候, 才会 实例化。

      1. 如果是FactoryBean类型, 那么会实例化当前Bean(beanName会有 & 前缀)。后面有单独的篇章来讲。
      2. 如果不是FactoryBean类型,则实例化当前bean。
      image

    doGetBean

    如果缓存里存在该bean。

    刚开始进来是没有的,因为还没实例化。实例化好之后下次就会从缓存里拿,直接return出去。

    这里会判断 是否是要获取FactoryBean接口 getObject()返回出来的实例
    不是FactoryBean接口类型,直接返回这个bean
    是FactoryBean接口类型,并且 beanName 不带 &前缀, 则会 调用该bean的getObject() 替换 这个bean,返回出去。

    image

    第一次进来缓存里不存在bean,走创建Bean的流程

    dependsOn

    还是个方法往下走

    是否 配置 dependsOn属性 配置了的话,先实例化 beanName = dependsOn里配置的beanName 的bean。

    image

    创建bean

    走完dependsOn的流程后,代码接着往下走

    这里有三个关于作用域的判断: 单例 , 多例 和 其他作用域。

    1. 单例 : 其实 就是有一个 单例池 缓存 , 第一次创建 , 第二次 走缓存, 保证返回的永远都是同一对象。
    2. 多例 : 可以看到每次都会 createBean,创建新的。
    3. 其他作用域 : 需要 自己定义 管理bean的方式 : 实现 Scope 接口,重写对bean的 get, remove 方法。
    image

    这里不管是单例还是 多例, 创建 bean 的 方法都是一样的 。

    单例Bean的创建, 走的是getSingleton(String beanName, ObjectFactory<?> singletonFactory):

    如果缓存不存在, 就会调用第二个 参数 传进来的 匿名类 对象 的getObject方法创建实例 :

    获取到创建好的实例后, 会缓存在单例池中,下次 获取就从缓存拿。

    image

    上图 红框内的代码, 就会调到传进来的 匿名实现类对象的 方法体 :createBean(beanName, mbd, args), 创建并返回bean。

    image
    createBean
    image
    doCreateBean:
       这个方法就是bean实例化的核心方法了, 有创建实例,依赖注入, aop代理等。
    
    1.Bean的创建
    image

    图中框柱的方法:createBeanInstance(beanName, mbd, args) 就会创建Bean,返回出来, 但是 属性 还没有被依赖注入, 各种 生命周期的 方法也都没调,只是 一个空对象。

    创建bean的方法, 总得来讲都是 用 beanClass 或者是 FactoryBeanClass 获取 合适的构造方法 / 方法, 反射调用,创建对象 :

    创建bean的方式 按优先级 有以下几种 :

    1. factoryMethod :

      1. FactoryBean + FactoryBean的非静态FactoryMethod

      2. 当前beanClass + 静态FactoryMethod

        这种xml版本的用的比较少,但是注解版本里有个 很重要的实例化Bean的方式 :@Configuration + @Bean 就是用的这种方式, 带有@Bean修饰的方法会被封装成BeanDefinition, 里面的FactoryBean 属性 就是 类上有@Configuration 修饰的这个bean, FactoryMethod 就是@Bean修饰的方法。

    2. 带有@Autowired注解的有参构造

    3. 不带有@Autowired注解的有参构造

    4. 最后才是无参构造。

    2. 各种信息的收集
    image

    这个方法的作用 是 收集 一些 属性和方法 :比如@Autowired/@Value/@Resource的属性和方法,@PreDestory / @PostConstruct的方法, 封装成metedata, BeanDifinition中。 这个时候已经开始 为 依赖注入 和 调用bean生命周期方法 做准备了, 到时候需要依赖注入的属性和 需要调用的方法, 直接从 收集好的地方找,然后进行处理。

    值得一提的是, 这个 阶段的处理 是 beanPostProcessor 的运用,会拿到 对应类型的MergedBeanDefinitionPostProcessor, 遍历调用postProcessMergedBeanDefinition方法, 来反复的收集 对应的 信息。每个BeanPostProceesor 各司其职, 负责收集自己关注的 属性或者方法.

    image
    3.依赖注入
    image

    上一步已经把需要依赖注入的属性收集到BeanDifinition中了, 依赖注入的时候, 取出需要依赖注入的属性,

    1. 如果是@Autowired/@Resource注解修饰的 引用类型, 就会 触发 对应类型Bean的getBean, 如果这个属性对应的Bean没有实例化, 就会触发 需要被注入的Bean的实例化, 否则就走缓存
    2. 如果是@Value注解修饰的, 就会从配置文件里拿到对应的值,赋给这个 属性。
    4.Bean的初始化
    image

    这个方法 就会调用 bean里面的各种 生命周期方法 ,还有 各种实现 Aware接口的方法:

    1. BeanNameAware、BeanClassLoaderAware、BeanFactoryAware接口方法的调用;

    2. 利用ApplicationContextAwareProcessor : 调用 EnvironmentAware、EmbeddedValueResolverAware、ResourceLoaderAware、
      ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware接口方法;

    3. 利用InitDestroyAnnotationBeanPostProcessor : 调用 @ProConstrcut 的方法;

    4. 利用 ConfigurationClassPostProcessor : 如果该类被B类@import进来,那么该方法会传入B类的注解元数据AnnotationMetadata;

    5. 调用 InitializingBean接口的 afterPropertiesSet();

    6. 调用 @InitMethod 方法;

    7. ApplicationListenerDetector (BeanPostProcessor接口) : 判断是否是ApplicationListener类型,有加入到事件管理器中

    8. AbstractAutoProxyCreator ( (BeanPostProcessor接口) 判断 是否需要生成AOP代理。

      这里又是一堆 BeanPostProcessor接口的运用, 可以看出 BeanPostProcessor 接口 真的编织在Bean实例化的各个阶段。

    加入缓存

    经过 doCreateBean方法, 一个完成的Bean被创建成功。这个时候, 那个 匿名类对象的方法体就调完了,代码回到

    image

    获取到新创建的 singletonObject, 最终 加入到缓存

    image image

    下次还要用就会从缓存池里拿。

    总结

    单例Bean的实例化大体流程 大概就是这样, 其中FactoryBean接口bean的实例化 , bean 的 创建, 信息的收集, 依赖注入 和 bean的初始化 内部的细节 比较 多,这就只是一掠而过,后续会有专门的篇章来讲。

    相关文章

      网友评论

        本文标题:【Spring源码】7.IOC之单例Bean实例化大体流程

        本文链接:https://www.haomeiwen.com/subject/zxsjhrtx.html