探索InitializingBean:Spring框架中的隐藏宝藏

​🌈 个人主页:danci_
🔥 系列专栏:《设计模式》《MYSQL》
💪🏻 制定明确可量化的目标,坚持默默的做事。


✨欢迎加入探索MYSQL索引数据结构之旅✨

    👋 Spring框架的浩瀚海洋中,InitializingBean如同一颗闪闪发光的珍珠,等待被你发现。这一隐藏的宝藏,能够让你的Spring应用在初始化时更灵活、可控。本篇文章将带你深入探索InitializingBean的奥秘,揭示其在Spring生命周期中的重要作用,让你的开发之路更加顺畅高效。准备好开启这段奇妙的探索之旅了吗?让我们一起揭开InitializingBean的神秘面纱吧!


目录

一、环境

二、示例

2.1、说明

三、执行过程

3.1 源码解析


一、环境

  • jdk: 1.8
  • spring-boot: 2.7.1

二、示例

@Component
public class TestInit implements InitializingBean {
    
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("调用TestInit.afterPropertiesSet方法");
    }
}

运行结果:

2.1、说明

  • springboot提供InitializingBean接口,用户可以实现之来自定义初始化操作
  • 实现InitializingBean接口重写afterPropertiesSet方法并注册到spring容器中,创建实例后即可触发执行自定义初始化操作

三、执行过程

       一直很好奇和想弄清楚什么时候调扩展点afterPropertiesSet()方法呢?用什么方式调用?

为满足好奇,启动一个简单springboot项目跟踪源码执行过程如下:

   

3.1 源码解析

1. Springboot项目启动入口SpringApplication.run

public ConfigurableApplicationContext run(String... args) {
        // 记录启动开始时间
		long startTime = System.nanoTime();
		DefaultBootstrapContext bootstrapContext = createBootstrapContext();
        // 准备ApplicationContext
		ConfigurableApplicationContext context = null;
		configureHeadlessProperty();
        // 从spring.factories 中获取监听器
		SpringApplicationRunListeners listeners = getRunListeners(args);
        // 发布事件
		listeners.starting(bootstrapContext, this.mainApplicationClass);
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            // 环境参数
			ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
            // 配置设置进系统参数
			configureIgnoreBeanInfo(environment);
            // 输出banner
			Banner printedBanner = printBanner(environment);
            // 创建applicationContext IOC容器
			context = createApplicationContext();
			context.setApplicationStartup(this.applicationStartup);
            // 准备上下文IOC容器,设置了一系列的属性值
			prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
            // 308行,启动spring容器
			refreshContext(context);
            // 刷新后的处理
			afterRefresh(context, applicationArguments);
			Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
			}
            // 发布事件
			listeners.started(context, timeTakenToStartup);
            // 调用 runner,实现了 ApplicationRunner或CommandLineRunner 的接口
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, listeners);
			throw new IllegalStateException(ex);
		}
		try {
			Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
			listeners.ready(context, timeTakenToReady);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

这里关注调refreshContext(context)来启动容器

 2. SpringApplication.refreshContext启动容器

protected void refresh(ConfigurableApplicationContext applicationContext) {
        // 734行
		applicationContext.refresh();
	}

然后调AbstractApplicationContext.refresh()来启动Spring容器

3. AbstractApplicationContext.refresh()启动Spring容器

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

        // 启动容器前准备。启动时间、状态和环境等
        prepareRefresh();

        // 告诉子类刷新内部bean工厂
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // 准备在上下文中使用bean工厂
        prepareBeanFactory(beanFactory);

        try {
            // 允许在上下文子类中对bean工厂进行后处理。
            postProcessBeanFactory(beanFactory);

            StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
            // 调用在上下文中注册为bean的工厂处理器
            invokeBeanFactoryPostProcessors(beanFactory);

            // 注册拦截bean创建的bean处理器。
            registerBeanPostProcessors(beanFactory);
            beanPostProcess.end();

            // 为此上下文初始化消息源。
            initMessageSource();

            // 初始化事件广播器
            initApplicationEventMulticaster();

            // 初始化特定上下文子类中的其他特殊bean。
            onRefresh();

            // 注册事件监听器
            registerListeners();

            // 583行,实例化所有剩余的(非lazy-init)单例
            finishBeanFactoryInitialization(beanFactory);

            // 完成启动操作
            finishRefresh();
        }

        catch (BeansException ex) {
           ...
        }

        finally {
            resetCommonCaches();
            contextRefresh.end();
        }
    }
}

