createBean方法详解

2023-11-09

前言:

  createBean是创建Bean的主要方法

   该方法位于:AbstractBeanFactory的doGetBean方法中的createBean调用、

createBean方法流程图:

 createBean源码解析:

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
            throws BeanCreationException {

        if (logger.isTraceEnabled()) {
            logger.trace("Creating instance of bean '" + beanName + "'");
        }
        RootBeanDefinition mbdToUse = mbd;//该参数是从AbstractBeanFactory的doGetBean传递过来的父子容器合并BeanDefinition

        // Make sure bean class is actually resolved at this point, and
        // clone the bean definition in case of a dynamically resolved Class
        // which cannot be stored in the shared merged bean definition.
        //判断需要创建的Bean是否可以实例化,即是否可以通过当前的类加载器加载
        Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
        if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
            //克隆一份BeanDefinition,用来设置上加载出来的class对象
            //之所以后续用该副本操作,是因为不希望将解析的class绑定到缓存里的BeanDefinition
            //因为class有可能是每次都需要动态解析出来的
            mbdToUse = new RootBeanDefinition(mbd);
            mbdToUse.setBeanClass(resolvedClass);
        }

        // Prepare method overrides.
        //校验和准备Bean中的方法覆盖
        try {
            mbdToUse.prepareMethodOverrides();//如果是0个则抛异常,如果是1个也就没有重载的必要
        }
        catch (BeanDefinitionValidationException ex) {
            throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
                    beanName, "Validation of method overrides failed", ex);
        }

        try {
            // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
            //如果Bean配置了初始化前和初始化后的处理器,则试图返回一个需要创建Bean的代理对象
            //resolveBeforeInstantiation只是针对有自定义的targetsource,
            //因为自定义的targetsource不是spring的bena,那么肯定不需要进行后续的一系列的实例化,初始化。
            //所以可以在resolveBeforeInstantiation直接进行proxy
            Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
            if (bean != null) {
                //如果这里有处理器在bean创建的之前 给出了代理bean,则无需执行后续的逻辑,
                // 直接返回用户给出的bean
                return bean;
            }
        }
        catch (Throwable ex) {
            throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
                    "BeanPostProcessor before instantiation of bean failed", ex);
        }

        try {
            //创建Bean的入口
            Object beanInstance = doCreateBean(beanName, mbdToUse, args);
            if (logger.isTraceEnabled()) {
                logger.trace("Finished creating instance of bean '" + beanName + "'");
            }
            return beanInstance;
        }
        catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
            // A previously detected exception with proper bean creation context already,
            // or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanCreationException(
                    mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
        }
    }
createBean源码解析

createBean方法大致流程:

  (1)判断需要创建的Bean是否可以实例化,即是否可以通过当前的类加载器加载(如果可以,克隆一份BeanDefinition)  

    (2)用BeanDefinition副本调用prepareMethodOverrides()方法,进行校验Bean和准备Bean中的方法覆盖(处理方法覆盖)

  (3)⭐处理Bean配置的初始化前后的处理器(前置:该方法里头会调用所有的Bean处理器,如果处理器中有调换了bean的,则返回bean,如果没有则返回null)

  (4)⭐调用doCreateBean()方法,该方法是创建Bean的入口

doCreateBean方法流程图:

 

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
            throws BeanCreationException {

        // Instantiate the bean.
        //bean实例包装类
        BeanWrapper instanceWrapper = null;
        if (mbd.isSingleton()) {
            //从未完成创建的包装Bean缓存中清理并获取相关中的包装Bean实例,毕竟是单例,只能存一份
            instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
        }
        if (instanceWrapper == null) {
            //创建bean的时候,这里创建bean的实例有三种方法
            //1.工厂方法创建
            //2.构造方法的方式注入
            //3.无参构造方法注入
            instanceWrapper = createBeanInstance(beanName, mbd, args);//********
        }
        //获取被包装的Bean,后续对bean的改动相对于对Wrapper的改动,反之依然
        final Object bean = instanceWrapper.getWrappedInstance();
        //获取实例化对象的类型
        Class<?> beanType = instanceWrapper.getWrappedClass();
        if (beanType != NullBean.class) {
            mbd.resolvedTargetType = beanType;
        }

        // Allow post-processors to modify the merged bean definition.
        //调用BeanDefinition属性合并完成后的BeanPostProcessor后置处理器
        synchronized (mbd.postProcessingLock) {
            if (!mbd.postProcessed) {
                try {
                    //@Autowired、@Value标记的属性在这里获取
                    //应用合并后的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;
            }
        }

        // Eagerly cache singletons to be able to resolve circular references
        // even when triggered by lifecycle interfaces like BeanFactoryAware.
        //向容器中缓存单例模式的Bean对象,以防循环引用
        //判断是否是早期引用的bean,如果是,则允许其提前暴露引用
        // 这里判断的逻辑主要有三个∶
        //1.是否为单例
        //2 是否允许循环引用
        //3.是否是在创建中的bean
        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {
            if (logger.isTraceEnabled()) {
                logger.trace("Eagerly caching bean '" + beanName +
                        "' to allow for resolving potential circular references");
            }
            //这里是一个匿名内部类,为了防止循环引用,尽早持有对象的引用
            //将还没完全配置好的bean存入到三级缓存中供其他bean使用(暴露引用)
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
            //这里的getEarlyBeanReference()方法,并不是在此调用,而是声明好了方法
            //具体的调用在Spring的AbstractBeanFactory的doGetBean的第三行调用
            // DefaultSingletonBeanRegistry类的getSingleton方法中,调用
            // singletonFactory.getObject()(单例工厂的getObject方法返回实例对象)
        }

        // Initialize the bean instance.
        //Bean对象的初始化,依赖注入在此触发
        //这个exposedObject在初始化完成之后返回作为依赖注入完成后的Bean
        Object exposedObject = bean;//暴露的对象
        try {
            //填充bean实例的属性
            populateBean(beanName, mbd, instanceWrapper);

            //初始化bean,过程如下:
            //1.判断是否实现了BeanNameAware,BeanClassLoaderAware,BeanFactoryAware方法,
            //如果有,则设置相关的属性
            //2.调用bean初始化前的前置(BeanPostProcessor)操作
            //3.执行初始化的方法
            //如果有InitializingBean,则调用afterPropertiesSet
            //如果有InitMethod,则调用初始方法
            //4.调用bean初始化的后置(BeanPostProcessor)操作
            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);
            }
        }

        //若允许循环依赖,则解决相关的循环依赖
        //earlySingletonExposure是前面代码判断出来的是否:暴露引用bean
        if (earlySingletonExposure) {
            //获取指定名称的已注册的单例模式Bean对象
            //这里的getSingleton()从三层缓存中依次去获取,由于
            //先前我们已经将对象存入到三级缓存中,所有这里在三级缓存获取得到
            //然后缓存中的对象被放到了二级缓存中,三级中移除了
            Object earlySingletonReference = getSingleton(beanName, false);//
            if (earlySingletonReference != null) {
                //如果经过initializeBean执行后返回的bean还是同一个(不是代理对象实例,即没有被增强)
                if (exposedObject == bean) {
                    //确保根据名称获取到的
                    exposedObject = earlySingletonReference;
                }
                //如果上面的if没通过,则表明引用的bean和注入的bean不一致,则需要看看依赖于此Bean的先前是否已经
                //allowRawInjectionDespiteWrapping标注是否允许此Bean的原始类型被注入到其他Bean里面,
                //即使自己最终会被包装(代理)
                //dependentBeanMap记录着每个依赖于此Bean的Bean实例集合
                //当发生循环引用时不允许新创建实例对象
                else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                    String[] dependentBeans = getDependentBeans(beanName);
                    Set<String> actualDependentBeans = new LinkedHashSet<>(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.");
                    }
                }
            }
        }
doCreateBean源码解析

