0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

SpingBoot的5个扩展点,超级实用!

jf_ro2CN3Fa 来源:稀土掘金 2024-02-22 11:28 次阅读

1、初始化器ApplicationContextInitializer

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

abd0c146-d123-11ee-a297-92fbcf53809c.jpg

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

abdef072-d123-11ee-a297-92fbcf53809c.jpg

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

abe7e056-d123-11ee-a297-92fbcf53809c.jpg

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

abedae28-d123-11ee-a297-92fbcf53809c.jpg

点进去看下,这两个底层调的方法是一样,就是传入一个类型,通过Spring SPI的方式查找这个类型的实现类

abfd4086-d123-11ee-a297-92fbcf53809c.jpg

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

ac04d81e-d123-11ee-a297-92fbcf53809c.jpg

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

ac15ab80-d123-11ee-a297-92fbcf53809c.jpg

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

ac22d224-d123-11ee-a297-92fbcf53809c.jpg

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

ac3169e2-d123-11ee-a297-92fbcf53809c.jpg

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

ac38b526-d123-11ee-a297-92fbcf53809c.jpg

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

ac439fea-d123-11ee-a297-92fbcf53809c.jpg

原来是调用了run()方法中的prepareContext()方法中的applyInitializers()方法,在这个方法中for循环的调用各个初始化器的initialize()方法,从而我们就能看到把Jack的ApplicationContextInitializer这句话给打印出来了。

那么这个查找的方法就是以结果为导向来反查调用方,因为你正查的话是很难找到,很难记住的,这个方法希望大家学习到。

ac4d959a-d123-11ee-a297-92fbcf53809c.jpg

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

ac5326b8-d123-11ee-a297-92fbcf53809c.jpg

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

2、监听器ApplicationListener

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

ac6006da-d123-11ee-a297-92fbcf53809c.jpg

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

ac6a9636-d123-11ee-a297-92fbcf53809c.jpg

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

ac7064c6-d123-11ee-a297-92fbcf53809c.jpgac7d6036-d123-11ee-a297-92fbcf53809c.jpg

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

ac886bde-d123-11ee-a297-92fbcf53809c.jpgac915ce4-d123-11ee-a297-92fbcf53809c.jpg

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

ac9c31fa-d123-11ee-a297-92fbcf53809c.jpg

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

aca3689e-d123-11ee-a297-92fbcf53809c.jpg

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

aca7ecd4-d123-11ee-a297-92fbcf53809c.jpg

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

acb37180-d123-11ee-a297-92fbcf53809c.jpgacb9465a-d123-11ee-a297-92fbcf53809c.jpg

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

acc19bca-d123-11ee-a297-92fbcf53809c.jpg

3、Runner

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

acc56958-d123-11ee-a297-92fbcf53809c.jpg

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

acccb172-d123-11ee-a297-92fbcf53809c.jpg

我们来写一个自己的Runner

acd64336-d123-11ee-a297-92fbcf53809c.jpg

运行一下,看下打印出来了

ace1cfb2-d123-11ee-a297-92fbcf53809c.jpg

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

ace77fc0-d123-11ee-a297-92fbcf53809c.jpg

4、BeanFactoryPostProcessor

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

aceb7364-d123-11ee-a297-92fbcf53809c.jpg

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

acf5f276-d123-11ee-a297-92fbcf53809c.jpg

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

acfe89ea-d123-11ee-a297-92fbcf53809c.jpg

运行一下,也打印出来了

ad02683a-d123-11ee-a297-92fbcf53809c.jpg

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

ad0d88be-d123-11ee-a297-92fbcf53809c.jpg

5、BeanPostProcessor

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

ad202c9e-d123-11ee-a297-92fbcf53809c.jpg

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

ad26b26c-d123-11ee-a297-92fbcf53809c.jpg

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

ad31e42a-d123-11ee-a297-92fbcf53809c.jpg

是在finishBeanFactoryInitialization()方法处调用的

ad37d4ac-d123-11ee-a297-92fbcf53809c.jpg

后记

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

ad458c5a-d123-11ee-a297-92fbcf53809c.jpg

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




审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • AOP
    AOP
    +关注

    关注

    0

    文章

    37

    浏览量

    11056
  • for循环
    +关注

    关注

    0

    文章

    61

    浏览量

    2443

原文标题:SpingBoot的5个扩展点,超级实用!

