Spring中Bean的实例化详细流程

2023-11-09

还是举个例子,我有一个朋友小汪他远赴南方某城市打工。然后安定下来后他的朋友很想来家里玩,但是呢我这个朋友家里搞的很乱,所以他不好意思请朋友来家里玩。这时我的另一个朋友说那请一个保姆把家里好好整理一下就可以了,然后给他介绍了一个保姆大S(PS:本文无意指向任何人,因为Spring的前缀是S)然后就把家里整理得井井有条,就请朋友来家里玩了。

好了引入正文,很早很早的Java开发者应该熟悉,最早的时候我们前端访问后端都是需要自己写Servlet的,大概是一个接口写一个Servlet。Java开发又是面向对象的编程,我们程序里面写了new 了很多的对象。写了很多个Servlet,对象很难管理造成我们的程序很乱,都看不下去。后面Spring来了对象都交给了Spring管理,Servlet相关的也都交给了SpringMVC,这样我们开发就顺利多了。好了这下懂我上面举的例子了吧,懂得保姆是什么意思了吧【Spring就像一个管家,一个保姆】。所以多了解Spring相关知识我们提高开发效率有很大的帮助。既然我们的对象交给了Spring管理,那我们的对象怎么生成的呢,就让我们一起看下。

我们在使用Spring的时候,容器中的Bean在我们项目启动的时候都已经给我们生成了,直接使用就行了。容器启动的时候会调用这个方法:

AbstractApplicationContext.refresh()

然后就会调用下面这个方法:

// Instantiate all remaining (non-lazy-init) singletons.
 // 翻译一下就是 实例化所有非懒加载的Bean
finishBeanFactoryInitialization(beanFactory);

如图refresh中的方法,它再次调用的每个方法都很重要,实例化所有单例Bean的方法在这个方法的最后调用

 我们写的对象基本都在这个方法内进行实例化。【PS方法只讲一些很重要的,具体的更详细方法调用我会在文章后面的流程图中展示出来。】

DefaultListableBeanFactory.preInstantiateSingletons()。
@Override
 public void preInstantiateSingletons() throws BeansException {

    // Iterate over a copy to allow for init methods which in turn register new bean definitions.
    // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
    // 获取所有的要实例化的Bean的名称
    List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
    // Trigger initialization of all non-lazy singleton beans...
    // 开始初始化单例的Bean
    for (String beanName : beanNames) {
      RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
       // Bean 不是抽象的,是单例的,不是懒加载的进入如下分支
      if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
        // 如果是FactoryBean进入此分支。本次只聊自己开发写的非FactoryBean
        // 所以聊else下面的分支。
        if (isFactoryBean(beanName)) {
        // FactoryBean的名称很特别
          Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
          if (bean instanceof FactoryBean) {
            final FactoryBean<?> factory = (FactoryBean<?>) bean;
            boolean isEagerInit;
            if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
              isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
                      ((SmartFactoryBean<?>) factory)::isEagerInit,
                  getAccessControlContext());
            }
            else {
              isEagerInit = (factory instanceof SmartFactoryBean &&
                  ((SmartFactoryBean<?>) factory).isEagerInit());
            }
            if (isEagerInit) {
              getBean(beanName);
            }
          }
        }
        else {
         // 非 FactoryBean进入此分支
          getBean(beanName);
        }
      }
    }
  }

然后会进入如下方法。

