介绍Sping Boot的5个扩展点

1、初始化器ApplicationContextInitializer

我们在启动Spring Boot项目的时候,是执行这样一个方法来启动的

image.png

我们一层一层往下点,最终发现执行的是这个方法
image.png

所以我们在启动项目的时候也可以这样启动 new SpringApplication(SpringbootExtensionPointApplication.class).run(args); 老的只是包装了一个静态方法,实际底层就是实例化一个SpringApplication对象,然后调用它的run方法。
image.png

我们进到构造方法里看下,红框里面标出来的,一个是设置初始化器,一个是设置监听器。
image.png

点进去看下,这两个底层调的方法是一样,就是传入一个类型,通过Spring SPI的方式查找这个类型的实现类,SPI我在《Sping Boot启动原理精讲第二讲》 的时候介绍过,这里就不再赘述了哦。

image.png

打个断点,启动一下,此时有7个上下文初始器,这是系统自带的,配置在不同的spring.factories文件中。
image.png

现在我要新建一个自己的初始化器

image.png

此时为了能够让Spring Boot在启动的时候能够扫描到我创建的初始化器应该怎么办? 就是在spring.factories文件中添加一下,注册一下,这样就能扫描到,这个就是SPI。SPI 全称为 Service Provider Interface,是一种服务发现机制。

image.png

那么这时候我们再启动一下Spring Boot,发现自己创建的ApplicationContextInitializer也已经注册上来了,变成8个了。

image.png

把断点放掉,在控制台中也打印出了这句话,那么这个就是第一个扩展点ApplicationContextInitializer
image.png

定义了这8个初始化器,那一定是有地方在调它们的,不然怎么会打印出来呢,那具体在什么地方调的,我们在自己的初始化器的地方打断点,看到已经进来了,然后看下方的堆栈信息,这个就是调用的地方。

image.png

原来是调用了run()方法中的prepareContext()方法中的applyInitializers()方法,在这个方法中for循环的调用各个初始化器的initialize()方法,从而我们就能看到把Jack的ApplicationContextInitializer这句话给打印出来了。那么这个查找的方法就是以结果为导向来反查调用方,因为你正查的话是很难找到,很难记住的,这个方法希望大家学习到。

image.png

那么最后来看下我们第一个扩展点所处的位置

image.png

初始化器可以做一些事情,比如Environment对象设置一些变量配置。

2、监听器ApplicationListener

在上面构造函数里我们发现除了有setInitializers,还有setListeners,那么这个listeners其实也是一个扩展点。

image.png

那么什么是监听器,就是这样的,这个其实就是观察者模式,ApplicationEventMulticaster发布事件,各个Listener监听事件。

image.png

和初始化器一样,现在我们自定义两个监听器,一个是Starting,一个是Started,括号里面的是泛型,这个是一定要写的,如果不写的话就是不管什么类型的Event都会监听。

image.png

image.png

这个泛型是上限为ApplicationEvent类型的Event,具体的实现类有很多个,Starting和Started只是其中两个。
image.png

image.png

现在我们还是把这两个监听器通过SPI的方式加到配置中去

image.png

好,运行一下,我们看到这两句话已经打印出来了

image.png

和监听器一样,既然能够打印出来,那肯定是有地方在调用,所以我们在JackStartingApplicationListener打个断点,然后看下堆栈信息

image.png

我们可以看到在SpringApplication run()方法的listeners.starting()开始进去发送ApplicationStartingEvent广播事件,最后发布出去,由我们自己编写的事件监听器接收到。
image.png

image.png

那么ApplicationStartedEvent事件也是一样的道理,通过打断点的方式来找到它的调用方,最后我们再来看下此时的扩展点图

image.png

3、Runner

我们看到在listeners.started()后面有个callRunners
image.png

我们点进去看下,它其实就是从容器中获取两种类型的Runner,一种是ApplicationRunner,还有一种是CommandLineRunner,然后for循环的对它们进行调用,那么其实这个也是一个扩展点

image.png

我们来写一个自己的Runner

image.png

运行一下,看下打印出来了
image.png

那么这个Runner的一般应用场景就是资源释放清理或者做注册中心,因为执行到Runner的时候项目已经启动完毕了,这时候就可以注册到注册中心上去了。此时我们再看下扩展点图。

image.png

4、BeanFactoryPostProcessor

我们看下run方法里的refreshContext()方法

image.png

这个方法底层会调spring里面的refresh()方法,这个方法里面就会做对容器的初始化。红框里的invokeBeanFactoryPostProcessors()方法,这里也有一个扩展点,就是BeanFactoryPostProcessor,执行对BeanFactory的后置处理。 Spring Boot解析配置成BeanDefinition的操作也是在此方法中。

