组件启动顺序_钢铁侠视角讲解Springboot组件ApplicationContextInitializer

前言

前一个小节,我们讲解了一下SpringBoot的配置属性"篮子"——EnvironmentPostProcessor,今天我们一起学习一下Springboot家族中另一个重要的组件——ApplicationContextInitializer,虽然这个组件的功能很强单,但是我们平时用的并不是很多,原因还是很简单的,每一个组件应该有它自己擅长的领域,各个组件各司其职,不能越俎代庖,种别人家的地

0808c26ea6d6fe9a19a51cf96362dcdf.png

spring家族组件分析系列教程

如果你以前看过或者没有看到spring源码的,你一定都会听说过BeanFactory或者ApplicationContext,本文并不是分析源码的教程,而是帮助你会用springboot的一些核心组件

从我的视角里,spring的容器生态就好比是一个战无不胜攻无不克的钢铁侠,它在我们开发的过程中,提供这种武器支持,有IOC激光武器,有AOP生化武器

411a0f430cb70ccd4fa0bfcf026b5255.png

但是其实想要有一个这么强大的"钢铁侠"作为我们平时开发的利器,离不开人工智能AI,钢铁侠的灵魂"贾维斯"的存在,"贾维斯"就好比组装和运行"钢铁侠"的管家,类比一下,贾维斯就是ApplicationContext.class,当“贾维斯”启动按了开始按钮的时候,就像spring的ApplicationContext调用了refresh方法,一键变身"钢铁侠"

e813ce797892cb87df5c682b1e934c5e.gif

今天我们讨论的并不是上图钢铁侠变成的过程refresh方法,而是讨论钢铁侠的灵魂管家"贾维斯",看看我们如何初始化训练这个人工智能

4a165fe76bc6e2137e5bd93759ee9740.png

贾维斯

本小节为原创文章,阅读的时间大概是5分钟,主要讲解如下内容

  1. 训练"贾维斯"的入口函数ApplicationContextInitializer
  2. 加载ApplicationContextInitializer的几种方式
  3. ApplicationContextInitializer的正确日常使用
  4. ApplicationContextInitializer的加载原理

简介ApplicationContextInitializer

训练"贾维斯"的入口叫做ApplicationContextInitializer,中文名叫做"应用上下文初始化器",优秀的spring源码,从名字就可以看到这个组件是干嘛用的,简而言之就是初始化ApplicationContext内部的一些属性的,或者在ApplicationContext注册一些什么组件,这些都是可以的,下文我们也会写例子,来具体说明ApplicationContextInitializer可以用来做的事情

再说说ApplicationContextInitializer在所有组件中的加载顺序,我们先给结论,最后从源码的角度分析为啥它的加载顺序是如此

它的顺序是在EnvironmentPostProcessor之后,在refresh方法之前,如果你不知道refresh方法,也没有关系,你只要知道它在所有的springboot组件中的加载顺序是第二

再仔细思考一下为啥它的加载顺序如此靠前,原因也很简单,"篮子"EnvironementPostProcessor执行完了,把一些额外配置加载到篮子中了,接下来的一步就是创建一个"贾维斯"(钢铁侠的真正人工AI管家)——ApplicationContext,我觉得ApplicationContext也是spring的AI管家,在管家去操控spring容器实例这个"钢铁侠"之前,贾维斯需要把自己初始化好,训练一下自己的AI智商,加载一些必要的元素,所以从这个角度来说,它的加载顺序如此靠前也可以理解了

加载ApplicationContextInitializer的几种方式

方式一:利用META-INF下的spring.factories

1.1 在META-INF下声明自定义的ApplicationContextInitializer与上篇文章加载EnvironmentPostProcessor一样的方式

a27c33cfdbe19756eeb4d814ff8943b5.png

1.2 创建一个自定义的简单的初始化器JarvisOneApplicationContextInitializer

4fc34ea2ba7510efd2329b928a787368.png

1.3 启动测试spring boot main函数,控制台日志输出

8a3f53d37af612b85830d0a09a0566b5.png

ApplicationContextInitializer

方式二:在application.properties,定义一个key值为context.initializer.classes的键值对,value值指向自己的自定义的ApplicationContextInitializer

2.1 application.properties

00aaec1516f4b2b20057315aa83dc1f8.png

2.2 自定义JarvisTwoApplicationContextInitializer

218785d9917cf3fd9e7b41dff1f42d7e.png

2.3 启动测试spring boot main函数,控制台日志输出

870ad39c8627cff77c253d85ce42d5d4.png

方法三:直接在启动的main函数中指定自定义的ApplicationContextInitializer

3.1 在main函数中直接加载

2b8d9945dab05f988835510968d36df0.png

3.2 自定义JarvisThreeApplicationContextInitializer

971a3723c23caeb8440e38ad9c9f54c2.png

3.3 启动测试spring boot main函数,控制台日志输出

ec4e7d851b69b2652ee82991c93126be.png

成功运行

方法四:在自定义的EnvironmentPostProcessor实现

4.1 在上篇文章的基础上,修改自定义的BazingaJSONEnvironmentPostProcessor的方法

baab768325d74a84c8da079c3448c63a.png

4.2 启动测试spring boot main函数,控制台日志输出

