一、 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. thinkphp的使用——隐藏index.php

    官方默认的.htaccess文件 <IfModule mod_rewrite.c>  Options +FollowSymlinks -Multiviews  RewriteEngine ...

  2. Diff Two Arrays-freecodecamp算法题目

    Diff Two Arrays(比较两个数组) 1.要求 比较两个数组,然后返回一个新数组 该数组的元素为两个给定数组中所有独有的数组元素.换言之,返回两个数组的差异. 2.思路 定义一个新数组变量, ...

  3. 第一课:PHP 文件是什么?

    PHP 文件是什么? PHP 文件可包含文本.HTML.JavaScript代码和 PHP 代码 PHP 代码在服务器上执行,结果以纯 HTML 形式返回给浏览器 PHP 文件的默认文件扩展名是 &q ...

  4. GoF23种设计模式之行为型模式之策略模式

    传送门 ☞ 轮子的专栏 ☞ 转载请注明 ☞ http://blog.csdn.net/leverage_1229 1概述           定义一系列算法,把它们一个个都封装起来,并且让它们可以相互 ...

  5. Python9-hashilib模块-day28(大年初三)

    __getitem__\__setitem__\__delitem__ class Foo: def __init__(self,name,age,sex): self.name = name sel ...

  6. 纯虚函数(pure virtual function )和抽象类(abstract base class)

    函数体=0的虚函数称为“纯虚函数”.包含纯虚函数的类称为“抽象类” #include <string> class Animal // This Animal is an abstract ...

  7. nslookup、dig命令Linux安装包

    linux下提供nslookup,dig命令的软件就是 bind-utils yum install bind-utils -y

  8. Python动态属性和特性(二)

    内置的property经常用作装饰器,但它其实是一个类.在Python中,函数和类通常可以互换,因为二者都是可调用对象,而且没有实例化的new运算符,所以调用构造方法和调用工厂函数没有区别,只要能返回 ...

  9. luogu1169 [ZJOI2007]棋盘制作

    悬线法 #include <iostream> #include <cstring> #include <cstdio> using namespace std; ...

  10. 用上GIT你一定会爱上他

    前言 Git是一个开源的分布式版本控制系统,用以有效.高速的处理从很小到非常大的项目版本管理. Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控 ...