doCreateBean方法大致流程:

  (1)判断BeanDefinition是不是单例的,如果是单例的,从未完成创建的包装Bean缓存中清理并获取相关中的包装Bean实例,毕竟是单例,只能存一份

  (2)判断第一步中获到的包装Bean实例如果是空的,调用createBeanInstance()方法创建bean,这里创建bean的实例有三种方法

    (1)尝试获取BeanDefinition里面的Class对象 

    (2)检查是否有权通过反射创建private的Class

    (3)如果bean使用的是java自带的函数式接口Supplier(工厂方法),则调用里头的get创建返回bean实例

    (4)如果BeanDefinition里面有定义Spring的工厂方法如(factory-method),则调用factory-method创建返回实例 

    (5)查询BeanDefinition中有没有缓存好的构造函数或者工厂方法,有则调用创建返回实例

    (6)判断是否使用(带惨构造函数装配)如果都不是最终调用无参构造函数创建返回实例  

  (3)获取被包装的Bean,后续对bean的改动相对于对Wrapper的改动,反之依然,获取实例化对象的类型并放入BeanDefinition中

  (4)对BeanDefinition的后置处理器上锁操作,判断BeanDefinition没有使用后置处理器,后调用applyMergedBeanDefinitionPostProcessors()方法应用合并后的BeanDefinition后置处

    理器(@Autowired、@Value标记的属性在这里获取) applyMergedBeanDefinitionPostProcessors内部跟绝大部分后置处理器一样采用了责任链模式,循环遍历所有后置处理器,

    IF实现了MergedBeanDefinitionPostProcessor接口的 后置处理器便调用postProcessMergedBeanDefinition方法这里我们重点关注:AutowiredAnnotationBeanPostProcessor.postProcessMergedBeanDefinition()

      (1)进入AutowiredAnnotationBeanPostProcessor类,首先我们可以看到该类的无参构造方法中对@Autowired、@Value注解添加到了该类的成员变量里。

      (2)我们查看postProcessMergedBeanDefinition()方法

        (A)首先第一行调用了findAutowiringMetadata()方法(寻找被注解标记的元数据)

             (1)用类名作为cacheKey(缓存建名) 

             (2)使用cacheKey在缓存容器中查找是否有相关缓存 

             (3)如果缓存容器中没有metadata(元数据)|| metadata的class和给定的clazz不符合则重新获取到构建好的metadata实例并放入到缓存容器中(双重锁机制)

        (B)用前面查找到的metadata调用checkConfigMembers方法,需要Spring用默认策略放到的InjectedElements(注入元素表)   

  (5)判断是否是早期引用的bean,如果是,则允许其提前暴露引用(单例、允许循环引用、创建中的bean)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