c1f6cbc6e0ffe09ceb0df669f905c662.png

如何正确的触发贾维斯的训练,大概的方式就是如上四种,推荐使用第一种,官方的,当然比较好,第二种比较冷门,而且容易被新手修改application.properties,第三种不是很灵活,第四种属于投机取巧,我们要好好触发贾维斯的训练,否则智障的贾维斯最后组装控制的钢铁侠肯定也是无法完成任务的

贾维斯的训练器ApplicationContextInitializer正确使用方式

其实说实话,这个初始化训练"贾维斯"的组件ApplicationContextInitializer能做的事情特别多,为什么这么说呢,因为可以从它的定义的接口说起,如下图,可以看出它就一个方法,initialize方法,这个方法的入参即合理又不合理,为什么说合理,因为它初始化的就是这个ApplicationContext,所以用它作为入参,合情合理,为什么说不合理,因为对于新手来说,ApplicationContext这个范围太大了,不知道需要怎么做才合理

bdbb29c36665bf0cc154bddb8625d414.png

为什么说这里的ApplicationContext太大了,很简单,我们举两个例子来说就可以了

例一:如果在我们自定义的ApplicationContextInitializer中,使用configurableApplicationContext.getBean()方法就算不合理的,为什么这么说,请您想一想,这个时候,我们是要对"贾维斯"这个AI人工智能进行训练,加载一些特性,如果这个时候,"贾维斯"还没有初始化好,更谈不上ApplicationContext整个容器生态这个钢铁侠能不能战斗了,最后你用getBean这个"钢铁侠"发射导弹的功能,是不是有点过分了,会不会炸到自己?

例二:讲一个种别人田的例子——稍微看过spring源码的人都知道(没看过也没有关系),ApplicationContext生态也就是这个钢铁侠是一个集大成者,它也是BeanDefinitionRegistry的一个具体实现,所以它可以去注册一些BeanDefinition,如下图伪代码所示,,这个代码有没有问题,其实没有大问题,但是它不合理,为啥这么说,你想一下,注册bean这种事情,就好比给钢铁侠去添加一些子弹,给他注入飞行等功能,这种事情应该是贾维斯去指派它的小弟去做的,但是在这个地方,我要你做的是初始化“贾维斯”

efc53fa506fce718ae5becedd57b6e1b.png

不合理的使用

总而言之,用spring的组件就应该在正确的时候,用正确的组件做正确的事情

ApplicationContextInitializer正确的使用方式

在我的个人片面理解里,ApplicationContextInitializer能做的事情,算2件半

第一个0.5个事情

帮EnvironmentPostProcessor做一些擦屁股的事情,因为我们可以自定义很多EnvironmentPostProcessor的实现类,加载各式各样的配置文件,有本地的,有properties,有json格式的,有yaml格式的,甚至有远程的,例如apollo,nacos等等,因为来源多样,如果配置的properties的key值相同的时候,就会出现属性配置冲突,这个时候就有一个优先级的问题了,所以用ApplicationContextInitializer来做这个事,也是合适的,谁让它这个组件是第二个被加载的呢?帮忙擦个屁股还是可以接受的,毕竟屁股总是要有人来擦的

fbad19b9bc9dad441a5cb11db48f596d.png

但还是上面的说的原则,其实EnvironmentPostProcessor本身就可以做好这个事情,通过addFirst或者addLast,addFirst,addBefore,addAfter方法来确认配置属性的优先顺序,所以我觉得这个算半个事情

第一件完整事情

去发布一些事件,spring有一个经典的模式,是事件的发布和订阅模式,后期文章也会介绍,在这边,我们就说发布和监听的动作应该在ApplicationContextInitializer中去做,才是正确的时间做正确的事情,nacos就是大量利用发布订阅的模式来做属性的热更新的,我们这边sample的伪代码如下

8fabe763d3bd33d183f26fe4f5eabe21.png

第二件完整的事情

第二件事情也是我们平时开发中,最经常使用的spring的技巧——在贾维斯的扳手里面增加一个很使用的工具BeanFactoryPostProcessor,这个类在贾维斯的工具箱中,在组装钢铁侠的过程中,有功不可没的作用,后期文章再介绍,新增的方式如下

ff564a714fbc06fb4e0433f149392db6.png

ApplicationContextInitializer的加载原理

其实ApplicationContextInitializer加载顺序的原理特别简单,看过上一篇文章的小伙伴们,只要把源码往下多看一行就可以了,我们回到SpringApplication的#prepareContext方法中

4ec5ce8aaa71052bd14798b3d2404c62.png

这边的原理还是非常简单的,小伙伴们可以自行去扫一眼spring boot的源码,就能够读懂ApplicationContextInitializer了

小结

本小节介绍了训练贾维斯ApplicationContext的利器ApplicationContextInitializer,了解了它的加载时机和顺序,以我个人的视角,说明了这个组件应该做和不应该做的事情,并且引出了下面spring boot核心组件系列的下几个核心组件BeanFactoryPostProcessor和ApplicationEvent

如果您觉得不错,希望你加一个关注,如果您觉得有任何错误的地方,欢迎后台私信我,或者你有什么想一起学的技术,可以告诉我,我们可以一起学习,您的关注是我写作最大的动力~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值