在 Spring 源码分析 - bean的加载_虾王之王的技术博客_51CTO博客 文章中,我们介绍了Spring对 获取bean的过程,但是并没有详细解释 Bean 是如何创建的,本文就来分析Spring是如何创建的bean。阅读本文前,建议先阅读衍生篇,方便更好理解,本文有很多涉及到 BeanPostProcesser 的部分。
我们知道
DefaultSingletonBeanRegistry#getSingleton(java.lang.String, ObjectFactory<?>)这一步创建了bean,如下图:
到了这一步,Spring 就基本对 Bean已经创建好了 不抱什么希望了,所以着手开始自己创建bean。
本文就来分析 getSingleton(String beanName, ObjectFactory<?> singletonFactory) 方法整个流程。
二、获取单例 - getSingleton
如下, 是整个 getSingleton方法的代码,做了一些基本注释
,
上面的代码注释也比较清楚,基本流程如下:
this.singletonObjects.get(beanName); :再次尝试从缓存中获取bean,若获取到,则直接返回。
if (this.singletonsCurrentlyInDestruction) :未获取到检测bena是否正在销毁,若是则抛出异常
beforeSingletonCreation 方法中记录bean正在创建的状态将beanName添加到singletonsCurrentlyInCreation集合中)。在循环依赖时可根据此判断。
singletonObject = singletonFactory.getObject(); :调用 ObjectFactory.getObject() 方法来实例化bean
afterSingletonCreation 方法移除bean正在夹杂的状态
addSingleton(beanName, singletonObject); : 对各种缓存状态做处理。
流程图如下所示 :
下面的流程图关于 beforeSingletonCreation 和 afterSingletonCreation 的注释写的有些问题,正确的描述如下:
inCreationCheckExclusions 不包含 beanName && singletonsCurrentlyInCreation 添加失败 :则认为Bean正在创建中,抛出异常
inCreationCheckExclusions 不包含 beanName && singletonsCurrentlyInCreation 移除失败 :认为Bean 已经创建结束,抛出异常。
可以非常直观的看出, getSingleton 方法中的关键逻辑非常简单,bean创建的具体逻辑在 singletonObject = singletonFactory.getObject(); 中,所以下面继续去分析singletonFactory.getObject()中做了什么。
三、创建bean - createBean概述
上面可以看到,主要步骤还是在回调的 getObject() 方法中。那么我们来看看在bean加载过程中的FactoryBean做了什么。代码如下:
兜了一大圈关键代码还是在createBean 方法里。接下来,我们就来仔细分析一下 createBean 方法。
AbstractAutowireCapableBeanFactory#createBean 方法代码如下 ,附带部分注释:
可以看到,createBean 的整体流程大致如下:
根据设置的class属性或者根据className来解析Class。
对override 属性进行标记及验证。
应用初始化前的后处理器,解析指定bean是否存在初始化前的短路操作。
创建bean。
返回bean
流程图如下:
四、创建bean - createBean详解
上面的逻辑看着似乎不复杂,实际上,真正的逻辑都被封装在了方法中,所以下面需要关注如下的几个方法:
1、resolveBeanClass
这里不再过多展示代码,这个方法的作用就是根据参数和返回值都能知道: 根据 BeanDefinition和 beanName 解析出 bean 的Class。
2、prepareMethodOverrides
见名知意: 准备方法重写,这里更多是做一个校验的功能。这个方法主要是针对 lookup-method 和 replaced-method 两个属性的,用来覆盖指定的方法。
详细代码如下:
解释一下上面的逻辑:
首先会判断是否有方法需要重写,这里的是根据 RootBeanDefinition 中的 methodOverrides 属性来进行判断,为空则表示没有。
若上述判断有方法需要覆盖,则会调用prepareMethodOverride(MethodOverride mo) 方法。而在 prepareMethodOverride(MethodOverride mo) 方法中会根据 需要覆盖的方法名称 来获取加载类中关于该方法的实现。如果获取不到 count == 0,则直接抛出异常,如果获取到只有一个 count == 1,则记录该方法并未被重载(因为Spring在方法匹配时,如果一个类中存在若干个重载方法,则在函数调用及增强的时候需要根据参数类型进行匹配,来最终确定调用的方法是哪一个,这里直接设置了该方法并未被重载,在后续方法匹配的时候就不需要进行参数匹配验证,直接调用即可)。
打个比方,比如指定覆盖A类中的 a方法,但是A类中可能存在多个a方法或者不存在a方法,若count == 0不 存在a方法,则谈何覆盖,直接抛出异常,若count ==1 则a方法的实现只有一个,标记该方法并未被重载后续可跳过参数验证的步骤。
3、resolveBeforeInstantiation
该方法主要是调用 InstantiationAwareBeanPostProcessor 来进行一些处理,这里实际上是给了用户一次代替Spring来创建bean的机会,代码实现上非常简单直接调用的后处理器方法。
该方法调用了后处理器的方法:
InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation : 在bean初始化前调用
BeanPostProcessor#postProcessAfterInitialization : 在bean初始化后调用
关于后处理器部分具体介绍请看衍生篇 :Spring 源码分析衍生篇四 : 后处理器 BeanPostProcesser
详细代码如下:在调用 doCreate 方法创建bean的实例前调用了该方法对 BeanDefinition 中的属性做一些前置处理。
其中 applyBeanPostProcessorsBeforeInstantiation 和 applyBeanPostProcessorsAfterInitialization 很明显就是调用bean的后处理器,也就是对后处理器中的 InstantiationAwareBeanPostProcessor 类型的后处理器进行 postProcessBeforeInstantiation 方法 和 BeanPostProcessor 类型的 postProcessAfterInitialization 方法的调用。
3.1 determineTargetType(beanName, mbd);
关于 factoryMethodName 值的由来 这一点我们在 Spring 源码分析衍生篇七 :ConfigurationClassPostProcessor 上篇 中有过强调。即如果通过@Bean 注入,则保存期工厂类的方法名称,简单来说就是配置类中对应该bean的注入方法名称。
需要注意的是 这里获取的 targetType 类型并不一定是真正生成的bean类型,也可能是实际类型 的父类或者父接口 。因为对于通过 @Bean 注解修饰注入到Spring容器的时候,BeanDefinition 的 factoryMethodName 属性值不为空,指向其工厂类的方法名。并且由于多态的特性,其工厂方法引入的类型并不一定是实际类型。这个类型的错误会在 AbstractAutowireCapableBeanFactory#doCreateBean 中纠正过来.
比如 :下面的 demoService()方法实际生成的类型是 DemoServiceImpl。这里返回的类型是DemoService 。那么我们这里获取到的 targetType 就是 DemoService.class。其 BeanDefinition.factoryMethodName = demoService (即 DemoConfig 生成DemoService的方法的名称)
3.2 postProcessBeforeInstantiation
在bean 实例化前调用,也就是将 AbstractBeanDefinition 转换为 BeanWrapper 前的处理。给子类一个修改BeanDefinition的机会,也就是说当程序经过这个方法后,bean可能已经不是我们所认为的bean了。或许是一个经过代理的代理bean。可能是通过cglib生成的,也可能是通过其他技术生成的。
3.3 postProcessAfterInitialization
这里是bean创建后的后置方法调用,逻辑基本类似。不同的是到达这一步时,Bean已经创建成功,并且注入属性也进行了赋值。
需要注意,如果bean交由Spring来创建,那么Spring会将需要的属性注入到bean中,如果是自己代理生成(比如 通过 postProcessBeforeInstantiation 方法生成),那么需要自己解决bean的属性注入问题。
4、创建bean - doCreateBean
代码执行到这里,可以确定第三步中并没有返回一个非空的bean(BeanPostProcessor 并没有代理生成一个bean)。所以Spring开始自己着手创建bean。do开头的方法才是真正做事情的,所以这里才是真正创建bean的地方。
doCreateBean 代码如下,带有详细注释:
可以看到大致逻辑如下:
createBeanInstance(beanName, mbd, args) :实例化bean,将BeanDefinition转换为BeanWrapper
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); : MergedBeanDefinitionPostProcessor 后处理器的应用。bean合并后的处理,比如 @Autowired、@Value注解正是通过 AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition 此方法实现的预解析。
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); : 关于循环依赖的处理,添加 ObjectFactory到singletonFactories缓存中,同时这里给了用户一个机会通过调用 SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference 方法来由用户生成暴露的实例
populateBean(beanName, mbd, instanceWrapper); :对创建的bean内部的一些属性进行填充注入
initializeBean(beanName, exposedObject, mbd); : 初始化bean的一些属性,如Aware 接口的实现, init-method属性等
循环依赖检查。和第四步不同的是,这里了是判断是否无法解决循环依赖,否则抛出异常。
registerDisposableBeanIfNecessary(beanName, bean, mbd); : 注册DisposableBean
完成创建并返回。
doCreatebean 方法的代码量非常大,这里由于篇幅问题,需要新开篇分析,所以本文中挑选几个简单的方法分析,复杂的方法新开篇分析。
4.1 createBeanInstance
见名知意 : 该方法完成了bean的实例创建。
大概逻辑可以概括为:
如果存在工厂方法则使用工厂方法进行初始化
若类有多个构造函数,则根据参数锁定构造函数并初始化
如果即不存在工厂方法也不存在带参构造函数,则使用默认的构造函数进行bean的实例化。
具体的代码分析已成文 : Spring源码分析六:bean的创建④ - createBeanInstance
4.2 applyMergedBeanDefinitionPostProcessors
这种方法命名的也见得多了,见名知意: 该方法完成了 MergedBeanDefinitionPostProcessors 后处理器的功能。主要是 bean合并后的处理。在 AutowiredAnnotationBeanPostProcessor对postProcessMergedBeanDefinition方法的实现中,就对@Autowired、@Value 等注解进行了一系列的预处理,这里我们并不需要太过在意。
关于 AutowiredAnnotationBeanPostProcessor 的内容详参衍生篇 : Spring源码分析衍生篇五 :AutowiredAnnotationBeanPostProcessor
详细代码如下,不再具体分析
4.3 addSingletonFactory
这一部分的逻辑就是为了解决循环依赖的问题,将未创建完成的当前bean,通过ObjectFactory进行一个包装,提前暴露给其他bean。
我们实际的分析是一部分代码块如下:
首先我们需要分析出 earlySingletonExposure 为true的条件:
bean是单例
允许循环依赖
当前bean正在创建中 : singletonsCurrentlyInCreation 包含当前bean。在Spring 中 有专门的属性记录 bean的加载状态 – DefaultSingletonBeanRegistry#singletonsCurrentlyInCreation。在bean创建前会将bean添加,bean创建结束后将bean移除。这一点我们在前篇有过提及。
满足上述三个条件后,则会 调用 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); 方法。这个方法作用是将正在创建的bean缓存起来,主要目的还是用来解决循环依赖的问题。
循环依赖相关内容请阅: Spring 源码分析衍生篇二 : Spring中的循环依赖
详细代码如下:
该方法中 getEarlyBeanReference 调用了后处理器的方法,可用于用户自己扩展替换Spring生成的提前暴露的对象 :
SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference (Object bean, String beanName);
4.4 populateBean
见名知意,下面这个方法是用来属性注入的。
populateBean 方法则是对bean 属性的注入,上面的 createBeanInstance 方法创建了 bean,但是其内部属性并没有注入,比如通过 @Autowired 注解注入的变量属性,此时还为null,需要对这种属性进行注入,这一步就是完成这种功能。
在这里方法里按照如下顺序调用了后处理器
InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation : 返回true 才会调用下面两个方法
InstantiationAwareBeanPostProcessor.postProcessProperties : 进行属性的注入。
InstantiationAwareBeanPostProcessor.postProcessPropertyValues : 已过时
篇幅所限,详细的代码分析请阅 Spring源码分析七:bean的属性注入⑤ - populateBean
4.5 initializeBean
到达这一步,其实bean已经创建结束了,这一步是完成最后的功能,提供一些功能的实现,如Aware 接口的实现, init-method、InitializingBean属性等。
篇幅所限,详情请阅 : Spring源码分析八:bean的初始化⑥ - initializeBean
4.6 循环依赖检查
Spring 循环依赖的解决仅对单例且非构造函数构造的形式有效,对于原型模式的bean,Spring直接抛出异常,在这个步骤中会检测已经加载的bean 是否已经出现了循环依赖,并判断是否需要抛出异常。
关于这一段的逻辑参考 : https://blog.youkuaiyun.com/qq_18297675/article/details/103674833
整个逻辑如下:
getSingleton(beanName, false); : 从缓存中获取缓存对象,这传递的false, 直接从 earlySingletonObjects 中获取循环依赖的对象 earlySingletonReference。
如果 earlySingletonReference == bean ,说明bean没有被修改,直接赋值即可。
如果 earlySingletonReference != bean ,那么说明 在 下面的代码中,bean被修改了
此时需获取依赖于当前bean的 dependentBeans。如果dependentBeans 中有已经创建好的,那么则抛出异常
4.7 registerDisposableBeanIfNecessary
这一步的目的是实现 destory-method 属性,如果bean配置了该属性,则需要注册以便在销毁时调用。
详细代码如下: