Spring容器的启动过程及留给开发者的可拓展点

一、Spring容器启动经过了哪些过程?

1、首先需要加载读取到应用环境中的配置,比如要加载的bean的class的包路径等信息。

【读取配置】

2、再就需要找到哪些类是需要spring进行类实例创建并管理的,扫描到具体的Class及Class元信息上的一些注解配置。

【找Class】

3、找到类以后,那么又该如何去创建这个类的bean,这个bean是否有一些创建层面或者配置层面的要求呢?所以这里需要去创建BeanDefinition(BD)-用于描述bean的信息。

【创建BD】

4、BD有了,那么又应该把这些BD交给谁来使用呢?所以接下来就需要注册BD到BeanFactory中。

【把BD交给BF】

5、接下来就需要BF进行根据BD的描述信息进行具体Bean的实例化了-创建bean。

【BF实例化Bean】

6、Bean实例化完成后,就需要构建Bean直接的依赖关系了,执行依赖注入。完了以后,这个bean就是一个成熟的bean了。

【依赖注入】

7、那么还有一些Bean是需要在创建完成后,执行一些初始化逻辑的,所以接下来就执行具体bean的初始化方法。

【Bean初始化逻辑】

二、Spring给我们提供了哪些拓展点?分别是用来做什么的?

Spring 框架提供了丰富的扩展点和机制,使开发者能够定制和扩展其核心功能。

1、允许开发者在应用程序上下文启动时加载一些动态逻辑

1)、ApplicationContextInitializer

不同于一般的扩展点(只要生成相应的bean即可生效)。因为该拓展点执行时,spring容器还没有初始化完成,因此想要自己的拓展生效,需要使用一下三种方式:

  • 1:手动指定

  • 2:spring-SPI,在 resources/META-INF/spring.factories 中配置
org.springframework.context.ApplicationContextInitializer=\
  com.example.hellosecurity.component.TestApplicationContextInitializer
  • 3:spring配置文件中显示配置
context.initializer.classes=com.example.hellosecurity.component.TestApplicationContextInitializer

2、允许开发者动态注册beanDefinition、修改或删除beanDefinition

1)、ImportBeanDefinitionRegistrar

  • 作用: 实现 ImportBeanDefinitionRegistrar 接口的类可以在配置类上使用 @Import 注解时,通过编程方式向容器中注册更多的 bean 定义。
  • 用途: 主要用于实现自定义的 bean 注册逻辑,可以根据特定条件向 Spring 容器中动态地注册一系列的 bean
  • 案例:mybatis-spring 中的 MapperScannerRegistrar;

2)、BeanDefinitionRegistryPostProcessor

  • 作用: 实现 BeanDefinitionRegistryPostProcessor 接口的类可以在 Spring 容器加载 bean 定义之后、实例化 bean 之前,动态地注册、修改或删除 bean 的定义。
  • 用途: 典型的应用场景包括基于条件注册 bean、动态地注册组件、替换现有的 bean 定义等。
  • 案例:mybatis-spring 中的 MapperScannerConfigurer;

3)、BeanFactoryPostProcessor

  • 作用: BeanFactoryPostProcessor 接口允许在 Spring 容器加载 bean 定义之后,在 bean 实例化之前对 bean 的定义进行修改。
  • 用途: 可以用来动态地修改或替换配置文件中的 bean 定义,例如修改 bean 的属性值、动态注入 bean 等。常见应用场景包括处理国际化消息、动态数据源的切换等。

在 Spring 的生命周期中,BeanDefinitionRegistryPostProcessor 接口定义了两个方法:postProcessBeanDefinitionRegistrypostProcessBeanFactory。它们的执行顺序是固定的,postProcessBeanDefinitionRegistry 方法会在 postProcessBeanFactory 方法之前被调用。

具体的执行顺序如下:

  1. postProcessBeanDefinitionRegistry:

    • 这个方法是 BeanDefinitionRegistryPostProcessor 接口的一部分。
    • 当 Spring 容器加载完所有的 bean 定义之后,会调用所有实现了 BeanDefinitionRegistryPostProcessor 接口的类的 postProcessBeanDefinitionRegistry 方法。
    • 在这个阶段,可以动态地注册更多的 bean 定义到 Spring 容器中,或者修改已有的 bean 定义。
  2. postProcessBeanFactory:

    • 这个方法是 BeanFactoryPostProcessor 接口的一部分,它是 BeanDefinitionRegistryPostProcessor 接口的父接口。
    • 在 postProcessBeanDefinitionRegistry 方法执行完毕后,Spring 容器会调用所有实现了 BeanFactoryPostProcessor 接口的类的 postProcessBeanFactory 方法。
    • 这个阶段通常用于进一步地修改或初始化已注册的 bean 实例。

