前言
前一个小节,我们讲解了一下SpringBoot的配置属性"篮子"——EnvironmentPostProcessor,今天我们一起学习一下Springboot家族中另一个重要的组件——ApplicationContextInitializer,虽然这个组件的功能很强单,但是我们平时用的并不是很多,原因还是很简单的,每一个组件应该有它自己擅长的领域,各个组件各司其职,不能越俎代庖,种别人家的地
![0808c26ea6d6fe9a19a51cf96362dcdf.png](https://i-blog.csdnimg.cn/blog_migrate/9a9ff1e7fbde5493503d6d877e1c6a68.jpeg)
spring家族组件分析系列教程
如果你以前看过或者没有看到spring源码的,你一定都会听说过BeanFactory或者ApplicationContext,本文并不是分析源码的教程,而是帮助你会用springboot的一些核心组件
从我的视角里,spring的容器生态就好比是一个战无不胜攻无不克的钢铁侠,它在我们开发的过程中,提供这种武器支持,有IOC激光武器,有AOP生化武器
![411a0f430cb70ccd4fa0bfcf026b5255.png](https://i-blog.csdnimg.cn/blog_migrate/2a7d1156f7d2c05082dc3dce1e72bf51.jpeg)
但是其实想要有一个这么强大的"钢铁侠"作为我们平时开发的利器,离不开人工智能AI,钢铁侠的灵魂"贾维斯"的存在,"贾维斯"就好比组装和运行"钢铁侠"的管家,类比一下,贾维斯就是ApplicationContext.class,当“贾维斯”启动按了开始按钮的时候,就像spring的ApplicationContext调用了refresh方法,一键变身"钢铁侠"
![e813ce797892cb87df5c682b1e934c5e.gif](https://i-blog.csdnimg.cn/blog_migrate/b4a5f5ff88a3b6a52cc210e29fba444e.gif)
今天我们讨论的并不是上图钢铁侠变成的过程refresh方法,而是讨论钢铁侠的灵魂管家"贾维斯",看看我们如何初始化训练这个人工智能
![4a165fe76bc6e2137e5bd93759ee9740.png](https://i-blog.csdnimg.cn/blog_migrate/33eb39ca1e6ec71cee65f787c11f1c0c.jpeg)
贾维斯
本小节为原创文章,阅读的时间大概是5分钟,主要讲解如下内容
- 训练"贾维斯"的入口函数ApplicationContextInitializer
- 加载ApplicationContextInitializer的几种方式
- ApplicationContextInitializer的正确日常使用
- 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](https://i-blog.csdnimg.cn/blog_migrate/ef73cd38a45f1e7002fc9d5e87bf6cba.jpeg)
1.2 创建一个自定义的简单的初始化器JarvisOneApplicationContextInitializer
![4fc34ea2ba7510efd2329b928a787368.png](https://i-blog.csdnimg.cn/blog_migrate/e527eec38b5aea2b34ef5c5eaefeb967.jpeg)
1.3 启动测试spring boot main函数,控制台日志输出
![8a3f53d37af612b85830d0a09a0566b5.png](https://i-blog.csdnimg.cn/blog_migrate/2c0ad5011575f98d94ce420a8a88b986.jpeg)
ApplicationContextInitializer
方式二:在application.properties,定义一个key值为context.initializer.classes的键值对,value值指向自己的自定义的ApplicationContextInitializer
2.1 application.properties
![00aaec1516f4b2b20057315aa83dc1f8.png](https://i-blog.csdnimg.cn/blog_migrate/c87a76148a0e58e94c8b598494b0d70d.jpeg)
2.2 自定义JarvisTwoApplicationContextInitializer
![218785d9917cf3fd9e7b41dff1f42d7e.png](https://i-blog.csdnimg.cn/blog_migrate/3bda24df6f4493cc9d5d9c67b5b39b94.jpeg)
2.3 启动测试spring boot main函数,控制台日志输出
![870ad39c8627cff77c253d85ce42d5d4.png](https://i-blog.csdnimg.cn/blog_migrate/0d9c4f0187febff75a76e4886004218b.jpeg)
方法三:直接在启动的main函数中指定自定义的ApplicationContextInitializer
3.1 在main函数中直接加载
![2b8d9945dab05f988835510968d36df0.png](https://i-blog.csdnimg.cn/blog_migrate/58606cd269f1f7d1e10e4b32bd4d57cd.jpeg)
3.2 自定义JarvisThreeApplicationContextInitializer
![971a3723c23caeb8440e38ad9c9f54c2.png](https://i-blog.csdnimg.cn/blog_migrate/5eb7f84ffb878da7c2418547187536be.jpeg)
3.3 启动测试spring boot main函数,控制台日志输出
![ec4e7d851b69b2652ee82991c93126be.png](https://i-blog.csdnimg.cn/blog_migrate/2535fdbb5869a546103a5e44010a60dd.jpeg)
成功运行
方法四:在自定义的EnvironmentPostProcessor实现
4.1 在上篇文章的基础上,修改自定义的BazingaJSONEnvironmentPostProcessor的方法
![baab768325d74a84c8da079c3448c63a.png](https://i-blog.csdnimg.cn/blog_migrate/9e216e5ece65611f48306924e09ef6f6.jpeg)
4.2 启动测试spring boot main函数,控制台日志输出
![c1f6cbc6e0ffe09ceb0df669f905c662.png](https://i-blog.csdnimg.cn/blog_migrate/33c913b403d75acde337b00f80a5e64d.jpeg)
如何正确的触发贾维斯的训练,大概的方式就是如上四种,推荐使用第一种,官方的,当然比较好,第二种比较冷门,而且容易被新手修改application.properties,第三种不是很灵活,第四种属于投机取巧,我们要好好触发贾维斯的训练,否则智障的贾维斯最后组装控制的钢铁侠肯定也是无法完成任务的
贾维斯的训练器ApplicationContextInitializer正确使用方式
其实说实话,这个初始化训练"贾维斯"的组件ApplicationContextInitializer能做的事情特别多,为什么这么说呢,因为可以从它的定义的接口说起,如下图,可以看出它就一个方法,initialize方法,这个方法的入参即合理又不合理,为什么说合理,因为它初始化的就是这个ApplicationContext,所以用它作为入参,合情合理,为什么说不合理,因为对于新手来说,ApplicationContext这个范围太大了,不知道需要怎么做才合理
![bdbb29c36665bf0cc154bddb8625d414.png](https://i-blog.csdnimg.cn/blog_migrate/d5e4ac4f3dd40c0fba06b24b4d137143.jpeg)
为什么说这里的ApplicationContext太大了,很简单,我们举两个例子来说就可以了
例一:如果在我们自定义的ApplicationContextInitializer中,使用configurableApplicationContext.getBean()方法就算不合理的,为什么这么说,请您想一想,这个时候,我们是要对"贾维斯"这个AI人工智能进行训练,加载一些特性,如果这个时候,"贾维斯"还没有初始化好,更谈不上ApplicationContext整个容器生态这个钢铁侠能不能战斗了,最后你用getBean这个"钢铁侠"发射导弹的功能,是不是有点过分了,会不会炸到自己?
例二:讲一个种别人田的例子——稍微看过spring源码的人都知道(没看过也没有关系),ApplicationContext生态也就是这个钢铁侠是一个集大成者,它也是BeanDefinitionRegistry的一个具体实现,所以它可以去注册一些BeanDefinition,如下图伪代码所示,,这个代码有没有问题,其实没有大问题,但是它不合理,为啥这么说,你想一下,注册bean这种事情,就好比给钢铁侠去添加一些子弹,给他注入飞行等功能,这种事情应该是贾维斯去指派它的小弟去做的,但是在这个地方,我要你做的是初始化“贾维斯”
![efc53fa506fce718ae5becedd57b6e1b.png](https://i-blog.csdnimg.cn/blog_migrate/2eed4d20fdb55432cd6543722c6c151d.jpeg)
不合理的使用
总而言之,用spring的组件就应该在正确的时候,用正确的组件做正确的事情
ApplicationContextInitializer正确的使用方式
在我的个人片面理解里,ApplicationContextInitializer能做的事情,算2件半
第一个0.5个事情
帮EnvironmentPostProcessor做一些擦屁股的事情,因为我们可以自定义很多EnvironmentPostProcessor的实现类,加载各式各样的配置文件,有本地的,有properties,有json格式的,有yaml格式的,甚至有远程的,例如apollo,nacos等等,因为来源多样,如果配置的properties的key值相同的时候,就会出现属性配置冲突,这个时候就有一个优先级的问题了,所以用ApplicationContextInitializer来做这个事,也是合适的,谁让它这个组件是第二个被加载的呢?帮忙擦个屁股还是可以接受的,毕竟屁股总是要有人来擦的
![fbad19b9bc9dad441a5cb11db48f596d.png](https://i-blog.csdnimg.cn/blog_migrate/80d887e874de8ba23bd83cf85e475f22.jpeg)
但还是上面的说的原则,其实EnvironmentPostProcessor本身就可以做好这个事情,通过addFirst或者addLast,addFirst,addBefore,addAfter方法来确认配置属性的优先顺序,所以我觉得这个算半个事情
第一件完整事情
去发布一些事件,spring有一个经典的模式,是事件的发布和订阅模式,后期文章也会介绍,在这边,我们就说发布和监听的动作应该在ApplicationContextInitializer中去做,才是正确的时间做正确的事情,nacos就是大量利用发布订阅的模式来做属性的热更新的,我们这边sample的伪代码如下
![8fabe763d3bd33d183f26fe4f5eabe21.png](https://i-blog.csdnimg.cn/blog_migrate/a50ed6ed9084ab19ce23ab866adc3c73.jpeg)
第二件完整的事情
第二件事情也是我们平时开发中,最经常使用的spring的技巧——在贾维斯的扳手里面增加一个很使用的工具BeanFactoryPostProcessor,这个类在贾维斯的工具箱中,在组装钢铁侠的过程中,有功不可没的作用,后期文章再介绍,新增的方式如下
![ff564a714fbc06fb4e0433f149392db6.png](https://i-blog.csdnimg.cn/blog_migrate/fbfe5b97caa58a2f187da845f53da737.jpeg)
ApplicationContextInitializer的加载原理
其实ApplicationContextInitializer加载顺序的原理特别简单,看过上一篇文章的小伙伴们,只要把源码往下多看一行就可以了,我们回到SpringApplication的#prepareContext方法中
![4ec5ce8aaa71052bd14798b3d2404c62.png](https://i-blog.csdnimg.cn/blog_migrate/bd189e140e71b3961f7b8e291fce4cd9.jpeg)
这边的原理还是非常简单的,小伙伴们可以自行去扫一眼spring boot的源码,就能够读懂ApplicationContextInitializer了
小结
本小节介绍了训练贾维斯ApplicationContext的利器ApplicationContextInitializer,了解了它的加载时机和顺序,以我个人的视角,说明了这个组件应该做和不应该做的事情,并且引出了下面spring boot核心组件系列的下几个核心组件BeanFactoryPostProcessor和ApplicationEvent
如果您觉得不错,希望你加一个关注,如果您觉得有任何错误的地方,欢迎后台私信我,或者你有什么想一起学的技术,可以告诉我,我们可以一起学习,您的关注是我写作最大的动力~