上文中我们学习了bean加载的整个过程,我们知道从spring容器中获取单例bean时会先从缓存尝试获取,如果缓存中不存在已经加载的单例bean就需要从头开始bean的创建,而bean的创建过程是非常复杂的,本文就开始研究bean加载这部分的源码。

1. bean创建流程分析

  在Spring中bean加载的逻辑是在getSingleton的重载方法中实现的:

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "'beanName' must not be null");
// 全局变量需要同步
synchronized (this.singletonObjects) {
// 首先检查对应的bean是否已经加载过,因为singleton模式就是复用已创建的bean,所以这一步是必须的
Object singletonObject = this.singletonObjects.get(beanName);
// 如果为空才可以进行singleton的bean的初始化
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while the singletons of this factory are in destruction " +
"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
if (logger.isDebugEnabled()) {
logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
beforeSingletonCreation(beanName);
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<Exception>();
}
try {
// 初始化bean
singletonObject = singletonFactory.getObject();
}
catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}
throw ex;
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
// 加入缓存
addSingleton(beanName, singletonObject);
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
}

  这里创建bean使用了回调方法(其实是匿名内部类),真正获取单例bean的方法其实现逻辑是在ObjectFactory类型的实例singletonFactory的getObject()方法中实现的。Spring在创建单例前后还有一些准备及处理操作,包括如下内容:

  • 检查缓存是否已经加载过;
  • 若没有加载,则记录beanName为正在加载状态;
  • 加载单例前记录加载状态;
  • 通过调用参数传入的ObjectFactory的个体Object方法实例化bean;
  • 加载单例后的处理方法调用;
  • 将结果记录至缓存并删除加载bean过程中所记录的各种辅助状态;
  • 返回处理结果;

  概括起来主要有下面几方面:

1.1 加载单例前后处理加载状态

  在beforeSingletonCreation()方法中有一个很重要的操作:记录加载状态,也就是通过this.singletonsCurrentlyInCreation.add(beanName)将当前正要创建的bean记录在缓存中,这样便可以对循环依赖进行检测。

protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.containsKey(beanName) &&
this.singletonsCurrentlyInCreation.put(beanName, Boolean.TRUE) != null) {
throw new BeanCurrentlyInCreationException(beanName);
}
}

  同记录加载状态相似,当bean加载结束后需要移除缓存中记录的该bean的加载状态记录:

protected void afterSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.containsKey(beanName) &&
!this.singletonsCurrentlyInCreation.remove(beanName)) {
throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
}
}

1.2 记录加载结果

  将结果记录至缓存并删除加载bean过程中所记录的各种辅助状态:

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);
}
}

1.3 返回处理结果

  虽然前面已经分析了加载bean的逻辑架构,但现在并没有开始对bean加载功能的探索,前面提到过,bean加载逻辑其实是在匿名内部类ObjectFactory的getObject()方法中定义的:

sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
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;
}
}
});

  ObjectFactory的核心部分其实只是调用了createBean()方法,也就是创建的bean的逻辑都在这里面,所以我们还需继续寻找真理。

2. 准备创建bean

  跟踪了这么多Spring代码,也发现了一些规律:一个真正干活的函数其实是以do开头的,比如doGetObjectFromFactoryBean(),而容易给我们带来错觉的函数,比如getObjectFromFactoryBean(),其实只是从全局角度做了一些统筹工作。这个规则对于createBean()也不例外,我们就来看一下其中做了哪些准备工作:

protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
if (logger.isDebugEnabled()) {
logger.debug("Creating instance of bean '" + beanName + "'");
}
// 根据设置的class属性或者根据className来解析Class
resolveBeanClass(mbd, beanName); // 验证及准备覆盖的方法
try {
mbd.prepareMethodOverrides();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(mbd.getResourceDescription(),
beanName, "Validation of method overrides failed", ex);
} try {
// 给BeanPostProcessors一个机会返回代理的机会来替代真正的实例
Object bean = resolveBeforeInstantiation(beanName, mbd);
if (bean != null) {
return bean;
}
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"BeanPostProcessor before instantiation of bean failed", ex);
} Object beanInstance = doCreateBean(beanName, mbd, args);
if (logger.isDebugEnabled()) {
logger.debug("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}

  总结一下具体步骤:

  • 根据设置的class属性或者根据className来解析Class;
  • 对override属性进行标记及验证;
  • 应用初始化前的后处理器,解析指定bean是否存在初始化前的短路操作;
  • 创建bean;

  在Spring的配置里面是没有诸如override-method之类的配置,那为什么还需要有override属性进行标记及验证这一步呢?这是因为在Spring配置中存在lookup-method和replace-method的,而这两个配置的加载其实就是将配置统一存放在BeadDefinition中的methodOverrides属性里,而这步操作就是针对这两个配置的。

  来看一下这几个主要的步骤:

2.1 处理override属性

  这部分的逻辑是在AbstractBeanDefinition类的prepareMethodOverrides方法中:

public void prepareMethodOverrides() throws BeanDefinitionValidationException {
// Check that lookup methods exists.
MethodOverrides methodOverrides = getMethodOverrides();
if (!methodOverrides.isEmpty()) {
for (MethodOverride mo : methodOverrides.getOverrides()) {
prepareMethodOverride(mo);
}
}
} protected void prepareMethodOverride(MethodOverride mo) throws BeanDefinitionValidationException {
// 获取对应类中对应方法名的个数
int count = ClassUtils.getMethodCountForName(getBeanClass(), mo.getMethodName());
if (count == 0) {
throw new BeanDefinitionValidationException(
"Invalid method override: no method with name '" + mo.getMethodName() +
"' on class [" + getBeanClassName() + "]");
}
else if (count == 1) {
// 标记MethodOverride暂未被覆盖,避免参数类型检查的开销
mo.setOverloaded(false);
}
}

  在Spring配置中存在lookup-method和replace-method两个配置功能,而这两个配置的加载其实就是将配置统一存放在BeanDefinition中的methodOverrides属性里,这两个功能实现原理其实是在bean实例化的时候如果检测到存在methodOverrides属性,会动态地为当前bean生成代理并使用对应的拦截器为bean做增强处理,这部分在创建bean部分会做详细解析。

  但是这里要提到的是,对于方法的匹配来讲,如果一个类中存在若干个重载方法,那么,在函数调用及增强的时候还需要根据参数类型进行匹配,来最终确认当前调用的到底是哪个函数。但是,Spring将一部分匹配工作在这里完成了,如果当前类中的方法只有一个,那么就设置该方法没有被重载,这样在后续调用的时候便可以直接使用找到的方法,而不需要进行方法的参数匹配验证了,而且还可以提前对方法存在性进行验证,正可谓一石二鸟,这部分需要结合后面的逻辑来理解,现在不理解可以先忽略。

2.2 实例化的前置处理

  在真正调用doCreate方法创建bean实例前使用了方法resolveBeforeInstantiation()对BeanDefinition中的属性做一些前置处理。这里无论其中是否有相应的逻辑实现,我们都可以理解,因为真正逻辑实现前后留有处理函数也是可扩展的一种体现。   

    protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
Object bean = null;
if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
// Make sure bean class is actually resolved at this point.
if (mbd.hasBeanClass() && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
bean = applyBeanPostProcessorsBeforeInstantiation(mbd.getBeanClass(), beanName);
if (bean != null) {
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
}
mbd.beforeInstantiationResolved = (bean != null);
}
return bean;
}

  这里最重要的无疑是两个方法applyBeanPostProcessorsBeforeInstantiation以及applyBeanPostProcessorsAfterInitialization,其实现非常简单,无非是对后处理器中所有InstantiationAwareBeanPostProcessor类型的后处理器进行postProcessBeforeInstantiation方法和BeanPostProcessor的postProcessAfterInitialization方法的调用。

实例化前的后处理器应用

  bean的实例化前调用,也就是将AbstractBeanDefinition转换为BeanWrapper前的处理,这相当于给子类一个修改BeanDefinition的机会,也就是说当程序经过这个方法之后,bean可能已经不是我们认为的bean了,有可能是一个经过处理的代理bean,可能是通过cglib生成也可能是通过其他技术生成的,这个在后面涉及到AOP时会讲到,现在我们只需要知道,在bean的实例化前会调用后处理器的方法进行处理:

protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName)
throws BeansException { for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
if (result != null) {
return result;
}
}
}
return null;
}

实例化后的后处理器应用

  Spring中的规则是在bean初始化后尽可能保证将注册的后处理器的postProcessAfterInitialization方法应用到该bean中,因为如果返回的bean不为空,那么便不会再次经历普通bean的创建过程,所以只能在这里应用后处理器的postProcessAfterInitialization方法。

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException { Object result = existingBean;
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
result = beanProcessor.postProcessAfterInitialization(result, beanName);
if (result == null) {
return result;
}
}
return result;
}

  这里还有一点很重要,在该函数中还有一个短路判断,这个很关键:

