Spring中Bean的实例化详细流程

news2024/9/21 22:43:52

还是举个例子,我有一个朋友小汪他远赴南方某城市打工。然后安定下来后他的朋友很想来家里玩,但是呢我这个朋友家里搞的很乱,所以他不好意思请朋友来家里玩。这时我的另一个朋友说那请一个保姆把家里好好整理一下就可以了,然后给他介绍了一个保姆大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的流程更加了解。看一些源码你的思路会更清晰,写代码也更得心应手,写代码的时候你可能不自己觉的就按照这些大神写代码的思路去完成高质量的代码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/824442.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【LeetCode】一、链表反转

题目 题目&#xff1a;给定单链表头节点&#xff0c;将单链表的链接顺序反转过来 例&#xff1a; 输入&#xff1a;1->2->3->4->5 输出&#xff1a;5->4->3->2->1 要求&#xff1a;按照两种方式实现 解决办法 方式一&#xff1a;&#xff08;直接迭…

腾讯云服务器CVM镜像操作系统大全_win_linux

腾讯云CVM服务器的公共镜像是由腾讯云官方提供的镜像&#xff0c;公共镜像包含基础操作系统和腾讯云提供的初始化组件&#xff0c;公共镜像分为Windows和Linux两大类操作系统&#xff0c;如TencentOS Server、Windows Server、OpenCloudOS、CentOS Stream、CentOS、Ubuntu、Deb…

大一python编程题库和答案,大一python基础编程题库

大家好&#xff0c;小编来为大家解答以下问题&#xff0c;大一python编程题库和答案山东理工大学&#xff0c;大一python编程题库和答案解析&#xff0c;今天让我们一起来看看吧&#xff01; 单项选择题 第一章python语法基础 1. Python 3.x 版本的保留字总数是C A 27 …

Python(五十七)列表生成式

❤️ 专栏简介&#xff1a;本专栏记录了我个人从零开始学习Python编程的过程。在这个专栏中&#xff0c;我将分享我在学习Python的过程中的学习笔记、学习路线以及各个知识点。 ☀️ 专栏适用人群 &#xff1a;本专栏适用于希望学习Python编程的初学者和有一定编程基础的人。无…

Delphi 开发不一样的窗体标题栏:TTitleBarPanel

目录 TTitleBarPanel 的使用 TTitleBarPanel 的使用进阶 一、设置标题栏高度、颜色 二、个性化标题栏的关闭等按键 我们在用Delphi开发程序的时候&#xff0c;窗体的标题栏一般都是标准的windows标题栏&#xff0c;上面包括&#xff1a;程序图标、标题、最小化、最大化、关闭…

TypeC拓展设计方案|TypeC转HDMI设计方案|CS5261/CS5265芯片设计参数对比

集睿智远CS5261/CS5265都可以用于设计TypeC转HDMI方案&#xff0c;低成本TypeC扩展坞设计方案&#xff0c;而两者也有些差异&#xff1a;1.CS5261支持DP1.4输入&#xff0c;一个HDMI1.4输出&#xff0c;即HDMI输出为4K30HZ ;CS5265DP1.4到HDMI2.0转换芯片&#xff0c;即HDMI输出…

Linux之 环境变量

什么是环境变量 windows中也有个 Linux 环境变量 env linux和windows环境变量&#xff0c;功能类似的&#xff0c; windows系统的环境变量&#xff0c;在cmd中可以之间调用程序运行。这些程序的执行程序的路径&#xff0c;一般编辑在path变量中 环境变量都分全局的&#xff…

Android性能优化—ANR问题分析

一、ANR是什么&#xff1f; ANR(Application Not responding)&#xff0c;是指应用程序未响应&#xff0c;Android系统对于一些事件需要在一定的时间范围内完成&#xff0c;如果超过预定时间能未能得到有效响应或者响应时间过长&#xff0c;都会造成ANR。可以简单的理解为应用…

网络入侵探测器Pi.Alert

什么是 Pi.Alert &#xff1f; Pi.Alert 是 WIFI/LAN 入侵探测器。通过扫描连接到您的 WIFI/LAN 的设备&#xff0c;提醒您未知设备的连接。它还警告断开“始终连接”的设备。 Pi.Alert 使用了三种扫描方式 方式1&#xff1a;arp-scan。arp扫描系统实用程序用于使用 arp 帧搜索…