因此,postProcessBeanDefinitionRegistry 方法先于 postProcessBeanFactory 方法被调用。这种顺序保证了在 Spring 容器加载和初始化 bean 的过程中,能够先注册和处理所有的 bean 定义,然后再对 bean 实例进行进一步的处理和配置。

3、允许开发者在具体Bean的实例化前后跟初始化逻辑前后做动态处理

1)、InstantiationAwareBeanPostProcessor

场景:

这个扩展点非常有用 ,无论是写中间件和业务中,都能利用这个特性。比如对实现了某一类接口的bean在各个生命期间进行收集,或者对某个类型的bean进行统一的设值等等。
需要注意的是,该拓展点会在每个bean的初始化阶段都会执行一遍。

  • postProcessBeforeInstantiation:实例化bean之前,相当于new这个bean之前
  • postProcessAfterInstantiation:实例化bean之后,相当于new这个bean之后
  • postProcessPropertyValues:bean已经实例化完成,在属性注入时阶段触发,@Autowired,@Resource等注解原理基于此方法实现
  • postProcessBeforeInitialization:初始化bean之前,相当于把bean注入spring上下文之前
  • postProcessAfterInitialization:初始化bean之后,相当于把bean注入spring上下文之后

2)、BeanPostProcessor

Bean 的后置处理器,常用与对 Bean 的一些加工处理,比如最常见的动态代理。可以对 Bean 进行处理,比如替换等操作。

  • postProcessBeforeInitialization:bean初始化逻辑之前,相当于把bean注入spring上下文之前。
  • postProcessAfterInitialization:bean初始化逻辑之后,相当于把bean注入spring上下文之后。

3)、BeanFactoryAware

实例化完成并且属性设置完成,初始化逻辑执行之前

场景:

可以在bean实例化之后,但还未初始化之前,拿到 BeanFactory,在这个时候,可以对每个bean作特殊化的定制。 也或者可以把BeanFactory拿到进行缓存,后续逻辑使用。

4)、ApplicationContextAwareProcessor

该类本身并没有扩展点,但是该类内部却有6个扩展点可供实现 ,这些类触发的时机在bean实例化之后,初始化之前

