读完这篇文章你将会收获到

  • Spring 何时将 bean 加入到第三级缓存和第一级缓存中
  • Spring 何时回调各种 Aware 接口、BeanPostProcessorInitializingBean

相关文章

概述

上两篇文章 Spring 获取单例流程(一)Spring 获取单例流程(二) 介绍了 getBean 前面的流程,今天最后的收尾,把后面的流程继续一起学习下

源码分析

// 我依赖的大哥都好了
// Create bean instance.
if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
} catch (BeansException ex) {
// 从三级缓存中移除这个 beanName 因为它可能被放进去了 因为放进去三级缓存可以解决 setter 的循环依赖
destroySingleton(beanName);
throw ex;
}
}); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

如果我们要创建的 bean 是一个单例,

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {

   synchronized (this.singletonObjects) {
// 看看第一级缓存中有没有
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) { // 将 beanName 加入到 singletonsCurrentlyInCreation 中,代表它正在创建中
beforeSingletonCreation(beanName);
boolean newSingleton = false; try {
singletonObject = singletonFactory.getObject();
newSingleton = true;
} catch (IllegalStateException ex) {
throw ex;
} catch (BeanCreationException ex) { throw ex;
} finally {
// singletonsCurrentlyInCreation 从这里面移除掉
afterSingletonCreation(beanName);
}
if (newSingleton) {
// 加入缓存中
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}

删减了部分不重要的代码,我们大致来看看其流程

  1. 获取同步锁,然后判断第一级缓存是否已经存在这个 bean
  2. 如果不存在则将 beanName 加入到 singletonsCurrentlyInCreation 中,代表它正在创建中
  3. 然后调用参数的 ObjectFactorygetObject 方法获得一个 bean
  4. 最后将其从 singletonsCurrentlyInCreation 中移除、代表其已经创建完成了
  5. 最后将其加入到第一级缓存中、从第二级和第三级缓存中移除掉

全篇完结.终 !!!

其实真正的秘密藏身在参数的 ObjectFactory 中,从上面的流程中可以宏观的知道 Spring 创建 bean 的一个流程

现在我们在看看参数的 ObjectFactory 究竟干啥子了

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException { ...........
........... try {
// 真正 处理逻辑
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
} catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { throw ex;
} catch (Throwable ex) {
throw new BeanCreationException(xxxx);
}
}

干活的还是 do 开头的大佬

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException { // 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.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass(); ..........
.........
// 是否需要提前曝光、用来解决循环依赖的问题
// 是单例&允许循环依赖&正在创建中
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
// 为了避免后期循环依赖、可以在 bean 初始化前将创建实例的ObjectFactory 加入工厂
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
} // Initialize the bean instance.
Object exposedObject = bean;
try {
// 填充属性
populateBean(beanName, mbd, instanceWrapper);
// 调用初始方法
exposedObject = initializeBean(beanName, exposedObject, mbd);
} catch (Throwable ex) {
.........
} ........
....... return exposedObject;
}

上面的流程大致就是

  • createBeanInstance 这个方法根据你的配置以及你的 bean 的情况选择出一种创建 bean 的方法、可能是工厂方法、可能是某个构造函数、可能是默认的构造函数。这里包含了当一个构造函数的参数是另一个 bean 的时候、它会通过 getBean 的方法获取这个参数的 bean

  • 然后将创建好的 bean 加入到第三级缓存中,默认设置我们是允许循环依赖的

  • populateBean 方法就是我们填充属性了、如果你依赖的其他 Spring 的其他 bean 是通过这种方式注入的话(autowireByName autowireByType )、就是在这一步注入的了,他获取其他 bean 也是通过 getBean 的方式获取

    protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    .........
    ......... if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
    autowireByName(beanName, mbd, bw, newPvs);
    }
    // Add property values based on autowire by type if applicable.
    if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
    autowireByType(beanName, mbd, bw, newPvs);
    }
    .........
    .........
    }
  • initializeBean 则是调用我们的各种回调接口、Aware 类型的、BeanPostProcessorInitializingBean、自定义初始化函数

    protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
    if (System.getSecurityManager() != null) {
    AccessController.doPrivileged((PrivilegedAction<Object>) () -> {。
    // 调用各种 Aware 接口
    invokeAwareMethods(beanName, bean);
    return null;
    }, getAccessControlContext());
    } else {
    // 调用各种 Aware 接口
    invokeAwareMethods(beanName, bean);
    } Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
    // 调用 BeanPostProcessor postProcessBeforeInitialization
    wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    } try {
    // 调用 InitializingBean 、自定义的初始化方法
    invokeInitMethods(beanName, wrappedBean, mbd);
    } catch (Throwable ex) {
    throw new BeanCreationException(
    (mbd != null ? mbd.getResourceDescription() : null),
    beanName, "Invocation of init method failed", ex);
    }
    if (mbd == null || !mbd.isSynthetic()) {
    // 调用 BeanPostProcessor postProcessAfterInitialization
    wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }
    return wrappedBean;
    }

    其实整体的流程就差不多了

