doGetBean源码:
1 | java复制代码protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, |
之前讨论了doGetBean里面从缓存获取bean的代码,doGetBean方法接下来的else处理Bean的scope为prototype或者单例模式但是缓存中还不存在bean的情况:
Spring同样为scope为prototype的Bean设计了一个缓存列表
1 | java复制代码if (isPrototypeCurrentlyInCreation(beanName)) { |
1 | java复制代码protected boolean isPrototypeCurrentlyInCreation(String beanName) { |
相比单例的set,这里是ThreadLocal
,只记录当前线程创建出来的scope为prototype的Bean,上面的if如果是true的话证明有循环依赖。
通过了循环依赖校验之后看容器是否存在父容器,如果存在且当前容器里没有包含此bean的BeanDefinition实例,尝试去从父类容器递归查询
为了防止之前的beanName已经被转换的不成样子,将&重新加上,再调用父类的doGetBean或者getBean方法,如果父类是AbstractBeanFactory,则调用doGetBean
如果当前容器里包含了此bean的BeanDefinition实例则继续执行
1 | java复制代码// typeCheckOnly 是用来判断调用 getBean() 是否仅仅是为了类型检查获取 bean,而不是为了创建Bean |
进入markBeanAsCreated方法里,
1 | java复制代码protected void markBeanAsCreated(String beanName) { |
有一个双重锁检查机制,创建的Bean的名称加到alreadyCreated(类型Set)这个缓存中,在加入缓存之前需要将原先的MergedBeanDefinition
设置上一个需要清除的标识符,目的是让后续从容器中获取BeanDefinition时重新合并子类和父类的BeanDefinition,这样就可以防止元数据被改动后,BeanDefinition还是按照原来的数据去创建
1 | java复制代码protected void clearMergedBeanDefinition(String beanName) { |
clearMergedBeanDefinition回去容器中获取RootBeanDefinition实例,然后把该实例需要重新合并的状态设为true(之前提到过,只要指定了parent属性,则两个BeanDefinition就合并成一个来使用)
回到doGetBean,接下来会调用getMergedLocalBeanDefinition
方法来合并子类和父类的BeanDefinition,进入到该方法里:
1 | java复制代码protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException { |
先从mergedBeanDefinitions缓存里获取之前已经合并好的RootBeanDefinition实例,如果stale为true的话就会合并一遍BeanDefinition,随后返回。
回到doGetBean,获取到了BeanDefinition之后,就去对相关实例做合法性校验
1 | java复制代码checkMergedBeanDefinition(mbd, beanName, args); |
进入到checkMergedBeanDefinition方法里:
1 | java复制代码protected void checkMergedBeanDefinition(RootBeanDefinition mbd, String beanName, @Nullable Object[] args) |
看一下RootBeanDefinition是否是抽象的。
回到doGetBean,尝试从BeanDefinition里获取显式的依赖关系,如果有depends-on的话就检查是否有循环依赖关系
如果没有循环依赖的话则会将相关的依赖关系注册上
1 | java复制代码registerDependentBean(dep, beanName); |
1 | java复制代码public void registerDependentBean(String beanName, String dependentBeanName) { |
先获取name,之后就进入两重注册,第一重注册往dependentBeanMap
中写入键值对,key是被依赖的Bean名字,value是依赖他的Bean名字列表
第二重注册往dependenciesForBeanMap
中写入键值对,键值对和上面正好是相反的
回到doGetBean,执行完显式依赖关系注册之后就会递归调用getBean(dep)
来将依赖的bean创建出来,往后就是根据不同的scope进行不同的创建bean的操作了,分为Singleton、Prototype和其他。
除了Prototype是直接调用createBean(beanName, mbd, args)
去创建Bean实例之外,scope=其他的将createBean封装到了一个匿名参数里
进入scope.get()方法
1 | java复制代码Object get(String name, ObjectFactory<?> objectFactory); |
可见后面的匿名函数实现的是ObjectFactory的getObject方法,调用createBean方法去创建适配scope的实例。
下面主要分析scope = singleton的:
1 | java复制代码if (mbd.isSingleton()) { |
进入getSingleton方法:
也是接收一个ObjectFactory
对象然后实现其getObject方法。
进入createBean:
1 | java复制代码protected abstract Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) |
这是个抽象方法需要子类去实现。
再回到上面的getSingleton方法:
1 | java复制代码public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { |
先将一级缓存singletonObjects
上锁,获取到锁之后再次尝试从一级缓存里去获取实例,以防别的线程创建好了,如果获取不到实例就开始做真正的创建了:
先看一下目前容器是否正在销毁所有的单例:singletonsCurrentlyInDestruction
是个flag。
通过验证之后会来到beforeSingletonCreation(beanName)
,进入:
1 | java复制代码protected void beforeSingletonCreation(String beanName) { |
if 的第一个条件可以忽略,第二个条件是尝试将beanName放入正在创建的单例名字列表里,添加失败则会抛异常,因为bean的正常创建流程按理是进入不到这里的,如果beanName出现在正在创建的名字列表中则表明在本次操作之前对于同一个bean的创建已经在进行了,doGetBean的时候第一步从三级缓存中获取Bean实例的时候就应该已经获取到了。
在并发场景下,两个线程在三级缓存里都没获取到单例,就会来到这里。
再回到上面的getSingleton方法,接下来就可以去创建并获取bean实例了:newSingleton
设置为true方便后续调用addSingleton
来添加一级缓存。
异常处理跳过,看一下finally:
进入afterSingletonCreation:
1 | java复制代码protected void afterSingletonCreation(String beanName) { |
由于bean实例已经创建完毕了,所以会从正在创建的bean名字列表singletonsCurrentlyInCreation
中移除beanName
回到外面,接下来就会去判断是否是新创建的单例,之前标志位已经是true了,进入addSingleton
:
1 | java复制代码protected void addSingleton(String beanName, Object singletonObject) { |
执行完addSingleton之后就会返回完整的bean实例了
回到doGetBean的创建单例的逻辑中,返回完整的bean实例之后就会执行
1 | java复制代码// 如果是普通bean,直接返回,是FactoryBean,返回他的getObject |
再看下scope为Prototype的创建:
进入beforePrototypeCreation
:
1 | java复制代码protected void beforePrototypeCreation(String beanName) { |
向类型为ThreadLocal的prototypesCurrentlyInCreation(当前线程正在创建的Prototype的bean名字列表)去添加该bean的记录,防止循环依赖,表示这个bean正在创建中。
回到doGetBean,之后也调用了prototypeInstance = createBean(beanName, mbd, args)
,创建完之后将先前注册的正在创建中的Bean信息给抹除掉
1 | java复制代码afterPrototypeCreation(beanName); |
1 | java复制代码protected void afterPrototypeCreation(String beanName) { |
回到doGetBean,之后也会调用
1 | java复制代码bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); |
获取bean实例或者beanFactory创建的bean实例
最后在看其他的情况,
根据生命周期范围选择实例化bean的合适方法来创建bean实例,比如request就是确保每个请求生成一个实例。
doGetBean最后会做一个类型检查,校验通过后返回实例:
1 | java复制代码if (requiredType != null && !requiredType.isInstance(bean)) { |
本文转载自: 掘金