【雕爷学编程】MicroPython动手做(28)——物联网之Yeelight 3

知识点&#xff1a;什么是掌控板&#xff1f; 掌控板是一块普及STEAM创客教育、人工智能教育、机器人编程教育的开源智能硬件。它集成ESP-32高性能双核芯片&#xff0c;支持WiFi和蓝牙双模通信&#xff0c;可作为物联网节点&#xff0c;实现物联网应用。同时掌控板上集成了OLED…

CommunityToolkit.Mvvm8.1 viewmodel使用-旧式写法

0.说明 CommunityToolkit.Mvvm8.1有一个重大更新的功能:源生成器功能,它极大简化我们的mvvm代码 但是本篇先总结一下原写法,下篇再总结源生成器功能 1.模型定义 必须继承:ObservableObject 2.viewmodel代码实现 几个关键点: SetProperty是给属性赋值,并且通知更改通知 But…

钉钉对接打通金蝶云星空获取流程实例列表详情(宜搭)接口与其他应收单接口

钉钉对接打通金蝶云星空获取流程实例列表详情&#xff08;宜搭&#xff09;接口与其他应收单接口 对接系统钉钉 钉钉&#xff08;DingTalk&#xff09;是阿里巴巴集团专为中国企业打造的免费沟通和协同的多端平台&#xff0c;提供PC版&#xff0c;Web版和手机版&#xff0c;有考…

Linux知识点 -- VS Code远程连接服务器协助开发

Linux知识点 – VS Code远程连接服务器协助开发 文章目录 Linux知识点 -- VS Code远程连接服务器协助开发一、VS Code的使用1.使用VS Code进行C语言编译与运行2.使用VS Code进行C代码的编译与运行 二、使用VS Code连接云服务器三、使用VS Code进行GDB调试 一、VS Code的使用 1…

Redis的基础知识

目录 一、什么是Redis 二、关于Redis的一些基本知识 &#xff08;1&#xff09;set命令 &#xff08;2&#xff09;get命令 三、Redis中的一些常用命令 &#xff08;1&#xff09;keys &#xff08;2&#xff09;exists &#xff08;3&#xff09;type &#xff08;4…

vite+typescript项目 :找不到模块“./***.vue”或其相应的类型声明——解决方案

vue3ts报错&#xff1a; 找不到模块“./App.vue”或其相应的类型声明。ts(2307) 解决方法&#xff1a; 1、在src文件夹找到 vite-env.d.ts 加入以下代码&#xff1a; declare module *.vue {import type { DefineComponent } from vueconst vueComponent: DefineComponent<…

Nodejs 第六章(npx)

npx是什么 npx是一个命令行工具&#xff0c;它是npm 5.2.0版本中新增的功能。它允许用户在不安装全局包的情况下&#xff0c;运行已安装在本地项目中的包或者远程仓库中的包。 npx的作用是在命令行中运行node包中的可执行文件&#xff0c;而不需要全局安装这些包。这可以使开…

手机python编程软件怎么用,手机python编程软件下载

大家好&#xff0c;小编来为大家解答以下问题&#xff0c;手机python编程软件保存的代码在哪里&#xff0c;手机python编程软件怎么运行&#xff0c;现在让我们一起来看看吧&#xff01; 原标题&#xff1a;盘点几个在手机上可以用来学习编程的软件 前天在悟空问答的时候&#…

win上打包发包发布

地址 192.168.X.X 账号密码 zhanghaomima 连接方式&#xff1a; Win自带工具&#xff1a; 远程桌面连接 更新客户端代码 直接替换 D:\xmes\client3\elements 下的 cust-bundles 文件夹 更新mobile代码 直接替换 D:\xmes\mobile\scripts 下的fragments文件夹 更新服务端代…

【物理】带电粒子在磁场和电场中移动的 3D 轨迹研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

2023年华数杯建模思路 - 案例:粒子群算法

# 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 什么是粒子群算法&#xff1f; 粒子群算法&#xff08;Particle Swarm Optimization,PSO&#xff09;是一种模仿鸟群、鱼群觅食行为发展起来的一种进化算…