AbstractBeanFactory.doGetBean() 的方法。
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
      @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
    //  这个方法主要是获取Bean的名称,一些Bean的名称可能命名的比较特别
    // 需要进行转换。
    final String beanName = transformedBeanName(name);
    Object bean;
    // Eagerly check singleton cache for manually registered singletons.
    // 首先先从容器的缓存中获取Bean,如果容器中已经存在,直接返回。
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    } 
    else {
      // Fail if we're already creating this bean instance:
      // We're assumably within a circular reference.
      // 先检查这个Bean是否在创建中,如果在创建中抛出异常 
      if (isPrototypeCurrentlyInCreation(beanName)) {
        throw new BeanCurrentlyInCreationException(beanName);
      }
       // 标记Bean为正在创建中。
      if (!typeCheckOnly) {
        markBeanAsCreated(beanName);
      }
      try {
        // Create bean instance.
        //  如果Bean是单例开始创建Bean .
        //  后面判断还有Prototype(多例)不是要讲的重点,代码删除了。
        if (mbd.isSingleton()) {
        //  这个方法是Java 8的 lambda 写法,这个方法里面会把创建好的
        // Bean放到Spring容器中,后面再获取这个Bean直接从容器中获取了。
          sharedInstance = getSingleton(beanName, () -> {
            try {
            // 正式开始创建Bean 。
              return createBean(beanName, mbd, args);
            }
            catch (BeansException ex) {
            // 创建过程出现异常,销毁Bean
              destroySingleton(beanName);
              throw ex;
            }
          });
          bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
        }
    return (T) bean;
  }

然后是正式真正的创建Bean的方法如下:

AbstractAutowireCapableBeanFactory.createBean() 的方法。
@Override
  protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
      throws BeanCreationException {
    RootBeanDefinition mbdToUse = mbd;
    try {
     // doCreateBean 是Spring正在做事的方法。
      Object beanInstance = doCreateBean(beanName, mbdToUse, args);
  }
  protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
      throws BeanCreationException {
    // Instantiate the bean.
    //  实例化Bean 先创建一个BeanWrapper .这个方法里面Spring 一般为默认
    // 无参的构造方法创建对象,所以大家如果重写对象的构造方法的时候,一定
    // 要把无参构造方法也写出来。要不然某些情况下启动Spring容器可能会报错。
    if (instanceWrapper == null) {
      instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    // Initialize the bean instance.
    Object exposedObject = bean;
    try {
    // 为Bean的属性赋值。
      populateBean(beanName, mbd, instanceWrapper);
      // 初始化Bean 。
      exposedObject = initializeBean(beanName, exposedObject, mbd);
    }
    return exposedObject;
  }
  // 初始化Bean。
  protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
    // 如果你的Bean实现了Spring内置的Aware方法,会在这里执行
    invokeAwareMethods(beanName, bean);
    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
    // 执行Bean的初始化前置处理器,很重要也就是Spring的钩子函数
      wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }
    try {
    // 执行Bean的初始化方法
      invokeInitMethods(beanName, wrappedBean, mbd);
    }
    if (mbd == null || !mbd.isSynthetic()) {
    // 执行Bean的后置处理器,也很重要。
    // 很多写底层架构的人都会对此钩子方法灵活应用
      wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }
    return wrappedBean;
  }

PS:下面是Bean实例化的详细的流程图,由于画好后的整个流程图无法完全保存,只有一张一张的截屏了。图片一张一张往下看就是整个完整的流程,自己可以找着图片一步一步看,就会对Bean的整个流程很清楚了。

 

 

读完熟悉了Spring实例化的流程你能做些什么呢?

 1:比如实现BeanPostProcessor。A初始化前和后分别会执行下面2个方法。

@Component
public class A implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName);
        return null;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName);
        return null;
    }
}

2:实现InitializingBeanA初始化的时候会执行以下方法。

@Component
public class A implements InitializingBean {
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("执行InitializingBean的afterPropertiesSet方法");
    }
}

上面实现的方法都会在A实例化的时候执行,如果你写的业务逻辑有需要在A实例化时候执行的就可以使用上面的方法完成。

 

 欢迎跟着图中走一遍源码,相信你会对Spring创建Bean的流程更加了解。看一些源码你的思路会更清晰,写代码也更得心应手,写代码的时候你可能不自己觉的就按照这些大神写代码的思路去完成高质量的代码。

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

