ApplicationContextInitializer是Spring框架原有的东西,这个类的主要作用就是在ConfigurableApplicationContext类型(或者子类型)的ApplicationContext做refresh之前,允许我们对ConfiurableApplicationContext的实例做进一步的设置和处理。
 
ApplicationContextInitializer接口是在spring容器刷新之前执行的一个回调函数。是在ConfigurableApplicationContext#refresh() 之前调用(当spring框架内部执行 ConfigurableApplicationContext#refresh() 方法的时候或者在SpringBoot的run()执行时),作用是初始化Spring ConfigurableApplicationContext的回调接口。
 
ApplicationContextInitializer是Spring框架原有的概念, 这个类的主要目的就是在ConfigurableApplicationContext类型(或者子类型)的ApplicationContext做refresh之前,允许我们对ConfigurableApplicationContext的实例做进一步的设置或者处理。

通常用于需要对应用程序上下文进行编程初始化的web应用程序中。例如,根据上下文环境注册属性源或激活概要文件。

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

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

 一、使用分析

该接口典型的应用场景是web应用中需要编程方式对应用上下文做初始化。比如,注册属性源(property sources)或者针对上下文的环境信息environment激活相应的profile。

在一个Springboot应用中,classpath上会包含很多jar包,有些jar包需要在ConfigurableApplicationContext#refresh()调用之前对应用上下文做一些初始化动作,因此它们会提供自己的ApplicationContextInitializer实现类,然后放在自己的META-INF/spring.factories属性文件中,这样相应的ApplicationContextInitializer实现类就会被SpringApplication#initialize发现:

     // SpringApplication#initialize方法,在其构造函数内执行,从而确保在其run方法之前完成
private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
this.webEnvironment = deduceWebEnvironment();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));// <===================
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}

然后在应用上下文创建之后,应用上下文刷新(refresh)之前的准备阶段被调用 :

SpringBoot内置的一些ApplicationContextInitializer

下面列出了一个使用缺省配置的Springboot web应用默认所使用到的ApplicationContextInitializer实现们:

DelegatingApplicationContextInitializer
使用环境属性context.initializer.classes指定的初始化器(initializers)进行初始化工作,如果没有指定则什么都不做。

通过它使得我们可以把自定义实现类配置在application.properties里成为了可能。

ContextIdApplicationContextInitializer
设置Spring应用上下文的ID,会参照环境属性。至于Id设置为啥值会参考环境属性:
spring.application.name
vcap.application.name
spring.config.name
spring.application.index
vcap.application.instance_index

如果这些属性都没有,ID使用application。

ConfigurationWarningsApplicationContextInitializer
对于一般配置错误在日志中作出警告

ServerPortInfoApplicationContextInitializer
将内置servlet容器实际使用的监听端口写入到Environment环境属性中。这样属性local.server.port就可以直接通过@Value注入到测试中,或者通过环境属性Environment获取。

SharedMetadataReaderFactoryContextInitializer
创建一个SpringBoot和ConfigurationClassPostProcessor共用的CachingMetadataReaderFactory对象。实现类为:ConcurrentReferenceCachingMetadataReaderFactory

ConditionEvaluationReportLoggingListener
将ConditionEvaluationReport写入日志。

以上都是SpringBoot内置的上文启动器,可见Spring留出的这个钩子,被SpringBoot发扬光大了。
实际上不仅于此,SpringBoot对Spring Framework的事件监听机制也都有大量的应用~

总结
ApplicationContextInitializer是Spring留出来允许我们在上下文刷新之前做自定义操作的钩子,若我们有需求想要深度整合Spring上下文,借助它不乏是一个非常好的实现。

随便浏览一下SpringBoot的源码可知,它对Spring特征特性的使用,均是非常的流畅且深度整合的。所以说SpringBoot易学难精的最大拦路虎:其实是对Spring Framework系统性的把握~

Tips:spring-test包里有个注解org.springframework.test.context.ContextConfiguration它有个属性可以指定ApplicationContextInitializer辅助集成测试时候的自定义对上下文进行预处理~