if(bean != null) {
return bean;
}

  当经过前置处理后返回的结果如果不为空,那么会直接略过后续的Bean创建而直接返回,这个地方很容易被忽视,但是却起着至关重要的作用,我们熟知的AOP功能就是基于这里的判断的,后面关于AOP的文章中也会涉及到。

3. bean创建

  当经历过resolveBeforeInstantiation()方法后,如果创建了代理或者重写了InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation()方法并在方法postProcessBeforeInstantiation()中改变了bean,则直接返回就可以了,否则就需要进行常规bean的创建,这是在doCreateBean中完成的:

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 根据指定bean使用对应的策略创建新的实例,如:工厂方法、构造函数自动注入、简单初始化
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null); // Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
// 应用MergedBeanDefinitionPostProcessor
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
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初始化完成前将创建实例的ObjectFactory加入工厂
addSingletonFactory(beanName, new ObjectFactory<Object>() {
public Object getObject() throws BeansException {
// 对bean再一次依赖引用,主要应用SmartInstantiationAwareBeanPostProcessor
// 其中我们熟知的AOP就是在这里将advice动态织入bean中,若没有则直接返回bean,不做任何处理
return getEarlyBeanReference(beanName, mbd, bean);
}
});
} // Initialize the bean instance.
Object exposedObject = bean;
try {
// 对bean进行填充,将各个属性值注入,其中,可能存在依赖于其他bean的属性,则会递归初始依赖bean
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
// 调用初始化方法,比如init-method
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) {
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<String>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
// 检测依赖
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
// 因为bean创建后其所依赖的bean一定是已经创建的,
// actualDependentBeans不为空则表示当前bean创建后其依赖的bean却没有全部
// 创建完,也就是说存在循环依赖
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
} // Register bean as disposable.
try {
// 根据scope注册bean
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
} return exposedObject;
}

  我们这里只简单看看整个函数的概要思路,各个部分详细逻辑留待后文分析:

1. 如果是单例则需要首先清除缓存。

2. 创建bean实例,即将BeanDefinition转换为BeanWrapper。

  转换是一个复杂的过程,但是我们还是可以尝试概括大致的功能,如下所示。

  • 如果存在工厂方法则使用工厂方法进行初始化;
  • 一个类有多个构造函数,每个构造函数都有不同的参数,所以需要根据参数锁定构造函数并进行初始化;
  • 如果及存在工厂方法也不存在带有参数的构造函数,则使用默认的构造函数进行bean的实例化;

3. MergedBeanDefinitionPostProcessor的应用。

  bean合并后的处理,Autowired注解正是通过此方法实现诸如类型的解析。

4. 依赖处理

  在Spring会有循环依赖的情况,例如,当A中含有B的属性,而B中又含有A的属性时就会构成一个循环依赖,此时如果A和B都是单例,那么在Spring中的处理方式就是当创建B的时候,涉及自动注入A的步骤时,并不是直接去再次创建A,而是通过放入缓存中的ObjectFactory来创建实例,这样就解决了循环依赖问题。

5. 属性填充。将所有的属性填充至bean的实例中。

6. 循环依赖检查。

  Spring中只在单例下才会解决循环依赖,而对于prototype的bean,Spring没有好的解决办法,唯一要做的就是抛出异常。在这个步骤里面会检测已经加载的bean是否已经出现了依赖循环,并判断是否需要抛出异常。

7. 注册DisposableBean

  如果配置了destroy-method,这里需要注册以便于在销毁时候调用。

8. 完成创建并返回。

4. 总结

  本文先从全局角度分析了bean创建的整个流程,然后着重分析了Spring在bean创建之前所做的一些准备工作,包括override属性处理、实例化前后对后处理器的应用,这些都只是一些全局性的统筹工作,之后又看了一下bean创建的实际过程,后面就要开始详细分析bean的创建过程了,这个才是真正爬坡的开始。