image.png

现在我们来创建一个自己的BeanFactoryPostProcessor,这个方法里面可以修改beanFactory的属性,beanfactory里面有BeanDefinition,可以修改BeanDefinition里面的值。BeanDefinition是一个bean的元数据的信息,有多少个bean就有多少个BeanDefinition。

image.png

运行一下,也打印出来了

image.png

此时我们再看下扩展点图,越来越完善了。

image.png

5、BeanPostProcessor

最后介绍的是BeanPostProcessor,它在通过反射构造函数进行bean实例化之后执行,那么红框里面标出来的registerBeanPostProcessors()方法就是向BeanFactory中注册beanpostprocessor,用于后续bean创建的拦截操作。

image.png

现在我们来创建一个自己的BeanPostProcessor,一共有两个方法,postProcessBeforeInitialization和postProcessAfterInitialization,不过我们一般用postProcessAfterInitialization,在bean调用反射构造函数实例化之后执行。著名的应用场景AOP底层就是通过BeanPostProcessor来实现的。

image.png

现在我在postProcessAfterInitialization上打个断点,看下堆栈信息是在哪里调用的

image.png

是在finishBeanFactoryInitialization()方法处调用的
image.png

后记

最后我来把扩展点图补充完整,如下所示,很清晰明了,在什么时候调用了什么,我们自己开发的时候结合应用场景,在什么时候要干什么事,就知道要创建什么类型的扩展点了。

image.png

本文前三个讲的是Spring Boot里面自己有的扩展点,后两个因为Spring Boot底层调的是Spring的源码,所以属于Spring里面的扩展点,所以如果这么算的话Spring里面的扩展点还有很多扩展点,比如InitializeBean、Aware等等这里都没讲,等待大家去发掘,谢谢观看 ~

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

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

相关文章

无脑入门pytorch系列(二)—— torch.mean

本系列教程适用于没有任何pytorch的同学(简单的python语法还是要的),从代码的表层出发挖掘代码的深层含义,理解具体的意思和内涵。pytorch的很多函数看着非常简单,但是其中包含了很多内容,不了解其中的意思…

Kindling the Darkness: A Practical Low-light Image Enhancer论文阅读笔记

这是ACMMM2019的一篇有监督暗图增强的论文,KinD其网络结构如下图所示: 首先是一个分解网络分解出R和L分量,然后有Restoration-Net和Adjustment-Net分别去对R分量和L分量进一步处理,最终将处理好的R分量和L分量融合回去。这倒是很常…

linux-MySQL的数据目录

总结: window中的my.ini linux 中 /etc/my.cnfwindow中的D:\soft\mysql-5.7.35-winx64\data linux 中 /var/lib/mysql 1.查找与mysql有关的目录 find / -name mysql [rootVM-4-6-centos etc]# find / -name mysql /opt/mysql /etc/selinux/targeted/tmp/modul…

Mybatis引出的一系列问题-JDBC 的探究