二、扩展实现方式

2.1、编程方式

先定义ApplicationContextInitializer,如下:

package com.transsnet.palmpay.controller;

import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.Order; //@Order的value值越小->越早执行。注:在类上标注,不是方法上
@Order(111)
public class ApplicationContextInitializer1 implements ApplicationContextInitializer { @Override
public void initialize(ConfigurableApplicationContext applicationContext) { // 打印容器里面有多少个bean
System.out.println("bean count=====" + applicationContext.getBeanDefinitionCount()); // 打印人所有 beanName
System.out.println(applicationContext.getBeanDefinitionCount() + "个Bean的名字如下:");
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanName : beanDefinitionNames) {
System.out.println(beanName);
} }
}

启动类里手动增加initializer

@SpringBootApplication
@EnableConfigServer
@EnableDiscoveryClient
public class ConfigServer {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(ConfigServer.class); // 方法一:添加自定义的 ApplicationContextInitializer 实现类的实例(注册ApplicationContextInitializer)
springApplication.addInitializers(new ApplicationContextInitializer1()); ConfigurableApplicationContext context = springApplication.run(args); context.close();
}
}

结果:

2.2、application.properties添加配置方式

对于这种方式是通过DelegatingApplicationContextInitializer这个初始化类中的initialize方法获取到application.properties中context.initializer.classes对应的类并执行对应的initialize方法。只需要将实现了ApplicationContextInitializer的类添加到application.properties即可。如下:

1、先定义先定义ApplicationContextInitializer,同1.1

2、在application.properties中定义:

2.3、使用spring.factories方式

1、先定义先定义ApplicationContextInitializer,同1.1

2、然后在项目下的resources下新建META-INF文件夹,文件夹下新建spring.factories文件

org.springframework.context.ApplicationContextInitializer=com.dxz.ApplicationContextInitializer1

这个加载过程是在SpringApplication中的getSpringFactoriesInstances()方法中直接加载并实例后执行对应的initialize方法。代码如下:

    private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
} private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<String>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}

三、ApplicationContextInitializer执行顺序

springboot中自带的DelegatingApplicationContextInitializer类的排序值为0,是springboot自带的ApplicationContextInitializer中排序最小,最先执行的类。(如果ApplicationContextInitializer没有实现Orderd接口,那么其排序值默认是最大,最后执行)

所以可以得到其执行顺序如下

1.如果我们通过DelegatingApplicationContextInitializer委托来执行我们自定义的ApplicationContextInitializer,那么我们自定义的ApplicationContextInitializer的顺序一定是在系统自带的其他ApplicationContextInitializer之前执行。

2.如果我们通过SpringApplication实例对象调用addInitializers方法加入自定义的ApplicationContextInitializer,那么spring-boot自带的ApplicationContextInitializer会先按顺序执行,再执行我们手动添加的自定义ApplicationContextInitializer(按照添加顺序执行),最后执行spring-boot自带的其他ApplicationContextInializer

3.如果我们创建自己的spring.factories文件,添加配置加入我们自定义的ApplicationContextInitializer,那么我们自定义的ApplicationContextInitializer会和spring-boot自带的ApplicationContextInitializer放在一起进行排序执行。