总结

  • 根据参数中的 name 找出对应的 beanName、无论这个 name 是别名或者是一个 factoryBeanbeanName
  • 查看缓存中是否包含这个 beanName 对象
    • 先从一级缓存 singletonObjects 中看看有没有
    • 然后从二级缓存 earlySingletonObjects
    • 都没有的话再从三级缓存 singletonFactories 中看看有没有
  • 如果缓存中有 bean、那么我们还是需要处理一下这个 bean
    • 如果 Spring 缓存中返回的 beanfactoryBean 、而用户也想要的是一个 beanFactory (参数 name 中的前缀是 & )、那么我们直接返回
    • 如果 Spring 缓存中返回的 bean 是普通的 bean、而用户也想要的是一个普通的 bean 、那么就直接返回
    • 如果 Spring 缓存中返回的 bean 是一个 factoryBean 、而用户想要的是一个普通的 bean 、那么我们就要从 factoryBean 中获取这个 bean
    • 而从 factoryBean 中获取这个 bean 的过程中、需要调用到前置处理、后置处理和我们常用的接口回调 BeanPostProcessor
  • 如果缓存中没有 bean 、则判断是否是 prototype 类型并且循环依赖
  • 如果没有则尝试能否在父容器中找到该 bean
  • 如果父容器也没有则获取该 beanName 对应的 beanDefinition 找出其依赖的 beanName
  • 判断该 beanName 与 依赖的 beanName 是否循环依赖、没有则注册其依赖关系并调用 getBean 方法去创建依赖的 beanName
  • beanName 加入到 singletonsCurrentlyInCreation
  • 根据指定 bean 使用对应的策略创建新的实例、如工厂方法、构造函数、创建一个不完整的 bean
  • 将创建好的 bean 加入到第三级缓存
  • 进行属性填充、进行各种接口回调
  • 最后将其从 singletonsCurrentlyInCreation 中移除、代表其已经创建完成了
  • 最后将其加入到第一级缓存中、从第二级和第三级缓存中移除掉
  • 返回 bean 给调用方

其实总体的流程还是不算复杂把、我们也可以从中收获到一些东西。其实我们最关心也是面试最常问的一个问题就是、Spring 如何解决循环依赖的问题、感兴趣的可以看看这篇文章公众号内的 Spring 循环依赖 这篇文章