createBean方法详解 的相关文章

  • 为什么 JTables 使 TableModel 在呈现时不可序列化?

    所以最近我正在开发一个工具 供我们配置某些应用程序 它不需要是什么真正令人敬畏的东西 只是一个具有一些 SQL 脚本生成功能并创建几个 XML 文件的基本工具 在此期间 我使用自己的 AbstractTableModel 实现创建了一系列
  • .properties 中的通配符

    是否存在任何方法 我可以将通配符添加到属性文件中 并且具有所有含义 例如a b c d lalalala 或为所有以结尾的内容设置一个正则表达式a b c anything 普通的 Java 属性文件无法处理这个问题 不 请记住 它实际上是
  • org.apache.hadoop.security.AccessControlException:客户端无法通过以下方式进行身份验证:[TOKEN,KERBEROS] 问题

    我正在使用 java 客户端通过 Kerberos 身份验证安全访问 HDFS 我尝试打字klist在服务器上 它显示已经存在的有效票证 我收到的异常是客户端无法通过以下方式进行身份验证 TOKEN KERBEROS 帮助将不胜感激 这是一
  • Pig Udf 显示结果

    我是 Pig 的新手 我用 Java 编写了一个 udf 并且包含了一个 System out println 其中的声明 我必须知道在 Pig 中运行时该语句在哪里打印 假设你的UDF 扩展了 EvalFunc 您可以使用从返回的 Log
  • 如何获取之前的URL?

    我需要调用我的网络应用程序的 URL 例如 如果有一个从 stackoverflow com 到我的网站 foo com 的链接 我需要 Web 应用程序 托管 bean 中的 stackoverflow 链接 感谢所有帮助 谢谢 并不总是
  • 检测并缩短字符串中的所有网址

    假设我有一条字符串消息 您应该将 file zip 上传到http google com extremelylonglink zip http google com extremelylonglink zip not https stack
  • Eclipse Maven Spring 项目 - 错误

    I need help with an error which make me crazy I started to study Java EE and I am going through tutorial on youtube Ever
  • 像 Java 这样的静态类型语言中动态方法解析背后的原因是什么

    我对 Java 中引用变量的动态 静态类型和动态方法解析的概念有点困惑 考虑 public class Types Override public boolean equals Object obj System out println i
  • 内部类的构造函数引用在运行时失败并出现VerifyError

    我正在使用 lambda 为内部类构造函数创建供应商ctx gt new SpectatorSwitcher ctx IntelliJ建议我将其更改为SpectatorSwitcher new反而 SpectatorSwitcher 是我正
  • Java ResultSet 如何检查是否有结果

    结果集 http java sun com j2se 1 4 2 docs api java sql ResultSet html没有 hasNext 方法 我想检查 resultSet 是否有任何值 这是正确的方法吗 if resultS
  • java.io.Serialized 在 C/C++ 中的等价物是什么?

    C C 的等价物是什么java io Serialized https docs oracle com javase 7 docs api java io Serializable html 有对序列化库的引用 用 C 序列化数据结构 ht
  • 如何使用 jUnit 将测试用例添加到套件中?

    我有 2 个测试类 都扩展了TestCase 每个类都包含一堆针对我的程序运行的单独测试 如何将这两个类 以及它们拥有的所有测试 作为同一套件的一部分执行 我正在使用 jUnit 4 8 在 jUnit4 中你有这样的东西 RunWith
  • 非 Spring 托管类中 DI 的编译时编织

    我想为标记为的类配置编译时编织 Configurable注释能够将 spring 依赖项注入到初始化的类中new操作员 我不想使用加载时编织 因为我无权访问应用程序服务器的运行脚本 因此无法修改它 另外 我希望能够在测试中使用此类 我的意思
  • Eclipse 启动时崩溃;退出代码=13

    I am trying to work with Eclipse Helios on my x64 machine Im pretty sure now that this problem could occur with any ecli
  • 我如何在java中读取二进制数据文件

    因此 我正在为学校做一个项目 我需要读取二进制数据文件并使用它来生成角色的统计数据 例如力量和智慧 它的设置是让前 8 位组成一个统计数据 我想知道执行此操作的实际语法是什么 是不是就像读文本文件一样 这样 File file new Fi
  • 创建一个 JSON 对象以在 Spring Boot 测试中发布

    我想编写基本测试来使用 JSON 负载在 users URL 上执行 POST 请求来创建用户 我找不到如何将新对象转换为 JSON 到目前为止有这么多 这显然是错误的 但解释了目的 Test public void createUser
  • 在java中为组合框分配键

    我想添加一个JComboBox在 Swing 中这很简单 但我想为组合中的每个项目分配值 我有以下代码 JComboBox jc1 new JComboBox jc1 addItem a jc1 addItem b jc1 addItem
  • 长轮询会冻结浏览器并阻止其他 ajax 请求

    我正在尝试在我的中实现长轮询Spring MVC Web 应用程序 http static springsource org spring docs 2 0 x reference mvc html但在 4 5 个连续 AJAX 请求后它会
  • CamcorderProfile.videoCodec 返回错误值

    根据docs https developer android com reference android media CamcorderProfile html 您可以使用CamcorderProfile获取设备默认视频编解码格式 然后将其
  • Java中super关键字的范围和使用

    为什么无法使用 super 关键字访问父类变量 使用以下代码 输出为 feline cougar c c class Feline public String type f public Feline System out print fe