Spring中Bean的实例化详细流程 的相关文章

  • Java EE:如何获取我的应用程序的 URL?

    在 Java EE 中 如何动态检索应用程序的完整 URL 例如 如果 URL 是 localhost 8080 myapplication 我想要一个可以简单地将其作为字符串或其他形式返回给我的方法 我正在运行 GlassFish 作为应
  • 在画布上绘图

    我正在编写一个 Android 应用程序 它可以在视图的 onDraw 事件上直接绘制到画布上 我正在绘制一些涉及单独绘制每个像素的东西 为此我使用类似的东西 for int x 0 x lt xMax x for int y 0 y lt
  • Java JDBC:更改表

    我希望对此表进行以下修改 添加 状态列 varchar 20 日期列 时间戳 我不确定该怎么做 String createTable Create table aircraft aircraftNumber int airLineCompa
  • 在 HTTPResponse Android 中跟踪重定向

    我需要遵循 HTTPost 给我的重定向 当我发出 HTTP post 并尝试读取响应时 我得到重定向页面 html 我怎样才能解决这个问题 代码 public void parseDoc final HttpParams params n
  • 控制Android的前置LED灯

    我试图在用户按下某个按钮时在前面的 LED 上实现 1 秒红色闪烁 但我很难找到有关如何访问和使用前置 LED 的文档 教程甚至代码示例 我的意思是位于 自拍 相机和触摸屏附近的 LED 我已经看到了使用手电筒和相机类 已弃用 的示例 但我
  • Java按日期升序对列表对象进行排序[重复]

    这个问题在这里已经有答案了 我想按一个参数对对象列表进行排序 其日期格式为 YYYY MM DD HH mm 按升序排列 我找不到正确的解决方案 在 python 中使用 lambda 很容易对其进行排序 但在 Java 中我遇到了问题 f
  • 在两个活动之间传输数据[重复]

    这个问题在这里已经有答案了 我正在尝试在两个不同的活动之间发送和接收数据 我在这个网站上看到了一些其他问题 但没有任何问题涉及保留头等舱的状态 例如 如果我想从 A 类发送一个整数 X 到 B 类 然后对整数 X 进行一些操作 然后将其发送
  • 如何将 pfx 文件转换为 jks,然后通过使用 wsdl 生成的类来使用它来签署传出的肥皂请求

    我正在寻找一个代码示例 该示例演示如何使用 PFX 证书通过 SSL 访问安全 Web 服务 我有证书及其密码 我首先使用下面提到的命令创建一个 KeyStore 实例 keytool importkeystore destkeystore
  • 为什么HashMap不能保证map的顺序随着时间的推移保持不变

    我在这里阅读有关 Hashmap 和 Hashtable 之间的区别 http javarevisited blogspot sg 2010 10 difference Between hashmap and html http javar
  • JRE 系统库 [WebSphere v6.1 JRE](未绑定)

    将项目导入 Eclipse 后 我的构建路径中出现以下错误 JRE System Library WebSphere v6 1 JRE unbound 谁知道怎么修它 右键单击项目 特性 gt Java 构建路径 gt 图书馆 gt JRE
  • 总是使用 Final?

    我读过 将某些东西做成最终的 然后在循环中使用它会带来更好的性能 但这对一切都有好处吗 我有很多地方没有循环 但我将 Final 添加到局部变量中 它会使速度变慢还是仍然很好 还有一些地方我有一个全局变量final 例如android Pa
  • 加密 JBoss 配置中的敏感信息

    JBoss 中的标准数据源配置要求数据库用户的用户名和密码位于 xxx ds xml 文件中 如果我将数据源定义为 c3p0 mbean 我会遇到同样的问题 是否有标准方法来加密用户和密码 保存密钥的好地方是什么 这当然也与 tomcat
  • 如何在 javadoc 中使用“<”和“>”而不进行格式化?

    如果我写
  • 将不同类型的参数传递给 jdbctemplate 查询

    我正在尝试使用带有少量不同类型参数的 where 子句从数据库中检索记录 这是我编写的简单方法 我将breedId和性别作为参数传递 public List
  • 如何在控制器、服务和存储库模式中使用 DTO

    我正在遵循控制器 服务和存储库模式 我只是想知道 DTO 在哪里出现 控制器应该只接收 DTO 吗 我的理解是您不希望外界了解底层域模型 从领域模型到 DTO 的转换应该发生在控制器层还是服务层 在今天使用 Spring MVC 和交互式
  • AWS 无法从 START_OBJECT 中反序列化 java.lang.String 实例

    我创建了一个 Lambda 函数 我想在 API 网关的帮助下通过 URL 访问它 我已经把一切都设置好了 我还创建了一个application jsonAPI Gateway 中的正文映射模板如下所示 input input params
  • 在 Mac 上正确运行基于 SWT 的跨平台 jar

    我一直致力于一个基于 SWT 的项目 该项目旨在部署为 Java Web Start 从而可以在多个平台上使用 到目前为止 我已经成功解决了由于 SWT 依赖的系统特定库而出现的导出问题 请参阅相关thread https stackove
  • 仅将 char[] 的一部分复制到 String 中

    我有一个数组 char ch 我的问题如下 如何将 ch 2 到 ch 7 的值合并到字符串中 我想在不循环 char 数组的情况下实现这一点 有什么建议么 感谢您花时间回答我的问题 Use new String value offset
  • 获取 JVM 上所有引导类的列表?

    有一种方法叫做findBootstrapClass对于一个类加载器 如果它是引导的 则返回一个类 有没有办法找到类已经加载了 您可以尝试首先通过例如获取引导类加载器呼叫 ClassLoader bootstrapLoader ClassLo
  • 如何修复 JNLP 应用程序中的“缺少代码库、权限和应用程序名称清单属性”?

    随着最近的 Java 更新 许多人都遇到了缺少 Java Web Start 应用程序的问题Codebase Permissions and Application name体现属性 尽管有资源可以帮助您完成此任务 但我找不到任何资源综合的

