一、 ApplicationContextInitializer 介绍

  首先看spring官网的介绍:

  翻译一下:

  • 用于在spring容器刷新之前初始化Spring ConfigurableApplicationContext的回调接口。(剪短说就是在容器刷新之前调用该类的 initialize 方法。并将 ConfigurableApplicationContext 类的实例传递给该方法)
  • 通常用于需要对应用程序上下文进行编程初始化的web应用程序中。例如,根据上下文环境注册属性源或激活配置文件等。
  • 可排序的(实现Ordered接口,或者添加@Order注解)

  看完这段解释,为了讲解方便,我们先看自定义 ApplicationContextInitializer 的三种方式。再通过SpringBoot的源码,分析生效的时间以及实现的功能等。

二、三种实现方式

  首先新建一个类 MyApplicationContextInitializer 并实现 ApplicationContextInitializer 接口。

 public class MyApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("-----MyApplicationContextInitializer initialize-----");
}
}

  2.1、mian函数中添加

  优雅的写一个SpringBoot的main方法

 @SpringBootApplication
public class MySpringBootApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MySpringBootApplication.class);
application.addInitializers(new MyApplicationContextInitializer());
application.run(args);
}
}

  运行,查看控制台:生效了

  

  2.2、配置文件中配置

context.initializer.classes=org.springframework.boot.demo.common.MyApplicationContextInitializer 

  

  2.3、SpringBoot的SPI扩展---META-INF/spring.factories中配置

org.springframework.context.ApplicationContextInitializer=org.springframework.boot.demo.common.MyApplicationContextInitializer

  

三、排序问题

  如图所示改造一下mian方法。打一个断点,debug查看排序情况。

  

  给 MyApplicationContextInitializer 加上Order注解:我们指定其拥有最高的排序级别。(越高越早执行)

 @Order(Ordered.HIGHEST_PRECEDENCE)
public class MyApplicationContextInitializer implements ApplicationContextInitializer{
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("-----MyApplicationContextInitializer initialize-----");
}
}

  下面我们通过debug分别验证二章节中提到的三种方法排序是否都是可以的。

  首先验证2.1章节中采用的main函数中添加:debug,断点处查看 application.getInitializers() 这行代码的结果可见,排序生效了。

  

  然后再分别验证2.2和2.3章节中的方法。排序都是可以实现的。

  然而当采用2.3中的SPI扩展的方式,排序指定 @Order(Ordered.LOWEST_PRECEDENCE) 排序并没有生效。当然采用实现Ordered接口的方式,排序验证结果都是一样的。

四、通过源码分析ApplicationContextInitializer何时被调用

  debug差看上文中自定的 MyApplicationContextInitializer 的调用栈。

  

  可见 ApplicationContextInitializer 在容器刷新前的准备阶段被调用。 refreshContext(context);

  在SpringBoot的启动函数中, ApplicationContextInitializer

     public ConfigurableApplicationContext run(String... args) {
//记录程序运行时间
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// ConfigurableApplicationContext Spring 的上下文
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//从META-INF/spring.factories中获取监听器
//1、获取并启动监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//2、构造容器环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//处理需要忽略的Bean
configureIgnoreBeanInfo(environment);
//打印banner
Banner printedBanner = printBanner(environment);
///3、初始化容器
context = createApplicationContext();
//实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[]{ConfigurableApplicationContext.class}, context);
//4、刷新容器前的准备阶段
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//5、刷新容器
refreshContext(context);
//刷新容器后的扩展接口
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
} catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
} try {
listeners.running(context);
} catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}

  然后看在 refreshContext(context); 具体是怎么被调用的。

 private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
...
}

  然后在 applyInitializers 中遍历调用每一个被加载的 ApplicationContextInitializer 的  initialize(context);  方法,并将 ConfigurableApplicationContext 的实例传递给 initialize 方法。

 protected void applyInitializers(ConfigurableApplicationContext context) {
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
initializer.getClass(), ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}

  OK,到这里通过源码说明了 ApplicationContextInitializer 是何时及如何被调用的。