文章出处:【微信号:芋道源码,微信公众号:芋道源码】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    写的一扩展串口的程序,发多字节,三次发送才收到...

    本帖最后由 hbdxzn 于 2013-1-10 14:53 编辑 写的一扩展串口的程序,发多字节,三次发送才收到一次,请问这是什么原因吖?void send(unsigned char *buf,unsigned c
    发表于 01-10 14:52

    超级电容电源模组

    本帖最后由 544642572 于 2013-8-28 10:15 编辑 最近对超级电容很感兴趣,想设计一超级电容充放电的电路,于是买了5
    发表于 08-27 10:27

    bW6101-超级法拉电容过压保护芯片

    。 可以通过外部端口选择为两种规格的超级电容进行充电保护。当选择端口为高电平时,对应保护为2.65V,当选择端口电平为低时,对应保护为2.45V。方便用户的灵活使用。 采用小型化的SOT23-
    发表于 12-24 14:44

    超级电容保护芯片-BW6101

    。 可以通过外部端口选择为两种规格的超级电容进行充电保护。当选择端口为高电平时,对应保护为2.65V,当选择端口电平为低时,对应保护为2.45V。方便用户的灵活使用。 采用小型化的SOT23-
    发表于 01-09 13:50

    转: 聚焦超级电容选型与应用

    器件,但它的能量储存机制却一也不涉及化学反应。这个机制是高度可逆的,它允许超级电容器充电放电达十万甚至数百万次。超级电容器可以被视为在两极板外加电压时被电解液隔开的两
    发表于 06-11 10:45

    超级电容器充电

    5v/500mA电源给超级电容器充电,超级电容器要怎么选择?我在这方面完全小白,之前没接触过超级电容器的充电。目的就是做一
    发表于 06-03 14:41

    基于FPGA的超级电容均压及充放电设计方案

    :Vc为N串联超级电容两端总电压;Vd为续流二极管上的正向导通压降;Vs为MOSFET上的导通压降。  逆变部分采用5kHz的50%占空比的PWM波加入一定的死区时间来实现,S1,S4采用同一组信号
    发表于 12-03 11:01

    TOWE新品|一可以DIY的“超级插座”

    安全功能和智能管理统一结合。......以上所有痛只需一TOWE“超级插座”即可解决!“超级插座”的定制系统可以对多种输出、输入模块,多种智能、功能模块进行选择,并且可随着功能需求
    发表于 04-22 11:49

    超级电容容量选取

    超级电容驱动LED灯组,1500mA 8并联,正向压降3.1V 超级电容 放电12A 放电时间10mS 超级电容充满电压5V 求
    发表于 07-03 05:55

    超级电容器“超级”在哪?

    高,庞大的表面积再加上非常小的电荷分离距离使得超级电容器较传统电容器而言有着很大的容量,功率密度可以达到电池的5~10倍;5)产品原材料构成、生产、使用、储存以及拆解过程均没有污染,是理想的绿色
    发表于 04-22 09:23

    5G波束赋形和超级上行技术

    针对上行速率进行了重点优化,通过「超级上行」技术,让处于基站边缘的5G手机也能具有更稳、更快的5G联接。 一起来认识「波束赋形」技术和「超级上行」技术吧↓↓↓ `
    发表于 05-13 09:04

    图文解析 5G SA 超级上行频谱

    1.1、背景当前5G C-Band主要采用TDD组网,即上行和下行时分复用C-Band 频谱资源,一般采用8:2/7: 3/4: 1时隙配比,实际用于上行的时频资源有限,导致用户上行体验不佳。超级
    发表于 05-06 09:54

    提供标准扩展的javascript小部件

    我有一提供标准扩展的javascript小部件。其中之一是beforecreate函数。它应该返回false以防止创建项目。我已经使用jquery向这个函数添加了一Ajax调用
    发表于 09-06 07:12

    超级电容器“超级”在哪?

    高,庞大的表面积再加上非常小的电荷分离距离使得超级电容器较传统电容器而言有着很大的容量,功率密度可以达到电池的5~10倍;5)产品原材料构成、生产、使用、储存以及拆解过程均没有污染,是理想的绿色
    发表于 10-30 15:17

    Raspberry Pi扩展

    描述clumsyMIDI - 树莓派扩展板一 Raspberry Pi 扩展板,包含一 MIDI 接口、DAC 和 OLED 显示器,只需要通孔焊接技能。这主要用作mt32-pi
    发表于 07-29 06:12