架构のSpring扩展点(一):上下文创建前的动态处理-​ApplicationContextInitializer

77 阅读3分钟

🍅 作者简介:大齐,自学Java入门,现在某国企担任初级架构师 🍅 有自己独立的学习方法,以及适合大部分人的学习路线,面试架构思路等 🍅 关注公众号【大齐架构】,查看最新最全的知识分享

Spring 的扩展点 是Spring易扩展的一个重要体现,熟悉这些扩展点的定义方式,以及其调用时机,不仅成为工作中利器,也能深度理解Spring框架的切入点。

ApplicationContextInitializer接口的扩展点

ApplicationContextInitializer 接口用于在 Spring 容器刷新之前执行的一个回调函数,通常用于向 SpringBoot 容器中注入属性。

来源

org.springframework.context它是由Spring本身提供的原生接口。

package org.springframework.context;
// ConfigurableApplicationContext刷新前的回调接口,一般用在web环境中
// 针对ApplicationContext需要一些程序化定制的场景,比如设置Environment
// 的profile相关环境变量
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
 
  // 初始化给定的applicationContext
  void initialize(C applicationContext);
 
}

Spring内置实现类

ContextIdApplicationContextInitializer

根据下列环境属性设置的值,设置Spring应用上下文的ID 默认值为application

  • spring.application.name
  • vcap.application.name
  • spring.config.name
  • spring.application.index
  • vcap.application.instance_index

ServerPortInfoApplicationContextInitializer

将内置 servlet容器实际使用的监听端口写入到 Environment 环境属性中。这样属性 local.server.port 就可以直接通过 @Value 注入到测试中,或者通过环境属性 Environment 获取。

三种实现方式

Spring上下文Context对象注册

@SpringBootApplication
public class SpringbootHelloWorldApplication {
 
    public static void main(String[] args) {
 
 
        SpringApplication springApplication = new SpringApplication(SpringbootHelloWorldApplication.class);
        springApplication.addInitializers(ac -> System.out.println("initializer registered--->"));
        springApplication.run(args);
    }
}

配置文件加载

在application.properties文件中配置如下内容: context.initializer.classes=dongshi.daddy.contextinitializer.MyContextInitializer。

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

在spring.factories添加配置

在项目下的resources下新建META-INF文件夹,文件夹下新建spring.factories文件: org.springframework.context.ApplicationContextInitializer=com.daq.config.MyContextInitializer

实现Api接口

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

三种方式最终的控制台输出结果都是一样的 .

  ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::       (v2.1.14.RELEASE)
initializer registered--->

Spring内部调用扩展点的原理

org.springframework.boot.SpringApplication#prepareContext,源码如下:

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
      SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
  ......
  // 调用方法
  applyInitializers(context);
  ......
}

调用方法applyInitializers的具体逻辑:

@SuppressWarnings({ "rawtypes", "unchecked" })
protected void applyInitializers(ConfigurableApplicationContext context) {
  // 获取所有的ApplicationContextInitializer,并循环调用其initialize方法
  for (ApplicationContextInitializer initializer : getInitializers()) {
    Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
        ApplicationContextInitializer.class);
    Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
    initializer.initialize(context);
  }
}

应用场景

动态加载配置:

您可以在应用程序上下文创建之前加载一些动态的配置,例如从外部配置文件中读取配置信息并注入到Spring的环境中。

执行额外的初始化逻辑:

如果您有一些需要在应用程序上下文启动之前执行的初始化逻辑,例如初始化数据库连接池或启动一些后台任务

public class CustomApplicationContextInitializer implements
            ApplicationContextInitializer<ConfigurableApplicationContext> 
{
        @override
        public void initialize(ConfigurableApplicationContext applicationContext) (
        MutablePropertySources propertySources =
                applicationContext.getEnvironment().getPropertySources();
 
        //创建自定义的属性源
        Map<String, Object> customProperties = new HashMap<>();
        customProperties.put("custom.property","custom value");
        MapPropertySource customPropertySource =new MapPropertySource("customPropertySource",customProperties);
        //将自定义属性源添加到应用程序上下文的属性源列表中
        propertySources.addFirst(customPropertySource);
        );
    }

👉 近期文章精选

架构のSpring扩展点(一):上下文创建前的动态处理-ApplicationContextInitializer

架构のSpring扩展点(二):Bean定义操作-BeanDefinitionRegistryPostProcessor

架构のSpring扩展点(三):Bean生命周期操作-InstantiationAwareBeanPostProcessor

架构のSpring扩展点(四):Bean初始化时对象自动注入-Aware全解析

架构のSpring扩展点(五):如何保证在同一线程内获取的bean是同一对象-自定义Scope

架构のSpring扩展点(六):ApplicationContextAwareProcessor接口全解析,看完就懂

认知结构的提升,需要合适的思考方法-架构提升