SpringBoot之ApplicationContextInitializer的理解和使用的更多相关文章

  1. ApplicationContextInitializer的理解和使用

    一.ApplicationContextInitializer 介绍 1.1 作用 ApplicationContextInitializer 接口用于在 Spring 容器刷新之前执行的一个回调函数 ...

  2. 对SpringBoot和SpringCloud的理解

    1.SpringCloud是什么 SpringCloud基于SpringBoot提供了一整套微服务的解决方案,包括服务注册与发现,配置中心,全链路监控,服务网关,负载均衡,熔断器等组件,除了基于Net ...

  3. SpringBoot启动流程分析(一):SpringApplication类初始化过程

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  4. SpringBoot集成Socket服务后打包(war包)启动时如何启动Socket服务(web应用外部tomcat启动)

      1.首先知道SpringBoot打包为jar和war包是不一样的(只讨论SpringBoot环境下web应用打包)     1.1.jar和war包的打开方式不一样,虽然都依赖java环境,但是j ...

  5. 很详细的SpringBoot整合UEditor教程

    很详细的SpringBoot整合UEditor教程 2017年04月10日 20:27:21 小宝2333 阅读数:21529    版权声明:本文为博主原创文章,未经博主允许不得转载. https: ...

  6. SpringBoot AOP 与 IoC

    Spring的核心就是AOP与IoC,想要学习SpringBoot,首先得理解这些概念: AOP(Aspect Oriented Programming 面向切面编程) IoC(Inversion o ...

  7. (一)SpringBoot入门【基于2.x版本】

    SpringBoot入门[基于2.x版本] 一.SpringBoot简介 首先大家学习SpringBoot的话,我希望大家是有一定java基础的,如果是有Spring的基础的话,上手会更加得心应手,因 ...

  8. SpringBoot基础篇-SpringBoot快速入门

    SpringBoot基础 学习目标: 能够理解Spring的优缺点 能够理解SpringBoot的特点 能够理解SpringBoot的核心功能 能够搭建SpringBoot的环境 能够完成applic ...

  9. MacOS下SpringBoot基础学习

    学于黑马和传智播客联合做的教学项目 感谢 黑马官网 传智播客官网 微信搜索"艺术行者",关注并回复关键词"springboot"获取视频和教程资料! b站在线视 ...

随机推荐

  1. 安装 Win7 的系统的时候如何分区

    解决方案 在安装Win7的系统的时候,可以使用下面方法进行分区: 1. 在出现同意许可条款,勾选“我接受许可条款(A)”后,点击下一步,然后继续下面操作: 2. 进入分区界面,点击“驱动器选项(高级) ...

  2. 01_8_Struts用DomainModel接收参数

    01_8_Struts用DomainModel接收参数 1. 配置struts.xml文件 <package name="user" namespace="/use ...

  3. 当c++遇上音乐

    运用到的函数 #include <windows.h> Beep( f, t ); Sleep( t ); eep() 函数可以让蜂鸣器发出频率为f赫兹,音长大约为 2t 毫秒的音.(注意 ...

  4. Linux-CentOS6.9启动流程排错

    1 . CentOS6.9启动流程 POST 加电自检,是BIOS功能的一个主要部分.负责完成对CPU.主板.内存.硬盘子系统.显示子系统.串并行接口.键盘.CD-ROM光驱等硬件情况的检测. Boo ...

  5. 使用Spring MVC后实现一个BaseController

    使用Spring MVC技术后,可以实现一个基类的Controller类来分装一些MVC常用的方法,其他的Controller都继承自这个BaseController,这样在使用常用的方法时将会变得非 ...

  6. LeetCode(232) Implement Queue using Stacks

    题目 Implement the following operations of a queue using stacks. push(x) – Push element x to the back ...

  7. Linux学习-SELinux 初探

    什么是 SELinux 什么是 SELinux 呢?其实他是『 Security Enhanced Linux 』的缩写,字面上的意义就是安全强化的 Linux 之意! 当初设计的目标:避免资源的误用 ...

  8. HBase0.94.2-cdh4.2.0需求评估测试报告1.0之四

    第二组:文件存储读过程记录 第一组:一个列,四个分区,随机ID 测试列和分区 测试程序或命令 导入文件大小(Mb) 导入文件个数(个) 是否触发flush事件(布尔) 是否触发compact事件(布尔 ...

  9. P2598 [ZJOI2009]狼和羊的故事(最小割)

    P2598 [ZJOI2009]狼和羊的故事 题目描述 “狼爱上羊啊爱的疯狂,谁让他们真爱了一场:狼爱上羊啊并不荒唐,他们说有爱就有方向......” Orez听到这首歌,心想:狼和羊如此和谐,为什么 ...

  10. 【15】ES6 for Humans: The Latest Standard of JavaScript: ES2015 and Beyond

    [15]ES6 for Humans 共148页: 目前看到:已经全部阅读.   亚马逊地址: 魔芋:总结: 我先看的是阮一峰的在线书籍.这本书的内容很多都与之重复的. 居然卖¥463.也是没谁了. ...