列出了Spring容器启动的整个过程。本例关注InitializingBean扩展点,其它内容不详细解读。 实例化所有剩余的(非lazy-init)单例 finishBeanFactoryInitialization(beanFactory);

4. AbstractApplicationContext.finishBeanFactoryInitialization(...)初始化(非lazy-init)单例

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
    ...

    // 918行,实例化所有剩余的(非lazy-init)单例。
    beanFactory.preInstantiateSingletons();
}

调bean工厂类的preInstantiateSingletons方法来实例化bean

5. ConfigurableListableBeanFactory.preInstantiateSingletons()实例化bean

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

    // 955行,获取bean
    getBean(beanName);

    ...
}

这里由调父父类AbstractBeanFactory.getBean来获取bean

6.AbstractBeanFactory.getBean(beanName)获取bean

public Object getBean(String name) throws BeansException {
    // 208行
    return doGetBean(name, null, null, false);
}

然后调本类doGetBean(...)方法获取bean

7. AbstractBeanFactory.doGetBean获取bean

protected <T> T doGetBean(
        String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
        throws BeansException {
        ...
            // 332行,创建bean实例
            if (mbd.isSingleton()) {
                sharedInstance = getSingleton(beanName, () -> {
                    try {
                        return createBean(beanName, mbd, args);
                    }
                    catch (BeansException ex) {
                        ...
                    }
                });
                ...
            }

        ...
    }

    return adaptBeanInstance(name, beanInstance, requiredType);
}

然后调子类的createBean来创建实例

8.AbstractAutowireCapableBeanFactory.createBean(...)创建实例

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
        throws BeanCreationException {
    ...
    // 542行
    doCreateBean(beanName, mbdToUse, args);
    ...
}

调本类doCreateBean

9.AbstractAutowireCapableBeanFactory.doCreateBean(...)执行创建bean

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

    ...
    // 实例化 Bean
    // 解决循环依赖问题, 是否允许循环依赖 等
    ...
        // 属性装配, 即自动注入
        populateBean(beanName, mbd, instanceWrapper);

        // 620行,应用工厂回调以及初始化方法和bean后处理程序。
        // 例如init-method、InitializingBean 接口、BeanPostProcessor 接口
        exposedObject = initializeBean(beanName, exposedObject, mbd);
   ...
}

10. AbstractAutowireCapableBeanFactory.initializeBean处理初始化完成处理

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    ...
        // 方法回调
        invokeInitMethods(beanName, wrappedBean, mbd);
    ...
}

11. AbstractAutowireCapableBeanFactory.invokeInitMethods实例的方法回调处理

protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
        throws Throwable {

    boolean isInitializingBean = (bean instanceof InitializingBean);
    // 判断为InitializingBean的实现类,且重写afterPropertiesSet方法,则调afterPropertiesSet方法
    if (isInitializingBean && (mbd == null || !mbd.hasAnyExternallyManagedInitMethod("afterPropertiesSet"))) {
        ...
        ((InitializingBean) bean).afterPropertiesSet();
        ...
    }
    ...
}

到此,探索InitializingBean已完。

ps: 以上是研读源码加上翻阅许多文献理解的总结,如有错误或不足的地方,欢迎指出,欢迎留言交流。我会继续努力学习和分享更多有干货的内容。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Javascript常见数据结构和设计模式

