【Spring源码】7.IOC之单例Bean实例化大体流程
前言
当Spring经过注解的扫描或者 解析xml, 把所有需要注册的Bean 封装成BeanDefinition, 并且了把实现了BeanPostProcessor接口的类实例化,并且加入到BeanFactory中,接下来就可以 从BeanFactory 中取出BeanDefinition,进行实例化了。
refresh.finishBeanFactoryInitialization(beanFactory)
imageLoadTimeWeaverAware接口
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-
取出beanFactory的内置容器中, 取出 所有BeanDefiniation的name集合, 然后遍历。
-
根据 beanName 获取到 对应的 beanDefinition。
-
从beanDefinition里判断,如果是非抽象的类 && 单例的, 和非懒加载的, 就走这里实例化。
抽象类不能实例化,这种BeanDefinition主要用来被其他的beanDefinition集成。
懒加载的不提前实例化, 只有 其他bean 需要注入这个bean的时候, 才会 实例化。
- 如果是FactoryBean类型, 那么会实例化当前Bean(beanName会有 & 前缀)。后面有单独的篇章来讲。
- 如果不是FactoryBean类型,则实例化当前bean。
doGetBean
如果缓存里存在该bean。
刚开始进来是没有的,因为还没实例化。实例化好之后下次就会从缓存里拿,直接return出去。
这里会判断 是否是要获取FactoryBean接口 getObject()返回出来的实例
不是FactoryBean接口类型,直接返回这个bean
是FactoryBean接口类型,并且 beanName 不带 &前缀, 则会 调用该bean的getObject() 替换 这个bean,返回出去。
第一次进来缓存里不存在bean,走创建Bean的流程
dependsOn
还是个方法往下走
是否 配置 dependsOn属性 配置了的话,先实例化 beanName = dependsOn里配置的beanName 的bean。
image创建bean
走完dependsOn的流程后,代码接着往下走
这里有三个关于作用域的判断: 单例 , 多例 和 其他作用域。
- 单例 : 其实 就是有一个 单例池 缓存 , 第一次创建 , 第二次 走缓存, 保证返回的永远都是同一对象。
- 多例 : 可以看到每次都会 createBean,创建新的。
- 其他作用域 : 需要 自己定义 管理bean的方式 : 实现 Scope 接口,重写对bean的 get, remove 方法。
这里不管是单例还是 多例, 创建 bean 的 方法都是一样的 。
单例Bean的创建, 走的是getSingleton(String beanName, ObjectFactory<?> singletonFactory):
如果缓存不存在, 就会调用第二个 参数 传进来的 匿名类 对象 的getObject方法创建实例 :
获取到创建好的实例后, 会缓存在单例池中,下次 获取就从缓存拿。
image上图 红框内的代码, 就会调到传进来的 匿名实现类对象的 方法体 :createBean(beanName, mbd, args), 创建并返回bean。
imagecreateBean
imagedoCreateBean:
这个方法就是bean实例化的核心方法了, 有创建实例,依赖注入, aop代理等。
1.Bean的创建
image图中框柱的方法:createBeanInstance(beanName, mbd, args) 就会创建Bean,返回出来, 但是 属性 还没有被依赖注入, 各种 生命周期的 方法也都没调,只是 一个空对象。
创建bean的方法, 总得来讲都是 用 beanClass 或者是 FactoryBeanClass 获取 合适的构造方法 / 方法, 反射调用,创建对象 :
创建bean的方式 按优先级 有以下几种 :
-
factoryMethod :
-
FactoryBean + FactoryBean的非静态FactoryMethod
-
当前beanClass + 静态FactoryMethod
这种xml版本的用的比较少,但是注解版本里有个 很重要的实例化Bean的方式 :@Configuration + @Bean 就是用的这种方式, 带有@Bean修饰的方法会被封装成BeanDefinition, 里面的FactoryBean 属性 就是 类上有@Configuration 修饰的这个bean, FactoryMethod 就是@Bean修饰的方法。
-
-
带有@Autowired注解的有参构造
-
不带有@Autowired注解的有参构造
-
最后才是无参构造。
2. 各种信息的收集
image这个方法的作用 是 收集 一些 属性和方法 :比如@Autowired/@Value/@Resource的属性和方法,@PreDestory / @PostConstruct的方法, 封装成metedata, BeanDifinition中。 这个时候已经开始 为 依赖注入 和 调用bean生命周期方法 做准备了, 到时候需要依赖注入的属性和 需要调用的方法, 直接从 收集好的地方找,然后进行处理。
值得一提的是, 这个 阶段的处理 是 beanPostProcessor 的运用,会拿到 对应类型的MergedBeanDefinitionPostProcessor, 遍历调用postProcessMergedBeanDefinition方法, 来反复的收集 对应的 信息。每个BeanPostProceesor 各司其职, 负责收集自己关注的 属性或者方法.
image3.依赖注入
image上一步已经把需要依赖注入的属性收集到BeanDifinition中了, 依赖注入的时候, 取出需要依赖注入的属性,
- 如果是@Autowired/@Resource注解修饰的 引用类型, 就会 触发 对应类型Bean的getBean, 如果这个属性对应的Bean没有实例化, 就会触发 需要被注入的Bean的实例化, 否则就走缓存
- 如果是@Value注解修饰的, 就会从配置文件里拿到对应的值,赋给这个 属性。
4.Bean的初始化
image这个方法 就会调用 bean里面的各种 生命周期方法 ,还有 各种实现 Aware接口的方法:
-
BeanNameAware、BeanClassLoaderAware、BeanFactoryAware接口方法的调用;
-
利用ApplicationContextAwareProcessor : 调用 EnvironmentAware、EmbeddedValueResolverAware、ResourceLoaderAware、
ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware接口方法; -
利用InitDestroyAnnotationBeanPostProcessor : 调用 @ProConstrcut 的方法;
-
利用 ConfigurationClassPostProcessor : 如果该类被B类@import进来,那么该方法会传入B类的注解元数据AnnotationMetadata;
-
调用 InitializingBean接口的 afterPropertiesSet();
-
调用 @InitMethod 方法;
-
ApplicationListenerDetector (BeanPostProcessor接口) : 判断是否是ApplicationListener类型,有加入到事件管理器中
-
AbstractAutoProxyCreator ( (BeanPostProcessor接口) 判断 是否需要生成AOP代理。
这里又是一堆 BeanPostProcessor接口的运用, 可以看出 BeanPostProcessor 接口 真的编织在Bean实例化的各个阶段。
加入缓存
经过 doCreateBean方法, 一个完成的Bean被创建成功。这个时候, 那个 匿名类对象的方法体就调完了,代码回到
image获取到新创建的 singletonObject, 最终 加入到缓存
image image下次还要用就会从缓存池里拿。
总结
单例Bean的实例化大体流程 大概就是这样, 其中FactoryBean接口bean的实例化 , bean 的 创建, 信息的收集, 依赖注入 和 bean的初始化 内部的细节 比较 多,这就只是一掠而过,后续会有专门的篇章来讲。