SpringBoot 容器刷新前回调ApplicationContextInitializer

目录
  • 引言
  • I. 项目准备
  • II. 容器刷新前扩展点实例
    • 1. 自定义ApplicationContextInitializer
    • 2. 扩展点注册
    • 3. 执行顺序指定
    • 4. 使用场景示例
    • 5. 小结
  • III. 不能错过的源码和相关知识点
    • 项目

引言

本文将作为Spring系列教程中源码版块的第一篇,整个源码系列将分为两部分进行介绍;单纯的源码解析,大概率是个吃力没人看的事情,因此我们将结合源码解析,一个是学习下别人的优秀设计,一个是站在源码的角度看一下我们除了日常的CURD之外,还可以干些啥

在Spring的启动过程中,一系列的操作步骤中,提供了很多的扩展点,供我们来增强;简单来说就是提供了很多的钩子,这样当我们在某个节点执行前后,想干点其他的事情时,可以很简单的支持;

本文介绍的ApplicationContextInitializer,spring容器在刷新之前会回调这个接口,从而实现在spring容器未初始化前,干一些用户希望做的事情

I. 项目准备

本文创建的实例工程采用SpringBoot 2.2.1.RELEASE + maven 3.5.3 + idea进行开发

具体的SpringBoot项目工程创建就不赘述了,核心的pom文件,无需额外的依赖

配置文件 application.yml, 也没有什么特殊的配置

源码工程参考文末的源码

II. 容器刷新前扩展点实例

1. 自定义ApplicationContextInitializer

当我们希望实现一个自定义的上下文初始化时,非常简单,实现上面这个接口就行了,如

public class ApplicationContextInitializer01 implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
        System.out.println("ApplicationContextInitializer01");
    }
}

2. 扩展点注册

上面自定义一个扩展点,如何使它生效呢?

官方提供了三种方式,如在启动时,直接进行注册: springApplication.addInitializers(new ApplicationContextInitializer01());

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication(Application.class);
        springApplication.addInitializers(new ApplicationContextInitializer01());
        try (ConfigurableApplicationContext context = springApplication.run(args)) {
        }
    }
}

当我们的扩展点是放在一个jar包中对外提供时,使用上面的启动注册方式显然是不可行的,此时更推荐的做法就是通过Spring的SPI机制进行注册

在资源目录下的META-INF/spring.factories文件中进行注册

org.springframework.context.ApplicationContextInitializer=com.git.hui.extention.context.ApplicationContextInitializer02

说明

  • 上面SPI的机制非常推荐大家使用,在之前的文章中,AutoConfiguration的注册通常也是使用这种方式

除了上面的两种注册方式之外,另外还有一个配置文件的方式,在配置文件application.propertiesapplication.yml中,如下配置

context:
  initializer:
    classes: com.git.hui.extention.context.ApplicationContextInitializer03

启动测试

上面三种注册方式,我们实现三个自定义的扩展点,然后启动之后,看一下实际输出

上面的输出,可以简单的得出一个结论,不同注册方式的优先级(为了更合理的验证下面的观点,推荐大家修改下上面三个自定义扩展点名,排除掉是因为扩展名导致的排序问题)

  • 配置文件注册 > SPI注册 > 启动时注册

3. 执行顺序指定

对于自定义的扩展点实现,当存在顺序关系时,我们可以通过@Order注解来实现, 如当上面的三个扩展点都是通过启动方式注册时

@Order(5)
public class ApplicationContextInitializer01 implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
        System.out.println("ApplicationContextInitializer01");
    }
}

@Order(2)
public class ApplicationContextInitializer02 implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
        System.out.println("ApplicationContextInitializer02");
    }
}

@Order(10)
public class ApplicationContextInitializer03 implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
        System.out.println("ApplicationContextInitializer03");
    }
}

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication(Application.class);
        springApplication.addInitializers(new ApplicationContextInitializer01(), new ApplicationContextInitializer02(), new ApplicationContextInitializer03());
        try (ConfigurableApplicationContext context = springApplication.run(args)) {
        }
    }
}

输出实例如下

接着重点来了

  • 若上面的三个自定义实现,不是相同的注册方式,如将03采用配置文件方式进行注册,那么01, 02 依然是启动注册
  • 则顺序是 03 > 02 > 01
  • @Order注解修饰的顺序,并不能打破 配置文件 > SPI > 启动方式注册的顺序

关于自定义实现类的执行顺序,规则如下

  • 配置文件 > SPI > 启动方式
  • 相同的注册方式,可以通过 @Order 注解进行修饰,值越小则优先级越高

4. 使用场景示例

最后我们再来看一下,这个扩展点到底有什么用,我们再什么场景下会用到这个呢?

一个经常可以看到的应用场景如通过它来指定需要激活的配置文件

public class ApplicationContextInitializer03 implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
        // 指定激活prod对应的配置文件
        configurableApplicationContext.getEnvironment().setActiveProfiles("prod");
    }
}

但是一般也很少见到有人这么干,因为直接使用配置参数就行了,那么有场景需要这么做么?