随机推荐

  • 费曼学习法读后感

    目录 一 概述 二 全书主要内容 2 1 学习的本质 2 2 确立一个学习对象 2 3 理解我们要学习的知识 2 3 1 归类和对比知识的来源 2 3 1 1 将知识有逻辑的系统化 2 3 1 2 筛选和留下最可靠的知识 2 3 1 3 分
  • UOS启用wayland

    目录 系统wayland情况分析 后记 系统wayland情况分析 UOS默认启用的是x11 但是华为L410 L420这种麒麟芯片的机器 默认启用的是wayland 原因后面再推测 所以也想看看非麒麟平台如何能切换到wayland试试效果
  • [转]什么是边缘计算?

    转自 https blog csdn net gui951753 article details 80952907 注 本篇翻译自施巍松教授的论文 Edge Computing Vision and Challenges 目录 文章目录 摘
  • 工程代码_【OPPO手机进入工程模式】代码指令汇总

    来源 故乡往忆 首先要说的是 因为安卓系统版本不同 有的是安卓7 1有的是安卓8 1 因此即便都是vivo手机 可能在具体的代码指令上面 也会稍有不同 但是也都是大同小异 具体请自测 其次 工程模式是工程师测试使用的 若更改了工程模式下的设
  • BART原理简介与代码实战

    写在前面 最近huggingface的transformer库 增加了BART模型 Bart是该库中最早的Seq2Seq模型之一 在文本生成任务 例如摘要抽取方面达到了SOTA的结果 本次放出了三组不同的预训练权重 bart large 基
  • 通过STM32单片机控制直流电机实现位置速度电流PID三闭环,让电机精准控制!

    今天我们将会为大家带来一个非常实用的主题 那就是如何通过STM32单片机控制直流电机实现位置速度电流PID三闭环 让电机精准控制 首先 我们需要准备一台直流电机和一块STM32单片机开发板 接下来 我们将会通过C语言代码来实现PID三闭环控
  • HTTP 413错误解决方法

    环境 Ubuntu 16 04 nginx 1 5 6 通过post上传文件过大时 会出现413错误码 前端打开开发者模式 在输出台的位置会出现 Failed to load resource the server responded wi
  • 教你如何用VB做控件*.ocx

    教你如何用VB做控件 ocx 一个控件有很多事件如 Click MouseDown MouseUp MouseMove等 要触发这些事件都需要你加入代码 在控件的声明处加入Public Event Click 就表明该控件有一 Click
  • win7安装linux子系统,linux系统装win7系统安装教程【图文教程】

    说到linux系统可能很多人都不是特别的了解 因为在我们接触电脑用的最多的还是Windows系统 linux系统是一个基于POSIX多用户和多任务的一种CPU操作系统 但是一般人在选择的时候 还是会倾向于Windows系统 那么对于电脑安装
  • Linux下搭建zabbix的客户端,zabbix客户端linux下安装

    一 自定义安装zabbix agentd 临时目录 mkdir mnt tmp cd mnt tmp 下载zabbix管理程序 因为里面有我们要使用的客户端配置文件 建议单独保存 不然每次都下 可不好玩 wget http ncu dl s
  • 全球及中国航空发动机市场动态前景及十四五项目专项调研报告2021-2027年版

    全球及中国航空发动机市场动态前景及十四五项目专项调研报告2021 2027年版 HS HS HS HS HS HS HS HS HS HS HS HS HS HS 修订日期 2021年10月 搜索鸿晟信合研究院查看官网更多内容 第一章 航空
  • 使用LSTM进行预测,有一对一、多对一、多对多的预测,其中有一些疑问一起探讨(一)

    数据说明 我的数据是1万6千多的数据 想用4个特征 这个特征未加输出 预测2个输出 也就是多对多的预测 使用LSTM 一对一的预测 先用对一的预测简单一些 就是用一段时序数据取预测 代码例子看的MATLAB工具箱的例子 代码测试可行 大致看
  • 深入浅出MYSQL索引实现机制

    一 什么是索引 提到数据库索引 我想你并不陌生 在日常工作中会经常接触到 比如 我们的某一个SQL查询语句响应很慢 你可能第一反应是 给这个SQL 加个索引吧 那么到底什么是索引昵 今天我们就来聊一下这个话题 本文以MYSQL 5 6 为例
  • Redis使用总结(二、缓存和数据库双写一致性问题)

    首先 缓存由于其高并发和高性能的特性 已经在项目中被广泛使用 在读取缓存方面 大家没啥疑问 都是按照下图的流程来进行业务操作 但是在更新缓存方面 对于更新完数据库 是更新缓存呢 还是删除缓存 又或者是先删除缓存 再更新数据库 其实大家存在很
  • Flutter 升级2.5之后报错?

    Q Flutter执行命令升级新版本后 用flutter doctor命令检查时存在如下问题 按照提示键入命令后 再次出现报错 A 当我们升级SDK后 执行flutter doctor 这里是提示我们需要安装Android开发的命令行工具
  • iPhone/iPad用iTunes“同步”不等于“备份”

    一个很 基础 却很 重要 很多人 搞不清楚 解释又很花时间的问题 就是 iPhone 跟电脑 iTunes 同步 和 备份 有什么不同 首先 Sync 翻译成中文 同步 本来就是一个定义 认知有点模糊的中文动词 尤其对电脑不是很熟悉的朋友
  • Java 构造函数的详解

    我们人出生的时候 有些人一出生之后再起名字的 但是有些人一旦出生就已经起好名字的 那么我们在java里面怎么在对象一旦创建就赋值呢 1 构造方法的作用 构造方法作用 对对象进行初始化 如图 2 构造函数与普通函数的区别 1 一般函数是用于定
  • CTF工具压缩包爆破神器Fcrackzip详细用法

    Fcrackzip简介 Fcrackzip是一款专门破解zip类型压缩文件密码的工具 工具小巧方便 破解速度快 能使用字典和指定字符集破解 适用于linux mac osx 系统 Fcrackzip下载 Windows下载 下载链接 htt
  • 「爬虫教程」吐血整理,最详细的爬虫入门教程

    初识爬虫 学习爬虫之前 我们首先得了解什么是爬虫 来自于百度百科的解释 网络爬虫 又称为网页蜘蛛 网络机器人 在FOAF社区中间 更经常的称为网页追逐者 是一种按照一定的规则 自动地抓取万维网信息的程序或者脚本 通俗来讲 假如你需要互联网上
  • Spring中Bean的实例化详细流程

    还是举个例子 我有一个朋友小汪他远赴南方某城市打工 然后安定下来后他的朋友很想来家里玩 但是呢我这个朋友家里搞的很乱 所以他不好意思请朋友来家里玩 这时我的另一个朋友说那请一个保姆把家里好好整理一下就可以了 然后给他介绍了一个保姆大S PS