private void invokeAwareInterfaces(Object bean) {
		if (bean instanceof EnvironmentAware) {
			((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
		}
		if (bean instanceof EmbeddedValueResolverAware) {
			((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
		}
		if (bean instanceof ResourceLoaderAware) {
			((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
		}
		if (bean instanceof ApplicationEventPublisherAware) {
			((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
		}
		if (bean instanceof MessageSourceAware) {
			((MessageSourceAware) bean).setMessageSource(this.applicationContext);
		}
		if (bean instanceof ApplicationStartupAware) {
			((ApplicationStartupAware) bean).setApplicationStartup(this.applicationContext.getApplicationStartup());
		}
		if (bean instanceof ApplicationContextAware) {
			((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
		}
	}
  • EnvironmentAware:用于获取EnviromentAware的一个扩展类,这个变量非常有用, 可以获得系统内的所有参数。当然个人认为这个Aware没必要去扩展,因为spring内部都可以通过注入的方式来直接获得。
  • EmbeddedValueResolverAware:用于获取StringValueResolver的一个扩展类, StringValueResolver用于获取基于String类型的properties的变量,一般我们都用@Value的方式去获取,如果实现了这个Aware接口,把StringValueResolver缓存起来,通过这个类去获取String类型的变量,效果是一样的。
  • ResourceLoaderAware:用于获取ResourceLoader的一个扩展类,ResourceLoader可以用于获取classpath内所有的资源对象,可以扩展此类来拿到ResourceLoader对象。
  • ApplicationEventPublisherAware:用于获取ApplicationEventPublisher的一个扩展类,ApplicationEventPublisher可以用来发布事件,结合ApplicationListener来共同使用,下文在介绍ApplicationListener时会详细提到。这个对象也可以通过spring注入的方式来获得。
  • MessageSourceAware:用于获取MessageSource的一个扩展类,MessageSource主要用来做国际化。
  • ApplicationContextAware:用来获取ApplicationContext的一个扩展类,ApplicationContext应该是很多人非常熟悉的一个类了,就是spring上下文管理器,可以手动的获取任何在spring上下文注册的bean,我们经常扩展这个接口来缓存spring上下文,包装成静态方法。同时ApplicationContext也实现了BeanFactoryMessageSourceApplicationEventPublisher等接口,也可以用来做相关接口的事情。

5)、@PostConstruct

Bean初始化逻辑执行之前执行。

其作用是在bean的初始化阶段,如果对一个方法标注了@PostConstruct,会先调用这个方法。这里重点是要关注下这个标准的触发点,这个触发点是postProcessBeforeInitialization之后,InitializingBean.afterPropertiesSet之前。

CommonAnnotationBeanPostProcessor 是一个后处理器,它实现了 BeanPostProcessor 接口。在容器实例化 bean 的过程中,CommonAnnotationBeanPostProcessor 会检查每个 bean 类型中标记了 @PostConstruct 注解的方法,并在适当的时机调用这些方法。

@PostConstruct 注解的调用逻辑是在 Spring IOC 容器实例化和初始化 bean 后,由 CommonAnnotationBeanPostProcessor 处理器负责触发调用的。

6)、InitializingBean

顾名思义,也是用来初始化bean的。InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候都会执行该方法。这个扩展点的触发时机在postProcessAfterInitialization之前。

用户实现此接口,来进行系统启动的时候一些业务指标的初始化工作。

7)、SmartInitializingSingleton

  • 作用: 在 Spring 容器中所有的 singleton bean 都初始化完成后,调用该接口的方法,可以执行一些特定的操作。
  • 方法: afterSingletonsInstantiated(),通过这个方法可以在所有 singleton bean 初始化完成后执行自定义逻辑,通常用于一次性初始化、缓存预热等操作。

4、允许开发者监听Spring容器发布的一些事件,做自己的监听处理逻辑

1)、ApplicationListener

  • 作用: 用于处理 Spring 容器中发布的事件。开发者可以实现这个接口来监听和响应特定类型的事件,如上下文加载完成事件、Bean 初始化完成事件等。
  • 方法: onApplicationEvent(ApplicationEvent event),通过这个方法可以处理所关心的事件,实现自定义的业务逻辑响应。

准确的说,这个应该不算spring&springboot当中的一个扩展点,ApplicationListener可以监听某个事件的event,触发时机可以穿插在业务方法执行过程中,用户可以自定义某个业务事件。但是spring内部也有一些内置事件,这种事件,可以穿插在启动调用中。我们也可以利用这个特性,来自己做一些内置事件的监听器来达到和前面一些触发点大致相同的事情。

接下来罗列下spring主要的内置事件:

  • ContextRefreshedEvent ApplicationContext 被初始化或刷新时,该事件被发布。这也可以在ConfigurableApplicationContext接口中使用 refresh()方法来发生。此处的初始化是指:所有的Bean被成功装载,后处理Bean被检测并激活,所有Singleton Bean 被预实例化,ApplicationContext容器已就绪可用。
  • ContextStartedEvent 当使用 ConfigurableApplicationContext (ApplicationContext子接口)接口中的 start() 方法启动 ApplicationContext时,该事件被发布。你可以调查你的数据库,或者你可以在接受到这个事件后重启任何停止的应用程序。
  • ContextStoppedEvent 当使用 ConfigurableApplicationContext接口中的 stop()停止ApplicationContext 时,发布这个事件。你可以在接受到这个事件后做必要的清理的工作
  • ContextClosedEvent 当使用 ConfigurableApplicationContext接口中的 close()方法关闭 ApplicationContext 时,该事件被发布。一个已关闭的上下文到达生命周期末端;它不能被刷新或重启
  • RequestHandledEvent 这是一个 web-specific 事件,告诉所有 bean HTTP 请求已经被服务。只能应用于使用DispatcherServlet的Web应用。在使用Spring作为前端的MVC控制器时,当Spring处理用户请求结束后,系统会自动触发该事件

5、销毁bean拓展点

1)、DisposableBean

这个扩展点也只有一个方法:destroy(),其触发时机为当此对象销毁时,会自动执行这个方法。比如说运行applicationContext.registerShutdownHook时,就会触发这个方法。

有些重要任务,需要优雅关闭线程池等

参考文献:

https://juejin.cn/post/7250667613020569637

Spring 扩展点的执行顺序 - 脉脉

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

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

相关文章

【漏洞复现】电信网关配置管理系统——命令执行

声明:本文档或演示材料仅供教育和教学目的使用,任何个人或组织使用本文档中的信息进行非法活动,均与本文档的作者或发布者无关。 文章目录 漏洞描述漏洞复现测试工具 漏洞描述 电信网关配置管理系统是一个用于管理和配置电信网关设备的软件系…

为什么Modbus链接/从机不通?From 摩尔信使MThings

为了回应用户平日里关于摩尔信使(MThings)使用过程中最常见的问题,包括“网络链接连不上”、“为什么不能增加串口”和“为什么从机不通”,我们在此统一介绍解决方法。 1、具备哪些通信能力 支持串口和网络两种通信方式。 需要…

开箱即用的fastposter海报生成器

什么是 fastposter ? fastposter 海报生成器是一款快速开发海报的工具。只需上传一张背景图,在对应的位置放上组件(文字、图片、二维码、头像)即可生成海报。 点击代码直接生成各种语言 SDK 的调用代码,方便快速开发。 软件特性&…

NSSCTF-Web题目18(反序列化)

目录 [NISACTF 2022]babyserialize 1、题目 2、知识点 3、思路 [SWPUCTF 2022 新生赛]ez_ez_unserialize 4、题目 5、知识点 6、思路 [NISACTF 2022]babyserialize 1、题目 2、知识点 反序列化、绕过过滤、命令执行 3、思路 <?php include "waf.php";…

正版软件 | R-Studio Corporate:企业级数据恢复的终极解决方案

数据是企业的生命线&#xff0c;而数据丢失可能随时威胁到企业的正常运营。R-Studio Corporate 是一款专为企业环境设计的多功能数据恢复软件&#xff0c;确保您在面临数据危机时&#xff0c;能够迅速、高效地恢复宝贵数据。 跨平台操作&#xff0c;灵活恢复 R-Studio Corporat…

雷池软硬件部署模式

硬件模式&#xff1a; 硬件反向代理 VRRP 硬件透明代理 雷池硬件设备 硬件透明墙 硬件流量镜像 流量镜像具备旁路阻断攻击的能力 软件模式 模式对比

shopify入门教程-应用开发(二)

4.内网穿透 为什么要用这个&#xff0c;就是把电脑上的开发内容通过内网穿透显示到你的开发店铺上。这里的内网穿透我用了ngrok,花生壳&#xff0c;但都不如shopify官方推荐的cloudflare好用。所以这里我也推荐cloudflare。 运用内网穿透2个步骤 把app运行起来 ​​​​​​​…

正点原子rk3588编译sdk

1、编译SDK 1.1 安装 RK3588 Linux SDK .repo/repo/repo sync -l -j101.2 SDK 工程目录介绍 app&#xff1a;存放上层应用 app&#xff0c;包括 Qt 应用程序&#xff0c;以及其它的 C/C应用程序。 buildroot&#xff1a;基于 buildroot 开发的根文件系统。 debian&#xff1…

【shell脚本实战案例】数据磁盘初始化

文章目录 一、案例应用场景二、案例需求三、案例算法四、代码实现五、实现验证 &#x1f308;你好呀&#xff01;我是 山顶风景独好 &#x1f388;欢迎踏入我的博客世界&#xff0c;能与您在此邂逅&#xff0c;真是缘分使然&#xff01;&#x1f60a; &#x1f338;愿您在此停留…

已解决javax.xml.bind.MarshalException:在RMI中,参数或返回值无法被编组的正确解决方法,亲测有效!!!

已解决javax.xml.bind.MarshalException&#xff1a;在RMI中&#xff0c;参数或返回值无法被编组的正确解决方法&#xff0c;亲测有效&#xff01;&#xff01;&#xff01; 目录 问题分析 出现问题的场景 服务器端代码 客户端代码 报错原因 解决思路 解决方法 1. 实现…

IBCS 虚拟专线用哪些特点!

当今数字化时代&#xff0c;高效、稳定、安全的网络连接对于企业和个人来说至关重要。IBCS 虚拟专线作为一种创新的网络解决方案&#xff0c;凭借其众多显著的优势&#xff0c;正逐渐成为众多用户的首选。 IBCS 虚拟专线最突出的优势之一在于其卓越的网络性能。它通过优化网络路…

DC-DC产品设计PCB注意事项

DC-DC的电路比LDO会复杂很多&#xff0c;噪声也更大&#xff0c;布局和layout要求更高&#xff0c;layout的好坏直接影响DC-DC的性能&#xff0c;所以了解DC-DC的layout至关重要。 一、Bad Layout EMI&#xff0c;DC-DC的SW管脚上面会有较高的dv/dt&#xff0c; 比较高的dv/d…

Python+Pytest+Allure+Yaml+Jenkins+GitLab接口自动化测试框架详解

PythonPytestAllureYaml接口自动化测试框架详解 编撰人&#xff1a;CesareCheung 更新时间&#xff1a;2024.06.20 一、技术栈 PythonPytestAllureYamlJenkinsGitLab 版本要求&#xff1a;Python3.7.0,Pytest7.4.4,Allure2.18.1,PyYaml6.0 二、环境配置 安装python3.7&…

kafka 消费者 API 使用总结

前言 应用程序使用KafkaConsumer向Kafka订阅主题&#xff0c;并从订阅的主题中接收消息。不同于从其他消息系统读取数据&#xff0c;从Kafka读取数据涉及一些独特的概念和想法。如果不先理解这些概念&#xff0c;则难以理解如何使用消费者API。本文将先解释这些重要的概念&…

你还能顶几天?

A总&#xff1a;你还能顶几天&#xff1f; 汪汪队&#xff1a;顶到奉命撤退的那一天 A总&#xff1a;你在这守散钱点几十年了&#xff0c;从来没跟我提过任何的要求&#xff0c;难道你不困难吗&#xff1f; 汪汪队&#xff1a;有困难&#xff0c;但是我提了有什么用呢&#…

【密码学】面向小白的古典密码基础入门笔记

目录 Mindmap 前言 破译方法 三类古典密码 替换密码 分类 单表替换密码 凯撒密码 简单替换密码 仿射密码 普莱费尔密码 培根密码 猪圈密码 摩斯密码 多表替换密码 维吉尼亚密码 移位密码 滚筒密码 栅栏密码 Mindmap 前言 1.所有古典密码都已不安全 2.密…

晋级国赛!卓翼飞思技术引领,助力辽宁赛区机器人及人工智能大赛圆满收官

近日&#xff0c;第二十六届中国机器人及人工智能大赛—辽宁赛区选拔赛在大连海事大学圆满收官。本次大赛吸引来自辽宁工业大学、大连理工大学等知名高校的10余支队伍参与&#xff0c;充分展现各高校在机器人及人工智能领域的深厚实力和创新精神。其中&#xff0c;由卓翼飞思实…

用ChatGPT快速打造一个专业WordPress网站

作为一个使用HostEase多年的老用户&#xff0c;我想和大家分享一下如何利用HostEase和ChatGPT快速构建一个WordPress网站的经验。这不仅仅是一个简单的操作步骤&#xff0c;更是一次从零到有的实战经历。希望我的分享能给你们带来一些实用的帮助。 获取主机服务和域名 首先&a…

解锁音乐潮流:使用TikTok API获取平台音乐信息

一、引言 TikTok&#xff0c;作为全球领先的短视频社交平台&#xff0c;不仅为用户提供了展示自我、分享生活的舞台&#xff0c;还为用户带来了丰富多样的音乐体验。在TikTok上&#xff0c;音乐与视频内容的结合&#xff0c;为用户带来了全新的视听盛宴。对于音乐制作人、品牌…

阿里云开启ssl证书过程记录 NGINX

&#x1f91e;作者简介&#xff1a;大家好&#xff0c;我是思无邪&#xff0c;2024 毕业生&#xff0c;某厂 Go 开发工程师.。 &#x1f402;我的网站&#xff1a;https://www.yishanicode.top/ &#xff0c;持续更新&#xff0c;希望对你有帮助。 &#x1f41e;如果文章或网站…