一、 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. C++ 内存分配操作符new和delete详解

    重载new和delete 首先借用C++ Primer 5e的一个例子: string *sp = new string("a value"); ]; 这其实进行了以下三步操作: ...

  2. Bootstrap历练实例:动画的进度条

    <!DOCTYPE html><html><head><meta http-equiv="Content-Type" content=&q ...

  3. 解决cocos2dx 打包lua环境搭建问题( ImportError: No module named Cheetah.Template)

    将c++ 封装成lua调用时,显示一下错误: PYTHON_BIN not defined, use current python. generating userconf.ini... Genera ...

  4. ES6_Promise 对象 阮一锋

    Promise的含义 promise是异步编程的一种解决方法,比传统的回调函数和事件更合理更强大.他由社区最早提出和实现,ES6将其写进语言标准,统一了用法,原生提供了promise对象.所谓prom ...

  5. 转 fine-tuning (微调)

    https://blog.csdn.net/weixin_42137700/article/details/82107208

  6. 【dsu || 线段树合并】bzoj4756: [Usaco2017 Jan]Promotion Counting

    调半天原来是dsu写不熟 Description The cows have once again tried to form a startup company, failing to rememb ...

  7. json转换为字典

    str---dict ata_dict=json.loads(data)

  8. 移动端H5前端性能优化指南:

    分享地址:https://isux.tencent.com/h5-performance.html

  9. zend studio10 破解方法

    zend studio 是一款强大的PHP开发工具,该软件的具体信息用户可以百度一下,下面是zend studio10的破解方法. 1.下载zend studio 10,下载地址:链接:http:// ...

  10. leetcode-13-basic-binaryTree

    101. Symmetric Tree 解题思路: 递归的方法如下.分几种情况考虑,如果左子树和右子树都是空,那么返回true:如果不同时为空,返回false:如果都不为空,则 判断其值是否相等,不相 ...