在JavaScript中&#xff0c;常见的数据结构包括两大类&#xff1a;原始数据类型&#xff08;Primitive Types&#xff09;和对象类型&#xff08;Object Types&#xff09;。对象类型又可以进一步细分为多种内置对象、数组、函数等。下面是一些JavaScript中常见的数据结构&…

《算法笔记》总结No.4——散列

散列的英文名是hash&#xff0c;即我们常说的哈希~该知识点在王道408考研的教材里面属于查找的范围。即便各位并无深入了解过&#xff0c;也听说过散列是一种更高效的查找方法。 一.引例 先来考虑如下一个假设&#xff1a;设有数组M和N分别如下&#xff1a; M[10][1,2,3,4,5,6…

【Spring AOP 源码解析前篇】什么是 AOP | 通知类型 | 切点表达式| AOP 如何使用

前言&#xff08;关于源码航行&#xff09; 在准备面试和学习的过程中&#xff0c;我阅读了还算多的源码&#xff0c;比如 JUC、Spring、MyBatis&#xff0c;收获了很多代码的设计思想&#xff0c;也对平时调用的 API 有了更深入的理解&#xff1b;但过多散乱的笔记给我的整理…

自动化设备上位机设计 四

目录 一 设计原型 二 后台代码 一 设计原型 二 后台代码 using SimpleTCP; using SqlSugar; using System.Text;namespace 自动化上位机设计 {public partial class Form1 : Form{SqlHelper sqlHelper new SqlHelper();SqlSugarClient dbContent null;bool IsRun false;i…

【机器学习实战】Datawhale夏令营:Baseline精读笔记2

# AI夏令营 # Datawhale # 夏令营 在原有的Baseline上除了交叉验证&#xff0c;还有一种关键的优化方式&#xff0c;即特征工程。 如何优化特征&#xff0c;关系着我们提高模型预测的精准度。特征工程往往是对问题的领域有深入了解的人员能够做好的部分&#xff0c;因为我们要…

链式二叉树oj题

1.输入k &#xff0c;找第k层节点个数 int TreeKlevel(BTNode*root,int k) {if (root NULL) {return 0;}if (k 1) {return 1;}return TreeKlevel(root->left, k - 1)TreeKlevel(root->right, k - 1); } 在这里我们要确定递归子问题&#xff0c;第一个就是NULL时返回&…

强化学习中的Q-Learning和Sarsa算法详解及实战

强化学习&#xff08;Reinforcement Learning, RL&#xff09;是一种通过与环境交互来学习最优策略的机器学习方法。在强化学习中&#xff0c;Q-Learning和Sarsa是两种重要的基于值的算法。本文将详细讲解这两种算法&#xff0c;并通过实际代码示例展示其应用。 1. 强化学习基…

algorithm算法库学习之——不修改序列的操作

algorithm此头文件是算法库的一部分。本篇介绍不修改序列的操作函数。 不修改序列的操作 all_ofany_ofnone_of (C11)(C11)(C11) 检查谓词是否对范围中所有、任一或无元素为 true (函数模板) for_each 应用函数到范围中的元素 (函数模板) for_each_n (C17) 应用一个函数对象到序…

Vue88-Vuex中的mapActions、mapMutations

一、mapMutations的调用 此时结果不对&#xff0c;因为&#xff1a;若是点击事件不传值&#xff0c;默认传的是event&#xff01;&#xff0c;所以&#xff0c;修改如下&#xff1a; 解决方式1&#xff1a; 解决方式2&#xff1a; 不推荐&#xff0c;写法麻烦&#xff01; 1-…

排序算法简述(第八jiang)

目录 排序 选择排序 O(n2) 不稳定&#xff1a;48429 归并排序 O(n log n) 稳定 插入排序 O(n2) 堆排序 O(n log n) 希尔排序 O(n log2 n) 图书馆排序 O(n log n) 冒泡排序 O(n2) 优化&#xff1a; 基数排序 O(n k) 快速排序 O(n log n)【分治】 不稳定 桶排序 O(n…

【图解大数据技术】Flume、Kafka、Sqoop

【图解大数据技术】Flume、Kafka、Sqoop FlumeFlume简介Flume的应用场景 KafkaKafka简介Kafka架构Flume与Kafka集成 SqoopSqoop简介Sqoop原理sqoop搭配任务调度器实现定时数据同步 Flume Flume简介 Flume是一个数据采集工具&#xff0c;多用于大数据技术架构下的日志采集。 …

设计模式之模版方法

模版方法介绍 模版方法&#xff08;Template Method&#xff09;模式是一种行为型设计模式&#xff0c;它定义了一个操作&#xff08;模板方法&#xff09;的基本组合与控制流程&#xff0c;将一些步骤&#xff08;抽象方法&#xff09;推迟到子类中&#xff0c;使得子类可以在…

C语言下的文件详解

主要内容 文件概述文件指针文件的打开与关闭文件的读写 文件 把输入和输出的数据以文件的形式保存在计算机的外存储器上&#xff0c;可以确保数据能随时使用&#xff0c;避免反复输入和读取数据 文件概述 文件是指一组相关数据的有序集合 文件是存储数据的基本单位&#…

# mysql 中文乱码问题分析

mysql 中文乱码问题分析 一、问题分析&#xff1a; MySQL 中文乱码通常是因为字符集设置不正确导致的。MySQL 有多种字符集&#xff0c;如 latin1、utf8、utf8mb4 等&#xff0c;如果在创建数据库、数据表或者字段时没有指定正确的字符集&#xff0c;或者在插入数据时使用了与…

关于Java异常机制及finally关键字的详解

异常机制(Exception) 软件程序在运行过程中&#xff0c;非常可能遇到异常问题。常见的异常&#xff1a; 1、用户输入错误 2、设备错误 3、硬件问题&#xff0c;例如打印机关掉、服务器问题 4、物理限制&#xff1a;磁盘满了 Java是采用面向对象的方式来处理异常的。 处理过程…

哈希表——C语言

哈希表&#xff08;Hash Table&#xff09;是一种高效的数据结构&#xff0c;能够在平均情况下实现常数时间的查找、插入和删除操作。 哈希表的核心是哈希函数&#xff0c;哈希函数是一个将输入数据&#xff08;通常称为“键”或“key”&#xff09;转换为固定长度的整数的函数…

使用vue3-treeselect问题

1.当vue3-treeselect是单选时&#xff0c;使用watch监听绑定value&#xff0c;无法监听到值清空 对照后将:value改为v-model&#xff0c;如图 2.使用vue3-treeselect全部清空按钮如何置空select的值&#xff0c;使用watch监听 多选&#xff1a;pageInfo.officeName(val) {// …

【Linux进阶】文件系统6——理解文件操作

目录 1.文件的读取 1.1.目录 1.2.文件 1.3.目录树读取 1.4.文件系统大小与磁盘读取性能 2.增添文件 2.1.数据的不一致&#xff08;Inconsistent&#xff09;状态 2.2.日志式文件系统&#xff08;Journaling filesystem&#xff09; 3.Linux文件系统的运行 4、文件的删…

Java--方法重写

1.方法的重写首先需要有继承关系&#xff0c;且为子类重写父类的方法 2.方法名必须相同 3.参数列表必须相同 4.修饰符的范围可以扩大但不能缩小&#xff0c;public>protected>default>private,即父类的属性可以从private改为public&#xff0c;但不能反过来 5.抛出…

python爬虫入门(四)之Beautiful Soup库

一、什么是Beautiful Soup库 1、Beautiful Soup库是用来做HTML解析的库 Beautiful Soup把看起来复杂的HTML内容&#xff0c;解析成树状结构&#xff0c;让搜索和修改HTML结构变得更容易 2、第三方库&#xff0c;先安装 终端输入pip install bs4 from bs4 import Beautiful…