本篇以一个最基础的问题作为主线:如何实现一个最简单的spring-bean?
通过Class+xml配置文件就可以做到:
现在我们可以加一个main方法,用于获取这个bean:
可以看到,只使用了一个最简单的方法new XmlBeanFactory(new ClassPathResource("spring-beans.xml"))
,我们就获取了一个BeanFactory,并且从这个BeanFactory中取出了我们想要的bean
那么问题来了:
1、xml是如何被加载成BeanFactory的?
2、BeanFactory是如何取出我想要的Bean的
本篇先分析第二个问题:beanFactory是如何取出我想要的bean的
bean的生命周期解析
bean的生命周期概括
spring中bean的生命周期包括实例化、设置属性、初始化、使用bean、销毁bean
在bean的生命周期过程中有很多可选的插入式前置/后置处理器,例如:
实例化:给Bean分配内存空间(对应JVM中的“加载”,这里只是分配了内存)
设置属性:进行Bean的注入和装配
初始化
执行各种通知
执行初始化的前置工作
进行初始化工作(使用注解 @PostConstruct 初始化 或者 使用(xml)init-method 初始化, 前者技术比后者技术先进~)
执行初始化的后置工作
实例化和初始化是两个完全不同的过程,前者只是给Bean分配内存空间,而后者则是将程序执行权从系统级别转到用户级别,执行用户添加的业务代码
初始化bean的时序图可以参考:
bean获取相关类
根据上面的案例,获取bean是通过getBean方法触发的:
DefaultListableBeanFactory
在xml加载的流程中我们见到过它
在xml解析的流程中,它提供的能力是:
注册beanDefinitionL:
DefaultListableBeanFactory#registerBeanDefinition
获取beanDefinition:
DefaultListableBeanFactory#getBeanDefinition
一个注册完的beanDefinition会存储在beanDefinitionNames和beanDefinitionMap中,见下面核心成员变量:
核心成员变量
getBean(String name)
根据beanName获取bean,继承自AbstractBeanFactory
doGetBean
同样继承自AbstractBeanFactory
doGetBean方法正式进入了bean的获取和使用流程。在期间,bean完成了加载。doGetBean是提供给外部用于获取bean的方法,ApplicationContext中的getBean内部调用的就是doGetBean方法。
方法整体架构较为复杂,大致分为九个步骤:
步骤一、处理并获取beanName
首先要处理beanName,该方法调用了canonicalName方法,继承自SimpleAliasRegistry,是用于处理别名的,在xml解析的流程中也见到过它,它将别名处理完后,存放在aliasMap中,参考下面链接:
可以看到这里的主要目的是为了检查一下传进来的name是不是别名,如果在别名map中可以找到对应的beanid,则返回真实beanId后续用beanId去处理。
步骤二、根据beanName获取单例
在getSingleton方法中,spring尝试从缓存中加载,分别查看了singletonObjects(初始化完成的bean)、earlySingletonObjects(已经实例化但未初始化未填充属性的bean)、singletonFactories(bean的FactoryBean缓存)三个缓存,基本思路为:
singletonObjects中存在,返回singleObject对象;不存在,在earlySingletonObjects中找
earlySingletonObjects中存在,返回singleObject对象;不存在,在singletonFactories中找
从singletonFactories能找到对应的工厂实例ObjectFactory,调用getObject方法获取singleObject对象,找不到对应的ObjectFactory,返回null,同时在earlySingletonObjects中存beanName对应null,在singletonObjects中移除beanName
这就是spring解决循环依赖的三级缓存,代码如下:
这里要区分一个概念:ObjectFactory和BeanFactory
BeanFactory是承载所有Bean加载方法的类,是bean的生产车间,默认实现就是DefaultListableBeanFactory
ObjectFactory是bean的模具,每个bean一定有一个ObjectFactory与之对应,其核心方法
ObjectFactory#getObject
,实现该方法就让ObjectFactory创建某种bean的能力,即实例化、填充、初始化的全流程
话说回来,在DefaultSingletonBeanRegistry#getSingleton
方法中调用ObjectFactory#getObject
方法,拿到了一个Object,如果获取到了,这里其实就是把bean初始化好了这里需要注意的是,获取到的bean不是最终的bean,因为有FactoryBean工厂bean存在,因此走步骤三完成最终装配
如果这里拿到的是null,或者FactoryBean的实例就是null那肯定执行不了ObjectFactory#getObject
方法,bean也就连实例化都没完成,因此走步骤四-九,从头开始创建bean
步骤三、最终装配
首先看条件1,如果bean是无参构造的,且已经能找到创建好的bean了
判断条件2不是正在加载中,如果是的话,就不处理了,防止并发,如果不是,说明确实已经加载好了,然后获取真正的bean
什么叫获取真正的bean?这里是因为引入了FactoryBean的概念,可以参考getObjectForBeanInstance方法
步骤四、原型模式依赖检查
如果步骤二获取不到实例,进行原型模式依赖检查。
只有在单例情况下才会尝试解决循环依赖,BeanFactory 不缓存 Prototype 类型的 bean,无法处理该类型 bean 的循环依赖问题,也就是情况:AbstractBeanFactory#isPrototypeCurrentlyInCreation
判断true
步骤五、通过父容器查找bean
对于步骤二根据beanName在缓存中获取不到实例的,且通过步骤四循环调用检查的,则到父容器中查找 bean
委托父类工厂加载的两个条件:
父类工厂方法非null
!containsBeanDefinition(beanName)
是在检测如果当前加载的XML配置⽂件中不包含beanName所对应的配置,就只能到parentBeanFactory去尝试下了
最后再去递归的调⽤getBean⽅法
步骤六、将xml解析的GenericBeanDefinition转换为RootBeanDefinition
因为从XML配置⽂件中读取到的bean信息是存储在GernericBeanDefinition中的,但是所有的bean后续处理都是针对于RootBeanDefinition的,所以这⾥需要进⾏⼀个转换,转换的同时如果⽗类bean不为空的话,则会⼀并合并⽗类的属性。
步骤七、寻找依赖并优先进行依赖bean的实例化
bean初始化时某些属性可能是动态配置且依赖其他bean,因此初始化bean之前,先要初始化这个bean的依赖。要注意的是,这里提前解析的是depends-on属性,而非@Autowired或其他属性注入方式注入的依赖,因此这里如果循环依赖了,那就是死循环了
首先mbd.getDependsOn()
获取到所有的依赖bean,这里是通过BeanDefinition的dependsOn属性取的,实际上是处理以下这种场景:
depends-on属性的解析可以参考以下链接:
然后分别调用DefaultSingletonBeanRegistry#registerDependentBean
和AbstractBeanFactory#getBean
方法把依赖bean注册了并实例化了,这里都还好理解
上面还有一个isDependent方法,它判断的实际上是严格依赖的depends-on
属性,例如:
这种会直接判断抛异常
但是这个isDependent方法看起来判true的思路有点难懂,这里要结合下面DefaultSingletonBeanRegistry#registerDependentBean
方法一起看,就明白了
首先注册beanA的时候,isDependent判断是不会抛异常的,走到registerDependentBean方法,这里主要是注册了两个map:
dependentBeansMap:beanA对应的依赖bean的集合
dependeciesForBeanMap:beanB对应的依赖它的集合
这样在实例化beanB的时候,再次走到isDependent方法就会发现自己在被依赖的bean列表中
步骤八、针对不同的scope进行bean创建
默认的scope是单例singleton,还有其他配置例如prototype、request等,对应不同的初始化策略
可以看到三个条件分成三个场景:
singleton场景
prototype场景
非singleton和prototype场景、自定义场景等
主要看下前两个场景:
singleton场景
singleton场景主要是调用了两个方法:
而getSingleton里面一大坨实际上是一个ObjectFactory#getObject
的匿名实现,里面实际调用的是AbstractBeanFactory#createBean
方法
这么看实际上是三个方法共同作用的结果:
getSingleton:是前面那个getSingleton方法的多态版本,获取bean实例的框架方法,承担的作用包括实例化bean、完成一些三级缓存的判断和配置工作,如果bean是非FactoryBean,这里拿到就是bean本身,如果是FactoryBean,拿到的就是FactoryBean的bean本身,还要在后面额外获取到工厂产物
匿名方法:不涉及三级缓存的判断和配置,真正的实例化方法是createBean方法
getObjectForBeanInstance:一个获取真正bean的方法,其实前面的getSingleton方法中就已经见过了,点我跳转
prototype场景
prototype场景因为不存在循环依赖那些问题,就不需要搞一堆缓存的框架了,直接调用AbstractBeanFactory#createBean
同时前后各调用了一个AbstractBeanFactory#beforePrototypeCreation
和AbstractBeanFactory#afterPrototypeCreation
主要是登记了一个为了防止并发实例化bean的缓存
最后再取出真正的bean实例,与singleton方法大致是一致的
步骤九、类型转换
doGetBean方法的最后一步
获取bean已经基本结束了。但可能bean有requiredType,返回的bean是string,但requiredType是Integer,那么就需要这一步骤进行类型转换。用户也可以自定义转换器。
核心代码就是一步判断和一步调用:
可以继续了解TypeConverter#convertIfNecessary
,点我跳转
createBean
继承自AbstractAutowireCapableBeanFactory,这个类顾名思义,具有自动注入能力的beanFactory
方法分为四个步骤:
步骤一、加载bean对应的class
首先锁定class,然后根据设置的class属性或者根据className来解析Class。这里是为了预先判断bean对应的class有没有被解析过
doResolveBeanClass方法中提供了多种加载类的方法,包括直接实现ClassLoader的,以及调用spring自己的Class工具类的
步骤二、处理override方法
这里只是统计override方法数量,如果当前类的方法只有一个,那么就设置该重载方法没有被重载,后续就不需要进行方法的参数匹配验证了
步骤三、实例化的前置处理和短路操作
resolveBeforeInstantiation方法是对BeanDefinition中的属性做前置处理的。当经过前置处理后返回的结果如果不为空,那么会直接短路,略过后续的bean的创建⽽直接返回结果。这⼀特性虽然很容易被忽略,但是却起着⾄关重要的作⽤,我们熟知的AOP功能就是基于这⾥的判断的。
在方法内分别调用了前处理和后处理两个方法:applyBeanPostProcessorsBeforeInstantiation、applyBeanPostProcessorsAfterInitialization
其他前/后置处理器可以参考:
其中,前处理发生在AbstractBeanDefinition转换为BeanWrapper前,给子类修改BeanDefinition的机会。这里可能bean就已经不是原本的bean了,可能成为了一个经过处理的代理bean。可能是通过cglib生成的。
后处理方法返回的bean如果不为空,后续就不再进行普通bean的创建过程。也避免单例循环创建,也可以在A与B互相依赖,创建A后又创建B又要创建A的时候直接获取到,避免循环依赖。
步骤四、加载bean
步骤三之后还获取不到真正的bean,执行真正的加载操作。
doCreateBean - 初始化bean的核心
doCreateBean方法是初始化bean的核心方法,整个方法可以看作三个大步骤:
实例化
属性注入
初始化
步骤一、实例化
首先判断是不是单例,如果是单例,从缓存factoryBeanInstanceCache中获取并移除缓存
如果不是单例,或缓存里面没有,直接调用createBeanInstance方法构建
然后应用MergedBeanDefinitionPostProcessor这个BD合并后置处理器
实例化之后判断是否需要暴露早期bean工厂,从而解决循环依赖问题
工厂是一个匿名类,实现了ObjectFactory#getObject
方法,它实现的方式并不是像默认工厂DefaultListableBeanFactory那样走全流程,而是简单的获取bean早期实例,即没有属性注入没有初始化的bean
其中调用的方法是AbstractAutoProxyCreator#getEarlyBeanReference
,点我跳转
再看addSingletonFactory
就是对三级缓存相关的三个核心成员变量的操作了
步骤二、属性注入
直接参考populateBean部分
步骤三、初始化
直接参考initializeBean部分
然后后面是一部分缓存处理,用于处理循环依赖,如果有循环依赖,这里earlySingletonReference != null
才成立,才处理内部逻辑
最后注册到 disposableBeans 里面,以便在销毁bean 的时候 可以运行指定的相关业务,然后返回初始化完成的bean
createBeanInstance - 实例化并解决三级循环依赖
解析bean class,这个事情正常前面已经做过了
构建bean实例的方法有三种:supplier接口、工厂方法、构造函数
supplier接口流程
这里大致可以看出来,是从BeanDefinition里面解析出来的supplier接口,调用Supplier#get
方法
这里解析supplier接口使用了一个initBeanWrapper方法
工厂方法流程
首先调用ConstructorResolver#instantiateUsingFactoryMethod
方法创建工厂,点我跳转
从这一块就可以看出来,找了FactoryBean、找了FactoryMethod、找了参数argsToUse
然后在instantiate方法里面调用了SimpleInstantiationStrategy#instantiate
点我跳转
解析构造函数流程
如果已经解析好了,且需要自动装配,走自动装配流程,不需要的话走默认无参构造函数构造
自动装配流程参考autowireConstructor方法,
instantiateBean和之前一样调用到SimpleInstantiationStrategy#instantiate
方法,点我跳转,然后再调用initBeanWrapper初始化beanWrapper,点我跳转,和上面supplier接口方法流程基本一致
到这里实例化方法createBeanInstance的流程框架就结束了,点我返回doCreateBean
populateBean
继承自AbstractAutowireCapableBeanFactory,负责处理属性注入
这是bean属性设置前最后一次切入改变bean的机会,通过实现后处理器BeanPostProcessor#postProcessAfterInstantiation
方法处理。
这里不做拦截,就往后做属性注入了
然后就到了最重要的注入部分,第一种注入模式是根据xml中的autowire属性自动注入,例如:
如果没有指定这个属性,而是写了@Autowired注解,则这时候拿到的propertyValue还是null,继续往后
这里又一次找到了BeanPostProcessor,通过BeanPostProcessor#postProcessProperties
引入一些自定义的属性处理机制(个人开发者实现这个方法的较少),例如注解扫描器就是在这里引入的,对应的实现是AutowiredAnnotationBeanPostProcessor
找完对应的bean后到了真正的填充属性流程,参考applyPropertyValues
initializeBean - 初始化bean
分别通过aware方法初始化,和用户自定义的initMethod初始化。
首先是对特殊的bean进行处理:实现了Aware、BeanClassLoaderAware、BeanFactoryAware的处理。参考invokeAwareMethods方法
然后调用BeanPostProcessor#postProcessBeforeInitialization
方法做初始化前置处理,这个时候是已经完成了bean的实例化(createBeanInstance)和属性填充(populateBean)工作
激活自定义的init方法,见invokeInitMethods方法
最后调用BeanPostProcessor#postProcessAfterInitialization
做初始化后置处理,这时候bean已经实例化完成了
如果是AOP场景,初始化完成的后处理器中就会有AOP创建相关的,见AOP部分
getObjectForBeanInstance - 获取真正的bean
继承自AbstractBeanFactory#getObjectForBeanInstance
首先引入一个概念:FactoryBean
FactoryBean也是一个bean,因此也是通过spring这套逻辑加载的,但是它有个特殊点就是它是一个Factory,可以理解为它是bean的模具,专门用于生产一些特殊的不方便直接定义的bean
它有一个专门的“生产”方法:
这个getObject()方法的作用是,它让FactoryBean的create流程实际上调用的是FactoryBean#getObject
方法,即bean定义的是FactoryBean,但加载出来实际上是FactoryBean的产物
FactoryBean的作用主要是为了丰富bean的创建方式,生产一些特殊的不方便直接定义的bean
回来看getObjectForBeanInstance方法:
首先判断beanName是以“&”开头的,这里的意义是,如果我就是想取出FactoryBean本身实例而非FactoryBean的产物,定义bean的时候就要让beanName以&开头
如果name不以&开头,且不说FactoryBean,则直接返回本身
如果是FactoryBean,则调用FactoryBeanRegistrySupport#getCachedObjectForFactoryBean
获取产物
可以看到,也是先从缓存中取,缓存中没有,则从头走创建流程,调用getObjectFromFactoryBean
这里的缓存是factoryBeanObjectCache,可以参考前面核心成员变量部分
PS:Mybatis中的SqlSessionFactoryBean就是一个很典型的FactoryBean的案例,它的作用是将DAO层的xml文件(sql文件)加载成bean,参考下面连接:
getObjectFromFactoryBean
继承自FactoryBeanRegistrySupport#getObjectFromFactoryBean
首先加同步锁,加锁对象是前面看到的成品bean缓存
然后做double check,如果还是null,调用doGetObjectFromFactoryBean
后面调用一个插入式的后处理器postProcessObjectFromFactoryBean
这个方法在AbstractAutowireCapableBeanFactory#postProcessObjectFromFactoryBean中重写了一下,引入了后置处理器BeanPostProcessor
后置处理器可以参考链接:
doGetObjectFromFactoryBean
核心代码其实就一处:
调用FactoryBean#getObject
autowireByName、autowireByType
autowireByName比较简单,按name找bean
这里调用了AbstractAutowireCapableBeanFactory#unsatisfiedNonSimpleProperties
方法,看其逻辑,可知是为了从BD中拿到所有的属性名称
然后对于这个名称数组,遍历完成依赖bean的加载,这时候要调getBean方法,这里就有可能产生循环依赖
autowireByType与autowireByName比较类似,只是多了一步根据type解析依赖的流程
解析方法参考resolveDependency方法
resolveDependency
方法继承自AutowireCapableBeanFactory,在DefaultListableBeanFactory中实现
首先if判断了几个特殊的依赖类型:Optional、ObjectFactory、javax.inject.Provider,非这几种类型的走常规流程:
走到doResolveDependency方法
doResolveDependency
场景一、首先根据名称快速查找,这里是对应AutowiredAnnotationBeanPostProcessor处理@Autowired注解的流程,这里找到了就返回去。
场景二、这一步是对应QualifierAnnotationAutowireCandidateResolver处理@Value注解的流程,这里就能找得到,找到的话判断value是不是String类型,如果是,调用resolveEmbeddedValue方法解析出@Value注解属性值对应的真实值,因为@Value注解可能用于快速配置,真实值在配置文件application.properties中
spring-boot就是用的这一套流程
如果上面@Value找到的值非String类型,这里基于注册的TypeConverter#convertIfNecessary
做格式转换,点我跳转
场景三、这一步是处理集合型依赖,例如Array、List、Set、Map,下层也是调用到findAutowireCandidates方法
场景四、调用findAutowireCandidates方法查找单个依赖,如果单个依赖还找到了多个bean适配,还需要做过滤流程
resolveEmbeddedValue - 占位符解析
继承自AbstractBeanFactory
方法核心在这个for循环中,循环embeddedValueResolvers属性。这个属性中spring提供了一个默认的占位符解析器PropertySourcesPlaceholderConfigurer
可以看下这个属性加载的地方,在AbstractBeanFactory#addEmbeddedValueResolver
继续找它的调用点PlaceholderConfigurerSupport#doProcessProperties
再往上追溯可以看到PropertySourcesPlaceholderConfigurer#postProcessBeanFactory
,这个东西是很熟悉的,就是可插入式处理器BeanFactoryPostProcessor#postProcessBeanFactory
,关于它的介绍可以参考下链接:
spring-boot的配置项解析就是依赖这套加载逻辑执行的
applyPropertyValues
填充属性方法,继承自AbstractAutowireCapableBeanFactory
先判断是不是转了类型的属性,如果是直接塞,如果不是要加载自定义类型转换器和bean解析器
后面就是遍历property,先解析再转换。最后存入
这里参考BeanDefinitionValueResolver#resolveValueIfNecessary
方法,点我跳转
对于需要转换的类型的,参考方法convertForProperty
evaluateBeanDefinitionString
继承自AbstractBeanFactory,可见是一个很顶层的方法了
核心是这里,取资源编辑注册器属性private BeanExpressionResolver beanExpressionResolver
,继承自AbstractBeanFactory
如果注册了编辑器,则调用其BeanExpressionResolver#evaluate
方法,否则直接返回原始值,点我跳转
资源编辑注册器的初始化是在AbstractApplicationContext#refresh
流程的prepareBeanFactory方法中
initBeanWrapper
继承自AbstractBeanFactory
这里是为了初始化一些工具类,例如自定义的属性解析器,用于后面populateBean流程中注入属性时解析一些自定义的属性
这些特殊类型的解析器与ApplicationContext#refresh
方法,prepareBeanFactory步骤加载的默认解析器功能一样,都是为了将一些String值解析成某些复杂类型的
这些复杂类型解析器的编写可以参考:
invokeAwareMethods
代码比较简单,可以看出就是对实现Aware接口的bean做一些配置,包括beanName、beanFactory、beanClassLoader等
Aware接口比如BeanFactoryAware、ApplicationContextAware、ResourceLoaderAware、ServletContextAware等,是为了让bean有意识地知道自己是被谁创造的
比如以下案例:
这个案例中setBeanFactory在加载bean的时候自动调用,而execute就可以拿着beanFactory调用getBean方法,即时获取输入元素的bean,进而反射动态调用输入元素中的特有方法,省区了判断类型的if流程。
setBeanFactory方法实际被调用点就在invokeAwareMethods中
除了BeanFactoryAware,其他两个同理。
convertForProperty
承担了一些列类型转换的工作,例如把string类型转成resource[]类型,resources一般是spring中一个比较重要的配置项,而配置值都是使用String类型的路径,这里就需要用到了该方法
后续实际转换的代码可以参考到TypeConverterDelegate#convertIfNecessary
点我跳转
invokeInitMethods
首先判断是InitializingBean的实现类,是则激活InitializingBean#afterPropertiesSet
方法,其他扩展点可以参考:
这里判断bean配置了initMethod方法则调用对应的初始化方法
ConstructorResolver
instantiateUsingFactoryMethod
先创建,并初始化实例持有器BeanWrapper。
然后判断工厂是实例工厂还是静态工厂,这一部分最核心的代码如下:
实际就是为了取出工厂的bean、工厂的class。
然后开始解析参数,这部分比较复杂,整体思路是:
如果有入参,就直接使用
没有入参,就找解析的参数
先考虑有工厂方法的情况,
如果有就取工厂方法的类,并从缓存中取解析出来的参数,
如果没解析过,就找工厂方法是否有重载,找候选排序最高的(public优先,参数多的优先)
然后根据构造器参数值解析参数,做一些列参数判断。
然后就是对候选方法的遍历,遍历过程中对每个候选方法计算权重。
这里比较重要的是一开始判断参数个数,如果对不上就直接下一个,对上了后面创建参数列表holder
最后创建实例并配置到beanWrapper
然后下级再调用到SimpleInstantiationStrategy#instantiate
,点我跳转
autowireConstructor
与工厂方法的差异点在于:工厂方法会遍历所有的备选方法找出参数类型差异最小的,而自动装配构造找的一个满足条件的就停止。
备选方法的获取是来自bean的配置文件
如果备选只有1个即无参构造,就直接开始构造了
否则还是解析参数的流程,跟工厂类差不多。上来先找入参,没有看缓存是否解析过。没有再往下解析。解析过程中参数来源于配置文件
如果有多个备选构造函数,入参也解析完了,里面也是遍历算权重的流程,算完了之后也是调用初始化方法开始构造
然后instantiate再调用到调用到SimpleInstantiationStrategy#instantiate
点我跳转
SimpleInstantiationStrategy - 实例化策略
instantiate
分两个场景:
有lookup-method、replace标签的时候,就有MethodOverrides属性,用cglib代理的方式构造类。动态代理和切面相关见后
没有MethodOverrides的时候直接反射构造类
BeanDefinitionValueResolver
在bean加载过程中,属性填充DefaultListableBeanFactory#populateBean
方法找到属性对应的bean后,调用applyProertyValues方法解析属性值,就用到属性解析器。
resolveValueIfNecessary
给定一个PropertyValue,返回一个value,解析对工厂中其他bean的引用。
在这个方法中主体就是通过多个if-else if-else组织的,根据不同场景委托给不同方法
value可能是:
RuntimeBeanReference : 在解析到依赖的Bean的时侯,解析器会依据依赖bean的name创建一个RuntimeBeanReference对像,将这个对像放入BeanDefinition的MutablePropertyValues中
RuntimeBeanNameReference
BeanDefinitionHolder、BeanDefinition:均调用resolveInnerBean方法分析
DependencyDescriptor
ManagedArray:用来保存它所管理的Array元素,它可以包含运行时期的bean的引用(将被解析为bean对象).
ManagedList:用来保存它所管理的List元素,它可以包含运行时期的bean引用(将被解析为bean对象).
ManagedSet :用来保存它所管理的set值,它可以包含运行时期的bean引用(将被解析为bean对象)
ManagedMap :用来保存它所管理的map值,它可以包含运行时期的bean引用(将被解析为bean对象)
以ManagedArray为例:
可以看到下面又递归调用了BeanDefinitionValueResolver#resolveValueIfNecessary
,其实也好理解,是因为需要解析稽核类型中的元素的最终类型,到最底层都是在把依赖bean解析成String
看RuntimeBeanNameReference这个场景可见端倪:
是通过doEvaluate方法解析的
evaluate/doEvaluate
evaluate方法最终也会调用到doEvaluate方法中
而这里又回调到了DefaultListableBeanFactory#evaluateBeanDefinitionString
点我跳转
AbstractAutoProxyCreator - 早期/后期处理器
getEarlyBeanReference
方法看起来很简单,就是从内存中获取bean
它的核心在earlyProxyReferences这个成员属性,那这个earlyProxyReferences存进去又是在哪用的呢?看它的移除方法:
可知AbstractAutoProxyCreator实现了BeanPostProcessor#postProcessAfterInitialization
,在初始化后调用把内存清理掉
它的调用点在AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization
,而此方法调用就比较多了:
bean创建前提前获取bean的时候
AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation
initializeBean前断路操作,以及initailzeBean后的后置操作
TypeConverterDelegate - 类型转换框架
convertIfNecessary
首先找自定义的转换器。自定义转换器是继承了PropertyEditor的自定义工具类,它被加载到上下文中是在容器AbstractApplicationContext#refresh
流程中的prepareRefresh步骤
如果找不到自定义转换器,后面会取默认的,spring默认提供的转换器就是由String转成ResourceArray的转换器
而继续跟进doConvertValue方法
发现对于String类型的结果,调用doConvertTextValue方法
这里以ResourceArrayPropertyEditor#setAsText
方法为例
ResourceArrayPropertyEditor
setAsText
到这里就比较明显了,是通过PathMatchingResourcePatternResolver#getResource
做资源列表的解析。前面也分析过了。
AutowiredAnnotationBeanPostProcessor - @Autowired注解处理器
核心成员变量
维护了一个依赖注入元数据的缓存
postProcessMergedBeanDefinition
实现了MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition
接口方法,处理特殊的BD
统一调用了findAutowiringMetadata方法
postProcessProperties
从AbstractAutowireCapableBeanFactory#populateBean
属性注入流程中调用进来
与postProcessMergedBeanDefinition方法一样,都基于findAutowiringMetadata方法做
findAutowiringMetadata
注解扫描的核心方法
如果已经扫过,都会存在injectionMetadataCache缓存里面,因此先从缓存中取
如果缓存中没有,这里synchronized加锁做双判,然后调用buildAutowiringMetadata扫描并构造,然后存入缓存
buildAutowiringMetadata
方法的主体逻辑就是一个do-while循环
可以看到方法的主体部分,对每个类中的@Autowired属性和桥接方法,例如被注解了@Autowired方法的set方法,分别处理
最后一圈走完,有一个核心的地方就是取父类,这样使得子类可以注入父类标记了@Autowired的变量
解决bean的循环依赖
循环依赖的分类:自依赖、直接依赖、间接依赖。
spring对循环依赖的处理有三种情况:构造器循环依赖、setter循环依赖、
构造器循环依赖
例如以下配置:
对于这种情况,spring只能抛出BeanCurrentlyInCreationException异常,研发过程中需要使用@Lazy等方式解决。
setter循环依赖
或者说是@Autowired产生的循环依赖
对于这种情况,spring只能处理单例bean的循环依赖问题,即通过三级缓存解决:
三级缓存可以在DefaultListableBeanFactory中找到,是继承自DefaultSingletonBeanRegistry
三级缓存
一级缓存singletonObjects:最基础的单例缓存,限制 bean 在 beanFactory 中只存一份,即实现 singleton scope
二级缓存earlySingletonObjects:看名字应该就能猜到是未初始化未填充属性提前暴露的Bean,是与三级缓存配合使用的。这里如果发现bean是AOP代理的,保存的就是代理的bean实例beanProxy,目标bean还是半成品
三级缓存singletonFactories:Bean创建时提供代理机会的Bean工厂缓存
基于这种设计,没有发生循环依赖的bean就是正常的创建流程。相互引用的bean 会触发链路中最初结点放入三级缓存内容,调用getEarlyBeanReference 返回相应对象
spring解决的逻辑如下:创建beanA时暴露singletonFactory存入一级缓存,先根据无参构造创建一个earlySingletonObject存入二级缓存,注入依赖beanA的beanB,最终创建好singletonObject,重新注入beanA,存入三级缓存。
三级缓存相关方法
DefaultSingletonBeanRegistry#addSingletonFactory
:FactoryBean存入三级缓存,同时确保二级缓存中没有初始状态的beanDefaultSingletonBeanRegistry#getSingleton
:调用FactoryBean.getObject方法创建bean,存入二级缓存,移除三级缓存中的工厂beanDefaultSingletonBeanRegistry#addSingleton
:存入一级缓存,二级、三级缓存都移除对应早期bean和工厂bean
三级缓存相关方法的调用点
调用点1 AbstractBeanFactory#doGetBean
开始阶段调用DefaultSingletonBeanRegistry#getSingleton
尝试获取之前创建好的不完整单例,当这里拿不到,才会往后走触发getSingleton流程并且调用createBean方法
调用点2 AbstractAutowireCapableBeanFactory#doCreateBean
方法执行完第一步createBeanInstance后,判断需要暴露bean工厂,执行DefaultSingletonBeanRegistry#addSingletonFactory
,这时候还没有执行populateBean和initializeBean
调用点3 AbstractAutowireCapableBeanFactory#doCreateBean
方法完成bean初始化三步后,此时可能还有bean依赖的其他未创建的bean,则这个bean是不完整的,此时暴露bean的早期实例,调用DefaultSingletonBeanRegistry#getSingleton
,调用后工厂bean移除,初始状态bean存入二级缓存
调用点4 依赖bean也创建完,给这个bean整体配置好后,回到AbstractBeanFactory#doGetBean
的getSingleton方法后,走到getObejct方法后,存入完整bean,调用方法DefaultSingletonBeanRegistry#addSingleton
prototype循环依赖
spring无法完成处理,因为prototype作用域的bean不进行缓存。一般可以通过setAllowCircularReferences
为false来禁用循环引用。
评论区