SpringBoot 容器刷新前回调ApplicationContextInitializer

 更新时间:2022年12月21日 09:26:43   作者:一灰灰  
这篇文章主要为大家介绍了SpringBoot 容器刷新前回调ApplicationContextInitializer使用示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

引言

本文将作为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的资料请关注脚本之家其它相关文章!

相关文章

  • SpringBoot快速实现接口消息加密的过程详解

    SpringBoot快速实现接口消息加密的过程详解

    在项目中,为了保证数据的安全,我们常常会对传递的数据进行加密,常用的加密算法包括对称加密(AES)和非对称加密(RSA),博主选取码云上最简单的API加密项目进行下面的讲解,需要的朋友可以参考下
    2023-11-11
  • Swift洗牌动画效果的实现方法

    Swift洗牌动画效果的实现方法

    这篇文章主要介绍了Swift洗牌动画效果的实现方法,非常不错,具有参考借鉴价值,需要的朋友参考下吧
    2016-12-12
  • java实现简易的计算器界面

    java实现简易的计算器界面

    这篇文章主要为大家详细介绍了java实现简易的计算器界面,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-04-04
  • java实现菜单树的示例代码

    java实现菜单树的示例代码

    这篇文章主要为大家详细介绍了java实现菜单树的相关知识,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以跟随小编一起学习一下
    2023-12-12
  • 5分钟搞懂java注解@Annotation的具体使用

    5分钟搞懂java注解@Annotation的具体使用

    这篇文章主要介绍了5分钟搞懂java注解@Annotation的具体使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-05-05
  • spring cloud gateway整合sentinel实现网关限流

    spring cloud gateway整合sentinel实现网关限流

    这篇文章主要介绍了spring cloud gateway整合sentinel实现网关限流,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-01-01
  • Java报错:UnsupportedOperationException in Collections的解决方案

    Java报错:UnsupportedOperationException in Collection

    在Java编程中,UnsupportedOperationException是一种常见的运行时异常,通常在试图对不支持的操作执行修改时发生,它表示当前操作不被支持,本文将深入探讨UnsupportedOperationException的产生原因,并提供具体的解决方案和最佳实践,需要的朋友可以参考下
    2024-06-06
  • SpringBoot常用数据库开发技术汇总介绍

    SpringBoot常用数据库开发技术汇总介绍

    Spring Boot常用的数据库开发技术有JDBCTemplate、JPA和Mybatis,它们分别具有不同的特点和适用场景,可以根据具体的需求选择合适的技术来进行开发
    2023-04-04
  • Java实现快速排序算法可视化的示例代码

    Java实现快速排序算法可视化的示例代码

    快速排序算法通过多次比较和交换来实现排序,是对冒泡排序算法的一种改进。本文将用Java语言实现快速排序算法并进行可视化,感兴趣的可以了解一下
    2022-08-08
  • SpringCloud-Alibaba-Nacos启动失败解决方案

    SpringCloud-Alibaba-Nacos启动失败解决方案

    这篇文章主要介绍了SpringCloud-Alibaba-Nacos启动失败解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-04-04

最新评论