Spring 获取单例流程(三)的更多相关文章

  1. Spring 获取单例流程(二)

    读完这篇文章你将会收获到 Spring 中 prototype 类型的 bean 如何做循环依赖检测 Spring 中 singleton 类型的 bean 如何做循环依赖检测 前言 继上一篇文章 S ...

  2. Spring 获取单例流程(一)

    读完这篇文章你将会收获到 在 getBean 方法中, Spring 处理别名以及 factoryBean 的 name Spring 如何从多级缓存中根据 beanName 获取 bean Spri ...

  3. Spring IOC 容器源码分析 - 获取单例 bean

    1. 简介 为了写 Spring IOC 容器源码分析系列的文章,我特地写了一篇 Spring IOC 容器的导读文章.在导读一文中,我介绍了 Spring 的一些特性以及阅读 Spring 源码的一 ...

  4. Spring源码分析(十五)获取单例

    本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 之前我们讲解了从缓存中获取单例的过程,那么,如果缓存中不存在已经加载的单例be ...

  5. 5.2:缓存中获取单例bean

    5.2  缓存中获取单例bean 介绍过FactoryBean的用法后,我们就可以了解bean加载的过程了.前面已经提到过,单例在Spring的同一个容器内只会被创建一次,后续再获取bean直接从单例 ...

  6. Spring源码分析(十三)缓存中获取单例bean

    摘要:本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 介绍过FactoryBean的用法后,我们就可以了解bean加载的过程了 ...

  7. 【转】Spring Bean单例与线程安全

    一.Spring单例模式及线程安全 Spring框架中的Bean,或者说组件,获取实例的时候都是默认单例模式,这是在多线程开发的时候需要尤其注意的地方. 单例模式的意思是只有一个实例,例如在Sprin ...

  8. Spring Bean单例与线程安全

    一.Spring单例模式及线程安全 Spring框架中的Bean,或者说组件,获取实例的时候都是默认单例模式,这是在多线程开发的时候需要尤其注意的地方. 单例模式的意思是只有一个实例,例如在Sprin ...

  9. Spring的单例实现原理-登记式单例

    单例模式有饿汉模式.懒汉模式.静态内部类.枚举等方式实现,但由于以上模式的构造方法是私有的,不可继承,Spring为实现单例类可继承,使用的是单例注册表的方式(登记式单例). 什么是单例注册表呢, 登 ...

随机推荐

  1. maven工程打胖瘦jar包插件

    <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <versio ...

  2. Java 第十一届 蓝桥杯 省模拟赛 梅花桩

    小明每天都要练功,练功中的重要一项是梅花桩. 小明练功的梅花桩排列成 n 行 m 列,相邻两行的距离为 1,相邻两列的距离也为 1. 小明站在第 1 行第 1 列上,他要走到第 n 行第 m 列上.小 ...

  3. Java实现 LeetCode 406 根据身高重建队列

    406. 根据身高重建队列 假设有打乱顺序的一群人站成一个队列. 每个人由一个整数对(h, k)表示,其中h是这个人的身高,k是排在这个人前面且身高大于或等于h的人数. 编写一个算法来重建这个队列. ...

  4. Java实现 LeetCode 292 Nim游戏

    292. Nim 游戏 你和你的朋友,两个人一起玩 Nim 游戏:桌子上有一堆石头,每次你们轮流拿掉 1 - 3 块石头. 拿掉最后一块石头的人就是获胜者.你作为先手. 你们是聪明人,每一步都是最优解 ...

  5. Java实现 蓝桥杯VIP 算法提高 质因数2

    算法提高 质因数2 时间限制:1.0s 内存限制:256.0MB 将一个正整数N(1<N<32768)分解质因数,把质因数按从小到大的顺序输出.最后输出质因数的个数. 输入格式 一行,一个 ...

  6. Java实现 蓝桥杯VIP 算法提高 change

    算法提高 change 时间限制:1.0s 内存限制:256.0MB 问题描述 数组A中共有n个元素,初始全为0.你可以对数组进行两种操作:1.将数组中的一个元素加1:2.将数组中所有元素乘2.求将数 ...

  7. Java实现 洛谷 P1000 超级玛丽游戏

    public class Main { public static void main(String[] args){ System.out.println(" ********" ...

  8. redis 分布式锁的简单使用

    RedisLock--让 Redis 分布式锁变得简单 目录 1. 项目介绍 2. 快速使用 2.1 引入 maven 坐标 2.2 注册 RedisLock 2.3 使用 3. 参与贡献 4. 联系 ...

  9. 彻底搞懂 etcd 系列文章(一):初识 etcd

    0 专辑概述 etcd 是云原生架构中重要的基础组件,由 CNCF 孵化托管.etcd 在微服务和 Kubernates 集群中不仅可以作为服务注册与发现,还可以作为 key-value 存储的中间件 ...

  10. iOS -iOS9中提示框(UIAlertController)的常见使用

    iOS 8 之前提示框主要使用 UIAlertView和UIActionSheet:iOS 9 将UIAlertView和UIActionSheet合二为一为:UIAlertController . ...