spring扩展点之五:ApplicationContextInitializer实现与使用的更多相关文章

  1. Spring扩展之一:ApplicationContextInitializer

    1.介绍 用于Spring容器ConfigurableApplicationContext在刷新之前初始化Spring的回调接口. 通常在需要对应用程序上下文进行一些编程初始化的Web应用程序中使用. ...

  2. spring扩展点之三:Spring 的监听事件 ApplicationListener 和 ApplicationEvent 用法,在spring启动后做些事情

    <spring扩展点之三:Spring 的监听事件 ApplicationListener 和 ApplicationEvent 用法,在spring启动后做些事情> <服务网关zu ...

  3. spring扩展点之二:spring中关于bean初始化、销毁等使用汇总,ApplicationContextAware将ApplicationContext注入

    <spring扩展点之二:spring中关于bean初始化.销毁等使用汇总,ApplicationContextAware将ApplicationContext注入> <spring ...

  4. spring4.1.8扩展实战之五:改变bean的定义(BeanFactoryPostProcessor接口)

    本章我们继续实战spring的扩展能力,通过自定义BeanFactoryPostProcessor接口的实现类,来对bean实例做一些控制: 原文地址:https://blog.csdn.net/bo ...

  5. spring扩展点整理

    本文转载自spring扩展点整理 背景 Spring的强大和灵活性不用再强调了.而灵活性就是通过一系列的扩展点来实现的,这些扩展点给应用程序提供了参与Spring容器创建的过程,好多定制化的东西都需要 ...

  6. Spring扩展之五:Aware接口等

    ApplicationContextAwareProcessor 1.介绍 ApplicationContextAwareProcessor是一个Spring内部工具,它实现了接口BeanPostPr ...

  7. Spring学习笔记之五----Spring MVC

    Spring MVC通常的执行流程是:当一个Web请求被发送给Spring MVC Application,Dispatcher Servlet接收到这个请求,通过HandlerMapping找到Co ...

  8. spring扩展点之一:BeanFactoryPostProcessor和BeanPostProcessor

    一.BeanFactoryPostProcessor和BeanPostProcessor的区别 BeanFactoryPostProcessor和BeanPostProcessor都是spring初始 ...

  9. Spring扩展:替换IOC容器中的Bean组件 -- @Replace注解

    1.背景:     工作中是否有这样的场景?一个软件系统会同时有多个不同版本部署,比如我现在做的IM系统,同时又作为公司的技术输出给其他银行,不同的银行有自己的业务实现(比如登陆验证.用户信息查询等) ...

随机推荐

  1. idea中 Application Server not specified

    一.问题 idea中的tomcat报错: Application Server not specified 二.解决 原因是没有关联本地的tomcat,关联本地tomcat即可

  2. 一个APACHE TOMCAT漏洞修复

    这种情况加个SSL证书就行了  就是HTTPS协议

  3. IntelliJ idea SpringBoot打war包

    简单易用的使用idea 将SpringBoot工程打war包的方法 pom.xml中添加标签 1. 声明打包格式 <packaging>war</packaging> 2.  ...

  4. javascript之DOM(四其他类型)

    一.Text类型 文本节点由Text类型表示,指的是可以以字面意思解释的纯文本内容,其中包含HTML代码. nodeType=3 nodeName=#text nodeValue=文本内容 paren ...

  5. django的form 登录组件

    1. 了解form 组件的原理 1.建立好form组件 class>>> 2.了解需要先is_valid() 判断是否可以取值 成功 form.cleaned_data 查看成功认证 ...

  6. 201800624模拟赛T2——回家路上

    题目描述 很多学生都抱怨浪费在回家路上的时间太长.这天dongdong刚走出学校大门,就听说某段路在施工(但不知道是哪条路),有可能导致他回家的时间会变长. Dongdong给出了一张地图,图中标号为 ...

  7. vue watch 深度监听

    watch 是vue 里非常有用的回调函数,监听数据变化,非常方便好用,但是,当监听的数据是个复杂型的数据里,里面的数据变化时普通的监听方式是监听不到的,必须使用深度监听: data() { retu ...

  8. Ansible常用模块整理

    问答题 请总结今天所学的ansible模块,以及各个模块的作用! ping ping模块用来检查目标主机是否在线 例子:ansible webserver -m ping yum yum模块用来在Ce ...

  9. linux学习18 shell脚本基础-bash变量和逻辑运行

    一.回顾 1.用户管理,权限管理,install,mktemp 2.用户管理: 3.权限管理: mode,ownership mode: user group other r w x 4.命令:ins ...

  10. Pandas | 14 统计函数

    统计方法有助于理解和分析数据的行为.可以将这些统计函数应用到Pandas的对象上. pct_change()函数 系列,DatFrames和Panel都有pct_change()函数.此函数将每个元素 ...