spring

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

2022-01-20  本文已影响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的初始化 内部的细节 比较 多,这就只是一掠而过,后续会有专门的篇章来讲。

上一篇下一篇

猜你喜欢

热点阅读