spring源码阅读笔记06:bean加载之准备创建bean的更多相关文章

  1. 【Spring源码分析】非懒加载的单例Bean初始化过程(下篇)

    doCreateBean方法 上文[Spring源码分析]非懒加载的单例Bean初始化过程(上篇),分析了单例的Bean初始化流程,并跟踪代码进入了主流程,看到了Bean是如何被实例化出来的.先贴一下 ...

  2. 【Spring源码分析】非懒加载的单例Bean初始化前后的一些操作

    前言 之前两篇文章[Spring源码分析]非懒加载的单例Bean初始化过程(上篇)和[Spring源码分析]非懒加载的单例Bean初始化过程(下篇)比较详细地分析了非懒加载的单例Bean的初始化过程, ...

  3. Spring源码分析:非懒加载的单例Bean初始化前后的一些操作

    之前两篇文章Spring源码分析:非懒加载的单例Bean初始化过程(上)和Spring源码分析:非懒加载的单例Bean初始化过程(下)比较详细地分析了非懒加载的单例Bean的初始化过程,整个流程始于A ...

  4. Spring源码分析:非懒加载的单例Bean初始化过程(下)

    上文Spring源码分析:非懒加载的单例Bean初始化过程(上),分析了单例的Bean初始化流程,并跟踪代码进入了主流程,看到了Bean是如何被实例化出来的.先贴一下AbstractAutowireC ...

  5. 【Spring源码分析】非懒加载的单例Bean初始化过程(上篇)

    代码入口 上文[Spring源码分析]Bean加载流程概览,比较详细地分析了Spring上下文加载的代码入口,并且在AbstractApplicationContext的refresh方法中,点出了f ...

  6. Spring源码分析:非懒加载的单例Bean初始化过程(上)

    上文[Spring源码分析]Bean加载流程概览,比较详细地分析了Spring上下文加载的代码入口,并且在AbstractApplicationContext的refresh方法中,点出了finish ...

  7. Spring源码阅读 之 配置的加载(希望有喜欢源码的朋友一起交流)

    想写Spring的源码方面的东西想了好久了,之前花了一段时间学习了SpringCloud,现在总算对SpringCloud有了一个大概的了解,从今天开始好好读一篇Spring的源码,结合书本跟网上的一 ...

  8. Spring源码剖析4:懒加载的单例Bean获取过程分析

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

  9. Spring源码阅读笔记02:IOC基本概念

    上篇文章中我们介绍了准备Spring源码阅读环境的两种姿势,接下来,我们就要开始探寻这个著名框架背后的原理.Spring提供的最基本最底层的功能是bean容器,这其实是对IoC思想的应用,在学习Spr ...

随机推荐

  1. Vue组件传递数据

    组件命名 1.字母全小写且必须包含一个连字符 my-componnect 2.使用 kebab-case(短横线分隔命名) 定义一个组件时,你也必须在引用这个自定义元素时使用 kebab-case,例 ...

  2. Vue-API之全局配置

    API 全局配置 Vue.config 是一个对象,包含 Vue 的全局配置. 源码位置:util/config.js 搜索config 可以找到其源码地址,其中声明了config的类型和默认参数 下 ...

  3. this软拷贝详解

    <script> if( !Function.prototype.softBind ){ Function.prototype.softBind = function( obj ){ va ...

  4. javascript输入用户名,在服务器中显示出了

    ## 第一个文件 pageEncoding="UTF-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Tra ...

  5. 负载均衡框架 ribbon 三

    Ribbon 在 SpringCloud 中的使用 1.构建 Eureka 注册中心 smart-platform-eureka1 (1)导入jar包 <properties> <p ...

  6. 必备技能五、router路由钩子

    在路由跳转的时候,我们需要一些权限判断或者其他操作.这个时候就需要使用路由的钩子函数. 定义:路由钩子主要是给使用者在路由发生变化时进行一些特殊的处理而定义的函数. 总体来讲vue里面提供了三大类钩子 ...

  7. Android开发进阶 -- 通用适配器 CommonAdapter

    在Android开发中,我们经常会用到ListView 这个组件,为了将ListView 的内容展示出来,我们会去实现一个Adapter来适配,将Layout中的布局以列表的形式展现到组件中.     ...

  8. 2653 区间xor

    前言 这个题目在我之前那篇c++位运算的的随笔中提到过. 有兴趣的话去看看吧! 飞机场:https://www.cnblogs.com/laoguantongxiegogofs/p/12444517. ...

  9. Fortify Audit Workbench 笔记 Header Manipulation

    Header Manipulation Abstract HTTP 响应头文件中包含未验证的数据会引发 cache-poisoning. cross-site scripting. cross-use ...

  10. Django 图片上传到数据库 并调用显示

    环境:Django2.0 Python3.6.4 建立项目,数据库设置,就不说了. 直接上代码: 在models.py中,需要建立模型,这里使用了ImageField字段,用来存储图片路径,这个字段继 ...