Springboot 内置ApplicationContextInitializer

103 篇文章 18 订阅

本文代码基于 Springboot 2.1.0

概述

在包org.springframework.context中提供了这样一个接口ApplicationContextInitializer :

public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {

	/**
	 * 初始化给定的 application context.
	 * @param applicationContext 需要被初始化的 application context
	 */
	void initialize(C applicationContext);

}

这是一个用来初始化Spring ConfigurableApplicationContext应用上下文的回调接口,设定的调用时机是在ConfigurableApplicationContext#refresh()调用之前。

该接口典型的应用场景是web应用中需要编程方式对应用上下文做初始化。比如,注册属性源(property sources)或者针对上下文的环境信息environment激活相应的profile

使用分析

在一个Springboot应用中,classpath上会包含很多jar包,有些jar包需要在ConfigurableApplicationContext#refresh()调用之前对应用上下文做一些初始化动作,因此它们会提供自己的ApplicationContextInitializer实现类,然后放在自己的META-INF/spring.factories属性文件中,这样相应的ApplicationContextInitializer实现类就会被SpringApplication#initialize发现:

// SpringApplication#initialize方法,在其构造函数内执行,从而确保在其run方法之前完成
private void initialize(Object[] sources) {
		if (sources != null && sources.length > 0) {
			this.sources.addAll(Arrays.asList(sources));
		}
		this.webEnvironment = deduceWebEnvironment();
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));//   <===================
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

然后在应用上下文创建之后,应用上下文刷新(refresh)之前的准备阶段被调用 :

	// SpringApplication#run方法
	public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		FailureAnalyzers analyzers = null;
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			Banner printedBanner = printBanner(environment);
			context = createApplicationContext();
			analyzers = new FailureAnalyzers(context);
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner); // <===================
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			listeners.finished(context, null);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
			return context;
		}
		catch (Throwable ex) {
			handleRunFailure(context, listeners, analyzers, ex);
			throw new IllegalStateException(ex);
		}
	}
// SpringApplication#prepareContext方法
private void prepareContext(ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
		context.setEnvironment(environment);
		postProcessApplicationContext(context);
		applyInitializers(context); // <==============
		listeners.contextPrepared(context);
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}

		// Add boot specific singleton beans
		context.getBeanFactory().registerSingleton("springApplicationArguments",
				applicationArguments);
		if (printedBanner != null) {
			context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
		}

		// Load the sources
		Set<Object> sources = getSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[sources.size()]));
		listeners.contextLoaded(context);
	}
	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);
		}
	}

常见的一些ApplicationContextInitializer实现

下面列出了一个使用缺省配置的springboot web应用所使用到的ApplicationContextInitializer实现:

实现类介绍
DelegatingApplicationContextInitializerorg.sf.boot.context.config使用环境属性context.initializer.classes指定的初始化器(initializers)进行初始化工作,如果没有指定则什么都不做
ContextIdApplicationContextInitializerorg.sf.boot.context设置Spring应用上下文的ID,会参照环境属性:
spring.application.name
vcap.application.name
spring.config.name
spring.application.index
vcap.application.instance_index
PORT
如果这些属性都没有,ID使用"application"。
ConfigurationWarningsApplicationContextInitializerorg.sf.boot.context对于一般配置错误在日志中作出警告
ServerPortInfoApplicationContextInitializerorg.sf.boot.context.embedded将内置servlet容器实际使用的监听端口写入到Environment环境属性中。这样属性"local.server.port"就可以直接通过@Value注入到测试中,或者通过环境属性Environment获取。
SharedMetadataReaderFactoryContextInitializerorg.sf.boot.autoconfigure创建一个SpringBoot和ConfigurationClassPostProcessor共用的CachingMetadataReaderFactory对象,实现类使用ConcurrentReferenceCachingMetadataReaderFactory
ConditionEvaluationReportLoggingListenerorg.sf.boot.autoconfigure.loggingConditionEvaluationReport写入日志

注意 : 上表中包名中的 springframework 缩写为 sf

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值