Spring bean是如何加载的

加载bean的主要逻辑

在AbstractBeanFactory中doGetBean对加载bean的不同情况进行拆分处理,并做了部分准备工作

具体如下

  1. 获取原始bean name

    1. 根据alia获取原始bean name
    2. 去除FactoryBean时的& [如果是需要获取FactoryBean自省,配置时需要在bean name前添加&]
  2. 尝试从缓存中获取实例
    1. 如果获取到实例,还要委托getObjectForBeanInstance解决FactoryBean的场景,就是调用getObject
  3. 判断原型场景的循环依赖问题,如果是原型同时bean又正在创建,说明是循环依赖,那直接抛异常,spring不尝试解决原型的循环依赖
  4. 如果在本容器中没有定义该bean,需要去父容器查找
    • 如果有参数,结合参数初始化
    • 如果没有参数,需要结合类型初始化,这边的调用是这个分支(当然这边一样没有类型)
  5. 如果不是类型检查,这边需要标记bean正在实例化
  6. bean实例化的准备工作
    1. 合并父bean的定义,并转化GenericBeanDefinition为RootBeanDefinition
    2. 校验BeanDefinition,如果是抽象类或者非原型带参数抛异常[这边注释说的是只有原型才可以配置构造方法的参数]
    3. 解决bean的依赖
      • 注册依赖的bean
      • 递归调用getBean实例化依赖bean
  7. 创建单例的实例
    • 为解决循环依赖问题,这边使用ObjectFactory在实例化前先暴露bean
    • 老规矩,需要委托getObejctForBeanInstance解决FactoryBean的问题
  8. 创建原型实例
    • 创建前的准备工作,使用prototypesCurrentlyInCreation标记bean正在实例化
    • 委托createBean实例化bean
    • 创建后的善后工作,从prototypesCurrentlyInCreation中删除标记
    • 老规矩,委托getObjectForBeanInstance解决工厂方法的问题
  9. 创建其他scope的实例,这边的逻辑结合了单例跟原型的处理逻辑,即使用解决循环依赖的ObjectFactory也使用prototypeCreation的标记
    • 获取作用域scope,并校验是否已配置
    • 使用ObjectFactory提早暴露实例
    • 标记bean正在创建并委托createBean实例化
    • 又是委托getObjectForBeanInstance解决工厂方法问题
  10. 最后需要对创建的实例进行类型校验,如果不一致,这边还需要委托TypeConverter进行类型装换

AbstractBeanFactory

@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException { // 获取原始的bean name,去除&,解决alias问题
final String beanName = transformedBeanName(name);
Object bean; // 尝试从缓存中获取bean
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isDebugEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
// ...
}
else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
// 如果从缓存中或得bean,还需要判断是否是FactoryBean,并调用getObejct
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
} else {
// 如果是原型scope,这边又是正在创建,说明有循环依赖,而原型的循环依赖Spring是不解决的
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
} // 如果当前容器没有配置bean,那么去父容器查找
// Check if bean definition exists in this factory.
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (args != null) {
// Delegation to parent with explicit args.
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
} // 如果不是类型检查,这边需要标记类正在创建
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
} try {
// 实例化类之前,先去容器中获取配置的bean信息,这边需要将之前的GenericBeanDefinition转化为RootBeanDefinition
// 同时如果父bean的话,需要合并到子bean
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args); // Guarantee initialization of beans that the current bean depends on.
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dependsOnBean : dependsOn) {
if (isDependent(beanName, dependsOnBean)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dependsOnBean + "'");
}
// 解决依赖
registerDependentBean(dependsOnBean, beanName);
getBean(dependsOnBean);
}
} // 创建单例的实例
// Create bean instance
if (mbd.isSingleton()) {
// 单例情况下,为解决循环依赖,在实例化之前,先新建一个ObjectFactory实例
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the 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);
} // 创建其他scope的实例
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
}
try {
// 还是先创建ObejctFactory,只是这边没有处理
Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
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;
}
} // 这边需要对实例进行类型校验,如果与requiredType不一致,需要委托TypeConverter尝试类型转换
// Check if required type matches the type of the actual bean instance.
if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
try {
return getTypeConverter().convertIfNecessary(bean, requiredType);
}
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;
}