答案当然是有的,比如现在广为流行的docker容器部署,当我们希望每次都是打同一个镜像,然后在实际运行的时候,根据不同的环境来决定当前镜像到底启用哪些配置文件,这时就有用了

比如我们通过容器的环境参数 app.env 来获取当前运行的环境,如果是prod,则激活application-prod.yml; 如果是test,则激活application-test.yml

那么此时可以这么干

public class EenvActiveApplicationContextInitializer implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
        String env = System.getenv("app.env");
        if ("prod".equalsIgnoreCase(env)) {
            configurableApplicationContext.getEnvironment().setActiveProfiles("prod");
        } else if ("test".equalsIgnoreCase(env)) {
            configurableApplicationContext.getEnvironment().setActiveProfiles("test");
        } else {
            throw new RuntimeException("非法的环境参数:" + env);
        }
    }
}

5. 小结

本文作为扩展点的第一篇,通过实现ApplicationContextInitializer接口,从而达到在spring容器刷新之前做某些事情的目的

通常自定义的ApplicationContextInitializer有三种注册方式,按照优先级如下

  • 配置文件 > SPI方式 > 启动方式注册
  • 相同的注册方式中,可以使用@Order注解来指定优先级,值越小优先级越高

最后还给出了一个可以应用的实例场景,即如何实现一个镜像在不同的环境中启动运行

下一个扩展点我们将介绍如何通过BeanDefinitionRegistryPostProcessor来实现非Spring生态的Bean加载使用

III. 不能错过的源码和相关知识点

项目

工程:github.com/liuyueyi/sp…

源码:github.com/liuyueyi/sp…

以上就是SpringBoot 容器刷新前回调ApplicationContextInitializer的详细内容,更多关于SpringBoot ApplicationContextInitializer的资料请关注我们其它相关文章!

(0)

