1. SpringBoot初始化器ApplicationContextInitializer使用与源码分析

一. SpingBoot 初始化器简介

  1. ApplicationContextInitializer实际上是Spring容器初始化前ConfigurableApplicationContext的回调接口,可以对上下文环境作一些操作,如运行环境属性注册、激活配置文件等。
  2. 通常自定义的ApplicationContextInitializer有三种注册方式,按照优先级如下:
    配置文件 > 工厂方式 > 启动注册 (相同的注册方式中,可以使用@Order注解来指定优先级,值越小优先级越高)。

二. 自定义初始化器

自定义初始化器需要实现ApplicationContextInitializer接口。

java 复制代码
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;

import java.util.HashMap;
import java.util.Map;

public class FirstInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        ConfigurableEnvironment environment = applicationContext.getEnvironment();

        Map<String, Object> map = new HashMap<>();
        map.put("key1", "value1");
        MapPropertySource mapPropertySource = new MapPropertySource("firstInitializer", map);
        environment.getPropertySources().addLast(mapPropertySource);
        System.out.println("run FirstInitializer");
    }
}

配置完之后,可以通过实现ApplicationContextAware接口去获取对应的值。

java 复制代码
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;

@Service
public class TestService implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public String test() {
        return applicationContext.getEnvironment().getProperty("key1");
    }
}
1. 通过META-INF配置

在resources目录新建/META-INFI/spring.factories文件,并添加如下配置:

shell 复制代码
org.springframework.context.ApplicationContextInitializer=org.example.initializer.FirstInitializer
2. 通过addInitializers方法添加
java 复制代码
public class Main {
    public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication(Main.class);
        springApplication.addInitializers(new FirstInitializer());
        springApplication.run(args);
    }
}
3. 通过application.yaml配置
bash 复制代码
context:
  initializer:
    classes: org.example.initializer.FirstInitializer

三. 源码分析

1. 初始化器注册
  1. 工厂加载方式

    ApplicationContextInitializer工厂加载的实现在SpringApplication类的构造函数中,Spring容器初始化前,先通过getSpringFactoriesInstances得到所有实现类的集合,然后通过setInitializers注入到SpringApplication类的initializers属性中;

    java 复制代码
    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    		this.resourceLoader = resourceLoader;
    		Assert.notNull(primarySources, "PrimarySources must not be null");
    		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    		this.webApplicationType = WebApplicationType.deduceFromClasspath();
    		this.bootstrapRegistryInitializers = new ArrayList<>(
    				getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
    		// 设置初始化器
    		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    		this.mainApplicationClass = deduceMainApplicationClass();
    	}
  2. addInitializers方式

    addInitializers方法实现逻辑比较简单,就是将初始化器直接添加到SpringApplication的成员变量initializers中。

    java 复制代码
    public void addInitializers(ApplicationContextInitializer<?>... initializers) {
    		this.initializers.addAll(Arrays.asList(initializers));
    	}
  3. 配置文件方式

    当初始化器配置在文件application.yaml时,它的注册是通DelegatingApplicationContextInitializer来实现,DelegatingApplicationContextInitializer是SpringBoot的一个内置初始化器,他的优先级最高,所以通过配置文件设置初始化器具有最高的优先级。

    主要的注册逻辑在DelegatingApplicationContextInitializer的initialize中实现。

    java 复制代码
    public void initialize(ConfigurableApplicationContext context) {
    		ConfigurableEnvironment environment = context.getEnvironment();
    		// 获取配置文件中的初始化器的Class对象
    		List<Class<?>> initializerClasses = getInitializerClasses(environment);
    		if (!initializerClasses.isEmpty()) {
    			// 初始化配置的初始化器
    			applyInitializerClasses(context, initializerClasses);
    		}
    	}
2. 初始化执行时机

ApplicationContextInitializer接口实现类的执行时机是在SpringApplication的prepareContext方法,即Spring容器正式刷新前,准备上下文环境时,它里面的applyInitializers方法会遍历已经注册的初始化器,并调用initialize方法。

java 复制代码
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
		...
		// 执行初始化器
		applyInitializers(context);
		...
}
java 复制代码
protected void applyInitializers(ConfigurableApplicationContext context) {
		// 遍历已经注册的初始化器,执行initialize方法
		for (ApplicationContextInitializer initializer : getInitializers()) {
			Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
					ApplicationContextInitializer.class);
			Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
			initializer.initialize(context);
		}
	}
相关推荐
YDS8292 小时前
苍穹外卖 —— Spring Cache和购物车功能开发
java·spring boot·后端·spring·mybatis
星光一影2 小时前
基于SpringBoot智慧社区系统/乡村振兴系统/大数据与人工智能平台
大数据·spring boot·后端·mysql·elasticsearch·vue
劲墨难解苍生苦2 小时前
spring ai alibaba mcp 开发demo
java·人工智能
leonardee2 小时前
Spring 中的 @ExceptionHandler 注解详解与应用
java·后端
不爱编程的小九九2 小时前
小九源码-springboot103-踏雪阁民宿订购平台
java·开发语言·spring boot
Elieal2 小时前
Spring 框架核心技术全解析
java·spring·sqlserver
组合缺一2 小时前
(对标 Spring)OpenSolon v3.7.0, v3.6.4, v3.5.8, v3.4.8 发布(支持 LTS)
java·后端·spring·web·solon
程序员爱钓鱼2 小时前
Python编程实战——Python实用工具与库:Pandas数据处理
后端·python·ipython
凸头2 小时前
Spring Boot接收前端参数的注解总结
前端·spring boot·后端