bean实例的缓存分析

上面提到在加载bean的时候,doGetBean首先尝试的是从缓存读取,这边我们来细细分析下缓存具体是如何处理的.

这边逻辑是定义在DefaultSingletonBeanRegistry中,它是AbstractBeanFactory的父类,主要职责是共享实例的注册.

这边虽然定义的是singleton,但是实际使用的时候,处理prototype,其他scope均使用了这边进行缓存.

这边主要是需要理解singletonObjects,singletonFactories,earlySingletonObjects,registeredSingletons这4个变量.

singletonObjects 缓存bean name ->实例###

Cache of singleton objects: bean name --> bean instance

这边缓存的是实例

singletonFactories 缓存bean name -->ObjectFactory###

Cache of singleton factories: bean name --> ObjectFactory

这边缓存的是为解决循环依赖而准备的ObjectFactory

earlySingletonObjects 缓存提早暴露的实例 bean name -->bean instance###

Cache of early singleton objects: bean name --> bean instance

这边缓存的也是实例,只是这边的是为解决循环依赖而提早暴露出来的实例,其实是ObjectFactory

registeredSingletons 已经注册的单例bean name###

Set of registered singletons, containing the bean names in registration order

上面三个变量,任意一个添加了,这边都会添加bean name,标记已经注册

4个变量的关系如下:###

  • singletonObjects与singletonFactories,earlySingletonObjects,是互斥的.就是一个bean如果在其中任意一个变量中就,不会存在在另一变量中.这三个变量用于记录一个bean的不同状态.
  • 如果bean已经添加到singletonObjects中,那么singltonFactories和earlySinletonObjects都不会考虑
  • singltonFactories中的bean 通过 ObjectFactory的getObject实例化后,添加到earlySingletonObjects

我们从下面几个方法,可以清楚看懂上面4个变量的使用:

DefaultSingletonBeanRegistry

/**
* 添加实例化的bean
* Add the given singleton object to the singleton cache of this factory.
* <p>To be called for eager registration of singletons.
* @param beanName the name of the bean
* @param singletonObject the singleton object
*/
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
} /**
* 为解决单例的循环依赖,这边注册ObjectFactory
* Add the given singleton factory for building the specified singleton
* if necessary.
* <p>To be called for eager registration of singletons, e.g. to be able to
* resolve circular references.
* @param beanName the name of the bean
* @param singletonFactory the factory for the singleton object
*/
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
/**
* 清除实例
* Remove the bean with the given name from the singleton cache of this factory,
* to be able to clean up eager registration of a singleton if creation failed.
* @param beanName the name of the bean
* @see #getSingletonMutex()
*/
protected void removeSingleton(String beanName) {
synchronized (this.singletonObjects) {
this.singletonObjects.remove(beanName);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.remove(beanName);
}
}
/**
* 获取实例时,调用ObejctFactory的getObject 获取实例
* Return the (raw) singleton object registered under the given name.
* <p>Checks already instantiated singletons and also allows for an early
* reference to a currently created singleton (resolving a circular reference).
* @param beanName the name of the bean to look for
* @param allowEarlyReference whether early references should be created or not
* @return the registered singleton object, or {@code null} if none found
*/
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 != NULL_OBJECT ? singletonObject : null);
}

未完待续