1 引入对JDBC的理解-1 一般来说,Java应用程序访问数据库的过程是: 装载数据库驱动程序;通过jdbc建立数据库连接;访问数据库,执行sql语句;断开数据库连接。 Public void FindAllUsers(){//1、装载sqlserve…

Paleobotany——北美中生代-新生代化石植物总目(Paleobotanical card search)

北美中生代-新生代化石植物总目(Paleobotanical card search) 总览1. 介绍2. 历史3. 内容 用户手册(卡片内容解读)示例卡片组成CIC编号的详细解读年代编码解读 卡片检索 总览 了解学习使用由耶鲁皮博迪自然历史博物馆&#xff08…

【贪心算法】leetcode刷题

贪心算法无固定套路。 核心思想:先找局部最优,再扩展到全局最优。 455.分发饼干 两种思路: 1、从大到小。局部最优就是大饼干喂给胃口大的,充分利用饼干尺寸喂饱一个,全局最优就是喂饱尽可能多的小孩。先遍历的胃口&a…

[containerd] ContentPlugin插件源码分析

文章目录 1. 概述2. 环境3. 注册4. 核心概念4.1. blob4.2. ingest 5. 抽象接口5.1. Manager接口5.2. Provider5.3. IngestManager5.4. Ingester 6. 核心实现6.1. Info6.2. Update6.3. Walk6.4. Delete6.5. ReaderAt6.6. Status6.7. ListStatuses6.8. Abort6.9. Writer 7. 总结 …

MySQL进阶--存储引擎

目录 一、简介二、什么是存储引擎?三、MySQL中常用的存储引擎1.InnoDB2.MyISAM3.Memory4.三种存储引擎对比 四、存储引擎的选择PS: 一、简介 本文的内容讲的是MySQL中的存储引擎的相关知识,初步认识MySQL中的存储引擎及各引擎的特点~ 二、什么是存储引…

Redis 安装以及配置隧道连接

目录 1.CentOS 1. 安装Redis 2. Redis 启动和停⽌ 3. 操作Redis 2.Ubuntu 1. 安装Redis 2. Redis 启动/停⽌ 3. 操作 Redis 3.开启隧道 3.1 Xshell 配置隧道 3.2 windTerm 配置隧道 3.3 FinalShell配置隧道 4.可视化客户端连接 Another Redis Desktop Manager 1.Cen…

Unity Image(RawImage) 实现按轴心放大缩小,序列化存储轴心信息,实现编译器窗口保存轴心

工作时分配给我的要实现的功能,写的时候遇到挺多的坑的,在此记录一下 效果 放大缩小的效果 2.编译器扩展窗口记录 实现点 1.Json序列化存储图片轴心位置, 放大倍率,放大所需要的事件 2.用了编译器扩展工具便于保存轴心信息坑点 1.Imag…

深度学习:探究Tensor和Numpy

目录 引言 1 pytorch中Tensor 1.1 什么是Tensor 1.2 为什么需要Tensor 1.3 如何创建Tensor 1.3.1 从已有其他数据结构转化创建为Tensor 1.3.2 随机初始化一个Tensor 1.3.3 从已保存文件加载一个Tensor 1.4 Tensor的特性 1.4.1 丰富的常用函数操作 1.4.2 灵活的dtype和…

maven开发利器:idea安装maven依赖分析插件 Maven Helper,谁用谁知道!

大家好,我是三叔,很高兴这期又和大家见面了,一个奋斗在互联网的打工人。 这篇博客给大家介绍一款博主实战开发中一直在使用的pom开发分析利器,教大家玩转maven:使用idea安装 Maven Helper 插件,可以分析依…

企业电子招标采购系统源码Spring Boot + Mybatis + Redis + Layui + 前后端分离 构建企业电子招采平台之立项流程图 tbms

 项目说明 随着公司的快速发展,企业人员和经营规模不断壮大,公司对内部招采管理的提升提出了更高的要求。在企业里建立一个公平、公开、公正的采购环境,最大限度控制采购成本至关重要。符合国家电子招投标法律法规及相关规范&am…

数据结构-链表

🗡CSDN主页:d1ff1cult.🗡 🗡代码云仓库:d1ff1cult.🗡 🗡文章栏目:数据结构专栏🗡 目录 目录 代码总览: 接口slist.h: slist.c: 1.什么是链表 1.1链…

用C语言构建一个数字识别卷积神经网络

卷积神经网络的具体原理和对应的python例子参见末尾的参考资料2.3. 这里仅叙述卷积神经网络的配置, 其余部分不做赘述,构建和训练神经网络的具体步骤请参见上一篇: 用C语言构建一个手写数字识别神经网路 卷积网络同样采用简单的三层结构,包括输入层con…

k8s pod数据存储Volumes

一、说在前面的话 在 Kubernetes 的 Deployment 中,您可以使用多种类型的 Volumes 来管理 Pod 中的数据。 作用是用来共享目录及配置,不用在每个pod里进行配置。 本文主要概述怎么使用HostPath、PersistentVolumeClaim、ConfigMap。 二、k8s有哪些Vol…

《golang设计模式》第一部分·创建型模式-04-抽象工厂模式(Abstract Factory)

文章目录 1. 概述1.1 角色1.2 类图 2. 代码示例2.1 设计2.2 代码2.3 类图 1. 概述 1.1 角色 AbstractFactory(抽象工厂):它声明了一组用于创建产品的方法,每一个方法对应一种产品。ConcreteFactory(具体工厂&#xf…

vue el-input 使用 回车键会刷新页面的问题

场景: vue项目中 在输入框输入字符并按下回车键搜索时,不会进行搜索, 而是会刷新页面 原因: 当form表单中只有一个input时,按下回车建会自动触发页面的提交功能, 产生刷新页面的行为 解决: 在…

Spring 事务详解(注解方式)

目 录 序言 1、编程式事务 2、配置声明式事务 2.1 基于TransactionProxyFactoryBean的方式(不常用,因为要为每一个类配置TransactionProxyFactoryBean) 2.2 基于AspectJ的XML方式(常用,可配置在某些类下的所有子…

无涯教程-Perl - unless...else 语句函数

Perl 除非语句后可以跟可选的 else 语句,该语句在布尔表达式为true时执行。 unless...else - 语法 Perl编程语言中的unless... else 语句的语法为- unless(boolean_expression) {# statement(s) will execute if the given condition is false } else {# stateme…