相关推荐

  • springboot如何关掉tomcat容器

    目录 springboot关掉tomcat容器 springboot使用第三方tomcat 1.改pom 2.再加一个启动类 3.打war包 springboot关掉tomcat容器 有的时候需要对外提供的并不是HTTP服务,而是RPC服务,但是又想使用springboot提供的便利支持. 这个时候需要关掉RPC服务,然后在main函数中自己添加守护线程 public static void main(String[] args) { SpringApplication app = new Sp

  • SpringBoot 如何从容器中获取对象

    目录 如何从容器中获取对象 SpringBoot中的容器 容器功能 1.组件添加 2.原生配置文件引入(xml文件引入) 3.配置绑定 如何从容器中获取对象 有时候在项目中,我们会自己创建一些类,类中需要使用到容器中的一些类.方法是新建类并实现ApplicationContextAware 接口,在类中建立静态对象 ApplicationContext 对象,这个对象就如同xml配置中的 applicationContext.xml,容器中类都可以获取到. 例如@Service. @Compon

  • SpringBoot详细讲解如何创建及刷新Spring容器bean

    目录 一.前期准备 1.1 创建工程 1.2 创建Controller 二.探究过程 2.1 启动类 2.2 SpringApplication 2.3 ApplicationContextFactory 2.4 SpringApplication 2.5 结论 参考视频:https://www.bilibili.com/video/BV1Bq4y1Q7GZ?p=6 通过视频的学习和自身的理解整理出的笔记. 一.前期准备 1.1 创建工程 创建springboot项目,springboot版本为

  • springboot 项目容器启动后如何自动执行指定方法

    目录 springboot 项目容器启动后自动执行指定 springboot 容器及启动过程 问题1:为什么要启动父子两个容器? 问题2:在什么时候启动父容器? 问题3:父容器和子容器的区别? 问题4:怎么保证父容器启动过程中 问题5:容器实际通过什么来管理bean springboot 项目容器启动后自动执行指定 我们需要写一个class去让它实现ApplicationRunner,然后重写它的run方法就行了,注意类加上@Component注解 注意:这个class需要写在和Applicat

  • SpringBoot向容器注册bean的方法详解

    目录 简介 法1:@Component 法2:@Configuration+@Bean 法3:@Import等 法4:FactoryBean 简介 本文用示例介绍SpringBoot如何向容器注册bean(即:将对象加入容器). 法1:@Component (@Controller/@Service/@Repository也可以,因为它里边包含@Component) 默认是加载和Application类所在同一个目录下的所有类,包括所有子目录下的类. 当启动类和@Component分开时,如果启

  • 大厂禁止SpringBoot在项目使用Tomcat容器原理解析

    目录 前言 SpringBoot中的Tomcat容器 SpringBoot设置Undertow Tomcat与Undertow的优劣对比 最后 前言 在SpringBoot框架中,我们使用最多的是Tomcat,这是SpringBoot默认的容器技术,而且是内嵌式的Tomcat.同时,SpringBoot也支持Undertow容器,我们可以很方便的用Undertow替换Tomcat,而Undertow的性能和内存使用方面都优于Tomcat,那我们如何使用Undertow技术呢?本文将为大家细细讲解

  • SpringBoot 容器刷新前回调ApplicationContextInitializer

    目录 引言 I. 项目准备 II. 容器刷新前扩展点实例 1. 自定义ApplicationContextInitializer 2. 扩展点注册 3. 执行顺序指定 4. 使用场景示例 5. 小结 III. 不能错过的源码和相关知识点 项目 引言 本文将作为Spring系列教程中源码版块的第一篇,整个源码系列将分为两部分进行介绍:单纯的源码解析,大概率是个吃力没人看的事情,因此我们将结合源码解析,一个是学习下别人的优秀设计,一个是站在源码的角度看一下我们除了日常的CURD之外,还可以干些啥 在

  • SpringBoot启动应用及回调监听原理解析

    这篇文章主要介绍了SpringBoot启动应用及回调监听原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 主类Main方法 public static void main(String[] args) { SpringApplication.run(SpringBootRunApplication.class, args); } 创建SpringApplication对象 public static ConfigurableApplica

  • SpringBoot+MyBatisPlus+Vue 前后端分离项目快速搭建过程(前端篇)

    后端篇 SpringBoot+MyBatisPlus+Vue 前后端分离项目快速搭建[后端篇][快速生成后端代码.封装结果集.增删改查.模糊查找][毕设基础框架] 前端篇 创建vue项目 1.找个文件夹进入命令行,输入:vue create vue-front 2.直接回车,等待片刻,稍微有点小久 3.根据提示指令测试 打开浏览器输入:http://localhost:8080/ 安装所需工具 安装的工具会有点多,为了提供更好的拓展性,可以自主选择安装(不建议),后面的代码中都是使用到了,不安装

  • springboot vue接口测试前后端树节点编辑删除功能

    目录 基于springboot+vue 的测试平台开发 一.编辑功能 1. 编辑页外显 2. 实现后端接口 3. 前后联调 4. 测试 二.删除功能 1. 后端接口 2. 前端实现 3. 测试 基于springboot+vue 的测试平台开发 继续更新. 一.编辑功能 1. 编辑页外显 点击树节点的编辑按钮,打开对话框,展示原有的节点名称. 本来树形控件里没有自带的编辑操作,我直接加了一个按钮在上面,绑定点击事件调用edit(data)方法. 为了确认 data 可用,我在edit方法中打印了一

  • Spring容器刷新obtainFreshBeanFactory示例详解

    目录 Spring容器刷新—02—obtainFreshBeanFactory BeanFactory和ApplicationContext obtainFreshBeanFactory 1.GenericApplicationContext系列的实现 2.AbstractRefreshableApplicationContext系列的实现 该使用哪个BeanFactory? Servlet环境 SpringBoot环境 Spring容器刷新—02—obtainFreshBeanFactory

  • SpringBoot+MyBatisPlus+Vue 前后端分离项目快速搭建过程(后端)

    数据库准备 data_test.sql: /* SQLyog Enterprise v12.08 (64 bit) MySQL - 5.7.31 : Database - data_test ********************************************************************* */ /*!40101 SET NAMES utf8 */; /*!40101 SET SQL_MODE=''*/; /*!40014 SET @OLD_UNIQUE_

  • SpringBoot+VUE实现前后端分离的实战记录

    一,前端VUE项目 这里使用VUE UI创建一个VUE项目 命令行输入vue ui进入 手动配置项目 选中这三个 点击下一步->点击创建项目 用IDEA打开刚才创建的项目 IDEA中的安装vue插件并重启 IDEA控制台中输入vue add axios安装axios 新建一个Show.vue 在index,js的routes中配置它的路由 编写Show,vue向后端请求数据并展示 <template> <div> <table> <tr> <td

  • 学习SpringBoot容器功能及注解原理

    目录 1.组件添加 1.1@Configuration 1.2@Import 1.3@Conditional 2.原生配置文件引入 2.1@ImportResource 3.配置绑定 3.1@ConfigurationProperties 3.2@EnableConfigurationProperties + @ConfigurationProperties 1.组件添加 1.1@Configuration @Configuration:告诉SpringBoot这是一个配置类 配置类里面使用@B

  • springboot vue项目管理前后端实现编辑功能

    目录 基于springboot+vue 的测试平台开发 一.打开编辑页面显示数据 1. 编辑按钮 2. 编写 handleUpdate 方法处理数据外显 二.保存编辑页面的内容 1. 后端增加 update 接口 2. 前端页面修改 基于springboot+vue 的测试平台开发 继续更新 今天实现项目列表的编辑功能: 点击[编辑]按钮,打开对话框,显示此条记录的信息修改对话框表单内容,点击对话框[保存]按钮,更新此条记录 一.打开编辑页面显示数据 1. 编辑按钮 之前复制的 table 组件

  • springboot vue接口测试前后端实现模块树列表功能

    目录 基于 springboot+vue 的测试平台 一.存放接口的js文件 二.在vue文件中调用接口 1. 触发接口 2. 调用接口 三.实现选择项目功能 1. 后端增加获取所有项目接口 2. 前端调用接口 3. 选择项目触发模块树接口 4. 测试一下 基于 springboot+vue 的测试平台 开发继续更新. 上一篇完成了模块树的列表接口,接下来可以和前端页面联调了. 一.存放接口的js文件 老样子,新建js文件,把要调用的后端接口请求封装在 src/api/apiModule.js这

随机推荐