Spring bean是如何加载的的更多相关文章

  1. spring bean的重新加载

    架构体系 在谈spring bean的重新加载前,首先我们来看看spring ioc容器. spring ioc容器主要功能是完成对bean的创建.依赖注入和管理等功能,而这些功能的实现是有下面几个组 ...

  2. Spring Boot 学习系列(09)—自定义Bean的顺序加载

    此文已由作者易国强授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. Bean 的顺序加载 有些场景中,我们希望编写的Bean能够按照指定的顺序进行加载.比如,有UserServ ...

  3. Spring XML Bean 定义的加载和注册

    前言 本篇文章主要介绍 Spring IoC 容器怎么加载 bean 的定义元信息. 下图是一个大致的流程图: 第一次画图,画的有点烂.

  4. interface21 - web - ContextLoaderListener(Spring Web Application Context加载流程)

    前言 最近打算花点时间好好看看spring的源码,然而现在Spring的源码经过迭代的版本太多了,比较庞大,看起来比较累,所以准备从最初的版本(interface21)开始入手,仅用于学习,理解其设计 ...

  5. Sspring bean被初始化加载2次

    Sspring bean被初始化加载2次 spring框架的web项目时,启动的时候发现某个bean被加载了两次,比如使用SchedulingConfigurer或者使用@PostConstruct的 ...

  6. SpringXML方式配置bean的懒加载lazy-init

    lazy-init(懒加载),表示该bean在容器初始化的时候不进行初始化. 例如: <bean name="role1" class="com.fz.entity ...

  7. Spring源码剖析2:Spring IOC容器的加载过程

    spring ioc 容器的加载流程 1.目标:熟练使用spring,并分析其源码,了解其中的思想.这篇主要介绍spring ioc 容器的加载 2.前提条件:会使用debug 3.源码分析方法:In ...

  8. Spring源码剖析3:Spring IOC容器的加载过程

    本文转自五月的仓颉 https://www.cnblogs.com/xrq730 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https ...

  9. 深入Spring之IOC之加载BeanDefinition

    本文主要分析 spring 中 BeanDefinition 的加载,对于其解析我们在后面的文章中专门分析. BeanDefinition 是属于 Spring Bean 模块的,它是对 spring ...

随机推荐

  1. tensorflow Process finished with exit code 137 (interrupted by signal 9: SIGKILL) 错误

    Process finished with exit code 137 (interrupted by signal 9: SIGKILL) 在使用tensorflow自带的数据集做手写数字识别的时候 ...

  2. unity 查找脚本被场景中哪些对象引用

    在需要查找的脚本上右键: 在场景中已经显示出所有引用该脚本的对象

  3. 软件工程导论复习 如何画系统流程图和数据流图 part2

    数据流图(DFD)    数据流图,简称DFD,是SA方法中用于表示系统逻辑模型的一种工具,它以图形的方式描绘数据在系统中流动和处理的过程,由于它只反映系统必须完成的逻辑功能,所以它是一种功能模型.下 ...

  4. centos7 opencc 安装

    繁体字转换:http://xh.5156edu.com/jtof.php 转换的有问题http://tool.lu/zhconvert/ git网址:https://github.com/BYVoid ...

  5. new operator

    [new operator] When the code new Foo(...) is executed, the following things happen: A new object is ...

  6. python没有switch,可以用字典来替代

    python没有switch,是因为可以用字典来替代,具体方法如下: def add(x,y): print(x+y)def subtraction(x,y): print(x-y)def multi ...

  7. Codeforces Round #535 (Div. 3)

    E: 题意: 给出n个整数ai和m个区间[li,ri] 你可以选择一些区间,并且将区间内的数字都减一.你要选择一些区间,然后使得改变后的数列中maxbi-minbi的值最大. 题解: 假设我们已经知道 ...

  8. Python学习day5作业

    目录 Python学习day5作业 ATM和购物商城 1. 程序说明 2. 基本流程图 3. 程序测试帐号 4. 程序结构: 5. 程序测试 title: Python学习day5作业 tags: p ...

  9. php Pthread 多线程 (四) 共享内存

    有些时候我们希望在多个线程中共享一些需要的数据,我们可以使用shmop扩展. <?php class Count extends Thread { private $name = ''; pub ...

  10. Redis数据持久化

    持久化选项 Redis提供了两种不同的持久化方法来将数据存储到硬盘里面.一种方法叫快照(snapshotting),它可以将存在于某一时刻的所有数据都写入硬盘里面.另一种方法叫只追加文件(append ...