随机推荐

  • zookeeper的Linux下安装和使用 单机版/集群版

    这个是单节点的 集群的在另一篇文章里做了说明 一 解压zookeeper的tar包 二 到zookeeper的conf目录下 拷贝zoo sample cfg 为zoo cfg 记住名字必须叫zoo cfg root VM 0 7 cent
  • 【高级篇 / SDWAN】(7.0) ❀ 07. DNS 解析最快的宽带优先上网 ❀ FortiGate 防火墙

    自从配置SD WAN之后 很多人反应网速变慢了 打开网站卡半天 你有没有接到过这种投诉 其实这是因为DNS解析的原因 由于多条宽带属于不同的运营商 而运营商自带的DNS对自己宽带的解析很快 但对其它宽带就会报错 所以我们只能使用通用DNS
  • 2021-08-06

    在编译OKVIS中 执行make j8时报错的解决方法 1 根据github上OKVIS的安装步骤一步一步执行 由于github经常进不去 我就进了gitee网站查到OKVIS的安装步骤 参考链接 https gitee com bill4
  • vue 引入 二维码

    vue cli3 动态生成二维码 不带logo的 第一步 先下载插件 npm install qrcodejs2 save 第二步 在需要生成二维码的 页面 导入 import QRCode from qrcodejs2 第三步 在页面中引
  • [云原生专题-34]:K8S - 核心概念 - 网络 - Web服务器与反向代理服务器nginx入门介绍

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文网址 https blog csdn net HiWangWenBing article details 122806880 目录 第1章 常见网
  • 深入了解一下C语言scanf()库函数

    目录 一 scanf函数的定义 二 scanf函数的返回值 三 scanf函数的控制符 四 scanf函数控制符的使用 1 一般用法 2 scanf 函数 控制符的用法 2 1 控制符的两种形式 2 2 字符的使用 3 键盘缓冲区残余信息问
  • Confluence 6 管理应用服务器内存设置

    应用服务器中的最小和最大 JVM Heap 空间配置将会影响系统的性能 Confluence 管理员可能希望对默认的配置进行修改 基于你系统的负载不同配置情况也会有所不同 请参考页面 Server Hardware Requirements
  • sed命令常见用法

    常见sed命令的操作 a 增加 在当前行下面增加一行指定内容 c 整行替换 将选定行替换为指定内容 d 删除 删除选定的行 w 将行写入新文件 r 从文件中读取 i 插入 在选定行上面插入一行指定内容 p 打印 如果同时指定行 表示打印指定
  • 五彩斑斓的黑

  • c++操作sqllite

    项目中需要使用的sqllite 有想过使用内存的结果 好像都不大使用 最接近的算是vector了 但是查询方式不大好 而且数据有好几个字段 所以考虑了数据库 sqllite目前已经到了3了 好快 好像这个数据库也不弱 就先用着吧 其实挺简单
  • FPGA—串口RS232(附实现代码)

    目录 1 理论 1 1 串口简介 1 2 RS232信号线 1 3 RS232通信协议简介 2 实操 2 1 硬件资源 2 2 顶层模块 2 2 1 模块说明 2 2 2 RTL 代码 2 2 3 仿真验证 2 3 串口数据接收模块 2 3
  • PyCharm集成SVN,检出、提交代码

    工作需要 使用PyCharm集成SVN 进行代码管理 搜索网上资料 没有讲的很清楚的 自己动手摸索 大致了解了使用方法 遂记录下来 希望他人少走些弯路
  • 比较Opencv自带的frontface检测器

    CascadeClassifier haarcascade frontalface alt new CascadeClassifier xml haarcascade frontalface alt xml CascadeClassifie
  • 【Linux】Linux服务器解决python3.7与openssl的低版本不兼容的问题

    安装了Python3 7之后 遇到的一个很麻烦的坑就是与系统自带的ssl版本不兼容 Python3 7需要的openssl的版本为1 0 2或者1 1 x 这个requirements在config Python3 7的时候使用 with
  • c++栈实现表达式求值

    文章目录 前言 一 思想分析 二 具体实现 前言 后缀表达式的算法思想与具体实现 一 思想分析 设定两个栈 操作数栈 OPND 操作符栈 OPTR 栈初始化 置操作数栈 OPND 为空 操作符栈 OPTR 中预设一个优先级最低的操作符 自左
  • Shell Expect 命令

    expect可以实现shell实现不了的用户交互的需求 expect可以将交互写在一个脚本上 完成很多自动化的动作 比如ssh ftp登陆等 都是需要交互需求的 expect是需要安装的 直接yum y install expect安装即可
  • Class 00 - 学习编程的方法&不同职业所使用的编程语言

    Class 00 学习编程的方法 不同职业所使用的编程语言 学习编程的方法 什么是编程 不同职业所使用的编程语言 数据分析 网页设计 移动应用开发 Web应用开发 游戏开发 Tips 学习编程语言的技巧 从电子表格到 SQL 再到 R 电子
  • threejs学习01-环境搭建+简单示例

    threejs学习 环境搭建 简单示例 环境搭建 node js vite js three js 轻量级的环境 先安装配置好node 在cmd中输入 node v 来查看node版本 node 配置好后就可以创建一个vite的项目了 先调
  • Vuforia Ground Plane 平面识别

    首先弄出这几个组件 如图 还有 再然后 然后就是关键了 如果Vuforia版本低于8 5 8 就得导入ARcore的arr 也就是这个 这个可以在 https dl google com dl android maven2 com goog
  • createBean方法详解

    前言 createBean是创建Bean的主要方法 该方法位于 AbstractBeanFactory的doGetBean方法中的createBean调用 createBean方法流程图 createBean源码解析 protected O