[死磕 Spring26/43] --- IOC 之加载 bean:总结
Posted wei198621
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[死磕 Spring26/43] --- IOC 之加载 bean:总结相关的知识,希望对你有一定的参考价值。
引用原文:
[死磕 Spring26/43] — IOC 之加载 bean:总结
https://www.cmsblogs.com/article/1391375416345890816
正文
在 【死磕 Spring】 Spring bean 解析篇深入分析了一个配置文件经历了哪些过程转变成了 BeanDefinition,但是这个 BeanDefinition 并不是我们真正想要的想要的 bean,因为它还仅仅只是承载了我们需要的目标 bean 的信息,从 BeanDefinition 到我们需要的目标还需要一个漫长的 bean 的初始化阶段,在 【死磕 Spring】 Spring bean 加载阶段已经详细分析了初始化 bean 的过程,所以这里做一个概括性的总结。 bean 的初始化节点由第一次调用 getBean()(显式或者隐式)开启,所以我们从这个方法开始。
public Object getBean(String name) throws BeansException
return doGetBean(name, null, null, false);
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException
// 获取 beanName,这里是一个转换动作,将 name 转换Wie beanName
final String beanName = transformedBeanName(name);
Object bean;
// 从缓存中或者实例工厂中获取 bean
// *** 这里会涉及到解决循环依赖 bean 的问题
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null)
if (logger.isDebugEnabled())
if (isSingletonCurrentlyInCreation(beanName))
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
else
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
else
// 因为 Spring 只解决单例模式下得循环依赖,在原型模式下如果存在循环依赖则会抛出异常
// **关于循环依赖后续会单独出文详细说明**
if (isPrototypeCurrentlyInCreation(beanName))
throw new BeanCurrentlyInCreationException(beanName);
// 如果容器中没有找到,则从父类容器中加载
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName))
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory)
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
else if (args != null)
return (T) parentBeanFactory.getBean(nameToLookup, args);
else
return parentBeanFactory.getBean(nameToLookup, requiredType);
// 如果不是仅仅做类型检查则是创建bean,这里需要记录
if (!typeCheckOnly)
markBeanAsCreated(beanName);
try
// 从容器中获取 beanName 相应的 GenericBeanDefinition,并将其转换为 RootBeanDefinition
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// 检查给定的合并的 BeanDefinition
checkMergedBeanDefinition(mbd, beanName, args);
// 处理所依赖的 bean
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null)
for (String dep : dependsOn)
// 若给定的依赖 bean 已经注册为依赖给定的b ean
// 循环依赖的情况
if (isDependent(beanName, dep))
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
// 缓存依赖调用
registerDependentBean(dep, beanName);
try
getBean(dep);
catch (NoSuchBeanDefinitionException ex)
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
// bean 实例化
// 单例模式
if (mbd.isSingleton())
sharedInstance = getSingleton(beanName, () ->
try
return createBean(beanName, mbd, args);
catch (BeansException ex)
// 显示从单利缓存中删除 bean 实例
// 因为单例模式下为了解决循环依赖,可能他已经存在了,所以销毁它
destroySingleton(beanName);
throw ex;
);
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
// 原型模式
else if (mbd.isPrototype())
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
finally
afterPrototypeCreation(beanName);
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
else
// 从指定的 scope 下创建 bean
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null)
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
try
Object scopedInstance = scope.get(beanName, () ->
beforePrototypeCreation(beanName);
try
return createBean(beanName, mbd, args);
finally
afterPrototypeCreation(beanName);
);
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
catch (IllegalStateException ex)
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; consider " +
"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
catch (BeansException ex)
cleanupAfterBeanCreationFailure(beanName);
throw ex;
// 检查需要的类型是否符合 bean 的实际类型
if (requiredType != null && !requiredType.isInstance(bean))
try
T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
if (convertedBean == null)
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
return convertedBean;
catch (TypeMismatchException ex)
if (logger.isDebugEnabled())
logger.debug("Failed to convert bean '" + name + "' to required type '" +
ClassUtils.getQualifiedName(requiredType) + "'", ex);
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
return (T) bean;
内部调用 doGetBean() 方法,doGetBean() 的代码量比较多,从这里就可以看出 bean 的加载过程是一个非常复杂的过程,会涉及到各种各样的情况处理。doGetBean() 可以分为以下几个过程。
- 转换 beanName。因为我们调用 getBean() 方法传入的 name 并不一定就是 beanName,可以传入 aliasName,FactoryBean,所以这里需要进行简单的转换过程。
- 尝试从缓存中加载单例 bean。
- bean 的实例化。
- 原型模式的依赖检查。因为 Spring 只会解决单例模式的循环依赖,对于原型模式的循环依赖都是直接抛出 BeanCurrentlyInCreationException 异常。
- 尝试从 parentBeanFactory 获取 bean 实例。如果 parentBeanFactory != null && !containsBeanDefinition(beanName) 则尝试从 parentBeanFactory 中获取 bean 实例对象,因为 !containsBeanDefinition(beanName) 就意味着定义的 xml 文件中没有 beanName 相应的配置,这个时候就只能从 parentBeanFactory 中获取。
- 获取 RootBeanDefinition,并对其进行合并检查。从缓存中获取已经解析的 RootBeanDefinition,同时如果父类不为 null 的话,则会合并父类的属性。
- 依赖检查。某个 bean 依赖其他 bean ,则需要先加载依赖的 bean。
- 对不同的 scope 进行处理。
- 类型转换处理。如果传递的 requiredType 不为 null,则需要检测所得到 bean 的类型是否与该 requiredType 一致,如果不一致则尝试转换,当然也要能够转换成功,否则抛出 BeanNotOfRequiredTypeException 异常。
下面就以下几个方面进行阐述,说明 Spring bean 的加载过程。
- 从缓存中获取 bean
- 创建 bean 实例对象
- 从 bean 实例中获取对象
从缓存中获取 bean
Spring 中根据 scope 可以将 bean 分为以下几类:singleton、prototype 和 其他,这样分的原因在于 Spring 在对不同 scope 处理的时候是这么处理的。
- singleton:在 Spring 的 IoC 容器中只存在一个对象实例,所有该对象的引用都共享这个实例。Spring - 容器只会创建该 bean 定义的唯一实例,这个实例会被保存到缓存中,并且对该bean的所有后续请求和引用- 都将返回该缓存中的对象实例。
- prototype:每次对该bean的请求都会创建一个新的实例
- 其他:其他包括 request、session、global session:
-
- request:每次 http 请求将会有各自的 bean 实例。
-
- session:在一个 http session 中,一个 bean 定义对应一个 bean 实例。
-
- global session:在一个全局的 http session 中,一个 bean 定义对应一个 bean 实例。
所以从缓存中获取的 bean 一定是 singleton bean,这也是 Spring 为何只解决 singleton bean 的循环依赖。调用 getSingleton() 从缓存中获取 singleton bean。
public Object getSingleton(String beanName)
return getSingleton(beanName, true);
protected Object getSingleton(String beanName, boolean allowEarlyReference)
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName))
synchronized (this.singletonObjects)
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference)
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null)
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
return singletonObject;
getSingleton() 就是从 singletonObjects、earlySingletonObjects、 singletonFactories 三个缓存中获取,这里也是 Spring 解决 bean 循环依赖的关键之处。详细内容请查看如下内容:
- 【死磕 Spring】----- IOC 之加载 bean:从单例缓存中获取单例 bean
- 【死磕 Spring】----- IOC 之加载 bean:创建 bean(五)
创建 bean 实例对象
如果缓存中没有,也没有 parentBeanFactory ,则会调用 createBean() 创建 bean 实例,该方法主要是在处理不同 scope 的 bean 的时候进行调用。
protected abstract Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException
该方法是定义在 AbstractBeanFactory 中的虚拟方法,其含义是根据给定的 BeanDefinition 和 args实例化一个 bean 对象,如果该 BeanDefinition 存在父类,则该 BeanDefinition 已经合并了父类的属性。所有 Bean 实例的创建都会委托给该方法实现。 方法接受三个参数:
- beanName:bean 的名字
- mbd:已经合并了父类属性的(如果有的话)BeanDefinition
- args:用于构造函数或者工厂方法创建 bean 实例对象的参数
该抽象方法的默认实现是在类 AbstractAutowireCapableBeanFactory 中实现,该方法其实只是做一些检查和验证工作,真正的初始化工作是由 doCreateBean() 实现,如下:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException
// BeanWrapper是对Bean的包装,其接口中所定义的功能很简单包括设置获取被包装的对象,获取被包装bean的属性描述器
BeanWrapper instanceWrapper = null;
// 单例模型,则从未完成的 FactoryBean 缓存中删除
if (mbd.isSingleton()) anceWrapper = this.factoryBeanInstanceCache.remove(beanName);
// 使用合适的实例化策略来创建新的实例:工厂方法、构造函数自动注入、简单初始化
if (instanceWrapper == null)
instanceWrapper = createBeanInstance(beanName, mbd, args);
// 包装的实例对象
final Object bean = instanceWrapper.getWrappedInstance();
// 包装的实例对象的类型
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class)
mbd.resolvedTargetType = beanType;
// 检测是否有后置处理
// 如果有后置处理,则允许后置处理修改 BeanDefinition
synchronized (mbd.postProcessingLock)
if (!mbd.postProcessed)
try
// applyMergedBeanDefinitionPostProcessors
// 后置处理修改 BeanDefinition
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
catch (Throwable ex)
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
mbd.postProcessed = true;
// 解决单例模式的循环依赖
// 单例模式 & 运行循环依赖&当前单例 bean 是否正在被创建
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure)
if (logger.isDebugEnabled())
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
// 提前将创建的 bean 实例加入到ectFactory 中
// 这里是为了后期避免循环依赖
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
/*
* 开始初始化 bean 实例对象
*/
Object exposedObject = bean;
try
// 对 bean 进行填充,将各个属性值注入,其中,可能存在依赖于其他 bean 的属性
// 则会递归初始依赖 bean
populateBean(beanName, mbd, instanceWrapper);
// 调用初始化方法
exposedObject = initializeBean(beanName, exposedObject, mbd);
catch (Throwable ex)
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName()))
throw (BeanCreationException) ex;
else
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
/**
* 循环依赖处理
*/
if (earlySingletonExposure)
// 获取 earlySingletonReference
Object earlySingletonReference = getSingleton(beanName, false);
// 只有在存在循环依赖的情况下,earlySingletonReference 才不会为空
if (earlySingletonReference != null)
// 如果 exposedObject 没有在初始化方法中被改变,也就是没有被增强
if (exposedObject == bean)
exposedObject = earlySingletonReference;
// 处理依赖
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName))
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans)
if (!removeSingletonIfCreatedForTypeCheckOnly以上是关于[死磕 Spring26/43] --- IOC 之加载 bean:总结的主要内容,如果未能解决你的问题,请参考以下文章
2死磕 Spring——IoC之深入理解Spring IoC
[死磕 Spring 4/43] --- IOC 之 获取验证模型
[死磕 Spring 15/43] --- IOC 之 IOC 初始化总结
[死磕 Spring 01/43 ] 号外02 通俗解释一下Spring的IOC原理