Spring中的ApplicationContextInitializer

Spring中的ApplicationContextInitializer

1.介绍

  • 先来看下这个类是用来做什么的。ApplicationContextInitializer类在源码中的注释如下:(源代码基于版本2.3.3)
* Callback interface for initializing a Spring {@link ConfigurableApplicationContext}
* prior to being {@linkplain ConfigurableApplicationContext#refresh() refreshed}.
*
* <p>Typically used within web applications that require some programmatic initialization
* of the application context. For example, registering property sources or activating
* profiles against the {@linkplain ConfigurableApplicationContext#getEnvironment()
* context's environment}. See {@code ContextLoader} and {@code FrameworkServlet} support
* for declaring a "contextInitializerClasses" context-param and init-param, respectively.
*
* <p>{@code ApplicationContextInitializer} processors are encouraged to detect
* whether Spring's {@link org.springframework.core.Ordered Ordered} interface has been
* implemented or if the @{@link org.springframework.core.annotation.Order Order}
* annotation is present and to sort instances accordingly if so prior to invocation
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {

  /**
   * Initialize the given application context.
   * @param applicationContext the application to configure
   */
  void initialize(C applicationContext);

}

这个类的主要目的就是在ConfigurableApplicationContext类型(或者子类型)的ApplicationContext做refresh之前,允许我们对ConfigurableApplicationContext的实例做进一步的设置或者处理。
通常用于需要对应用程序上下文进行编程初始化的web应用程序中。例如,根据上下文环境注册属性源或激活概要文件。

  • 参考ContextLoader和FrameworkServlet中支持定义contextInitializerClasses作为context-param或定义init-param。
  • ApplicationContextInitializer支持Order注解,表示执行顺序,越小越早执行;

2. 常见实现类分析

  • 使用分析:在从SpringBoot启动过程分析到自定义一个springboot-starter一文中我们分析到了在SpringApplication创建时先获取了一些ApplicationContextInitializer,然后是在prepareContext()应用上下文环境准备时使用这些初始化器做了具体的初始化操作。
  • 缺省配置的ApplicationContextInitializer配置类如下:
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer

逐个看一下每一个Initializer中都具体做了哪些初始化的操作:

  • ConfigurationWarningsApplicationContextInitializer:对于一般配置错误在日志中作出警告
  • ContextIdApplicationContextInitializer:设置ApplicationContext#getId()所获取的ID值,默认取spring.application.name属性值,没有配置时默认为 application
  • DelegatingApplicationContextInitializer:使用环境属性context.initializer.classes指定的初始化器(initializers)进行初始化工作,如果没有指定则什么都不做
    /**
     * {@link ApplicationContextInitializer} that delegates to other initializers that are
     * specified under a {@literal context.initializer.classes} environment property.
     *
     * @author Dave Syer
     * @author Phillip Webb
     */
    public class DelegatingApplicationContextInitializer
           implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
    
       // NOTE: Similar to org.springframework.web.context.ContextLoader
    
       private static final String PROPERTY_NAME = "context.initializer.classes";
    
       private int order = 0;
    
       @Override
       public void initialize(ConfigurableApplicationContext context) {
           ConfigurableEnvironment environment = context.getEnvironment();
           List<Class<?>> initializerClasses = getInitializerClasses(environment);
           if (!initializerClasses.isEmpty()) {
               applyInitializerClasses(context, initializerClasses);
           }
       }
    
    从上面的部分代码可见,实现了Ordered接口时,该initializer的order为0,也就是说使用DelegatingApplicationContextInitializer方式进行包装配置的初始化器将会最先被加载
  • ServerPortInfoApplicationContextInitializer: 将内置servlet容器实际使用的监听端口写入到Environment环境属性中。这样属性local.server.port就可以直接通过@Value注入到测试中,或者通过环境属性Environment获取

3. ApplicationContextInitializer的使用

Spring通过ApplicationContextInitializer允许我们在SpringApplication上下文刷新之前进行自定义的操作,可以使用这个hook来实现某些特定的需求。

  • 首先定义一个ApplicationContextInitializer,可以使用实现Ordered接口或@Order注解的方式指定顺序:
 /**
  * 自定义的ApplicationContextInitializer
  *
  * @author duwenxu
  * @create 2021-01-25 13:48
  */
 @Slf4j
 public class MyInitializer implements ApplicationContextInitializer,Ordered {
     public int getOrder() {
         return 111;
     }
 
     public void initialize(ConfigurableApplicationContext applicationContext) {
         ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
         Iterator<String> beanNamesIterator = beanFactory.getBeanNamesIterator();
         int beanDefinitionCount = beanFactory.getBeanDefinitionCount();
         log.info("beanDefinitionCount={}",beanDefinitionCount);
         while (beanNamesIterator.hasNext()){
             System.out.println(beanNamesIterator.next());
         }
     }
 }

ApplicationContextInitializer的使用有以下3中方式:

  • 在启动类中使用SpringApplication.addInitializers() 手动增加Initializer
@SpringBootApplication
@Slf4j
public class WebsocketApplication {

 public static void main(String[] args) {
     run(args);
 }

 private static void run(String[] args) {
     SpringApplication springApplication = new SpringApplication(WebsocketApplication.class);
     springApplication.addInitializers(new MyInitializer());
     springApplication.run(args);
 }
}
  • application.properties添加配置方式

    在分析缺省的initializer时,提到了DelegatingApplicationContextInitializer,可以使用该类来添加我们自己的initializers

    添加如下的配置properties中即可:
 context.initializer.classes=com.netty.websocket.initializertest.MyInitializer
  • 可以参照SpringBoot缺省的Initializer,将MyInitializer配置在spring.factories文件中
 org.springframework.context.ApplicationContextInitializer=com.netty.websocket.initializertest.MyInitializer

spring在获取ApplicationContextInitializer时将会获取到我们自定义的MyInitializer

4. 上述几种方式ApplicationContextInitializer的执行顺序

  1. 如果我们通过DelegatingApplicationContextInitializer委托来执行我们自定义的ApplicationContextInitializer,那么我们自定义的ApplicationContextInitializer的顺序一定是在系统自带的其他ApplicationContextInitializer之前执行。
  2. 如果我们通过SpringApplication实例对象调用addInitializers方法加入自定义的ApplicationContextInitializer,那么spring-boot自带的ApplicationContextInitializer会先按顺序执行,再执行我们手动添加的自定义ApplicationContextInitializer(按照添加顺序执行),最后执行spring-boot自带的其他ApplicationContextInializer
  3. 如果我们创建自己的spring.factories文件,添加配置加入我们自定义的ApplicationContextInitializer,那么我们自定义的ApplicationContextInitializer会和spring-boot自带的ApplicationContextInitializer放在一起进行排序执行
    执行结果如下:


    加载自定义的ApplicationContextInitializer打印信息.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,470评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,393评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,577评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,176评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,189评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,155评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,041评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,903评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,319评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,539评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,703评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,417评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,013评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,664评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,818评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,711评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,601评论 2 353

推荐阅读更多精彩内容