spring boot中的ApplicationContextInitializer

什么是ApplicationContextInitializer?作用是什么?

注释中描述ApplicationContextInitializer主要在ConfigurableApplicationContext的refresh方法中作为callback作用(spring boot应用在启动时,会在prepareContext时调用ApplicationContextInitializer的initialize方法),通常用在需要对某些参数进行初始化的web应用中,并鼓励在ApplicationContextInitializer实现类中使用@Order注解或者实现Ordered接口。

下图是spring boot中所有ApplicationContextInitializer实现类(剔除了test包中的实现类)。下面我们对这些实现类挨个解释说明

 

ContextIdApplicationContextInitializer

	private static final String NAME_PATTERN = "${spring.application.name:${vcap.application.name:${spring.config.name:application}}}";

	private static final String INDEX_PATTERN = "${vcap.application.instance_index:${spring.application.index:${server.port:${PORT:null}}}}";
    
	public ContextIdApplicationContextInitializer() {
		this(NAME_PATTERN);
	}

	public ContextIdApplicationContextInitializer(String name) {
		this.name = name;
	}

    @Override
	public void initialize(ConfigurableApplicationContext applicationContext) {
		applicationContext.setId(getApplicationId(applicationContext.getEnvironment()));
	}

	private String getApplicationId(ConfigurableEnvironment environment) {
		String name = environment.resolvePlaceholders(this.name);
		String index = environment.resolvePlaceholders(INDEX_PATTERN);
		String profiles = StringUtils
				.arrayToCommaDelimitedString(environment.getActiveProfiles());
		if (StringUtils.hasText(profiles)) {
			name = name + ":" + profiles;
		}
		if (!"null".equals(index)) {
			name = name + ":" + index;
		}
		return name;
	}

代码比较简单。依次获取应用的name,index,profiles,然后对三个值进行拼接。获取name的逻辑,首先读取spring.application.name属性,如果没有value,则读取vcap.application.name属性,如果没有value,再读取spring.config.name:application属性。index的读取规则同name的一致。profiles如果有多个值,则使用逗号分割拼接。

ServerPortInfoApplicationContextInitializer

	@Override
	public void initialize(ConfigurableApplicationContext applicationContext) {
		applicationContext.addApplicationListener(
				new ApplicationListener<EmbeddedServletContainerInitializedEvent>() {

					@Override
					public void onApplicationEvent(
							EmbeddedServletContainerInitializedEvent event) {
						ServerPortInfoApplicationContextInitializer.this
								.onApplicationEvent(event);
					}

				});
	}

	protected void onApplicationEvent(EmbeddedServletContainerInitializedEvent event) {
		String propertyName = getPropertyName(event.getApplicationContext());
		setPortProperty(event.getApplicationContext(), propertyName,
				event.getEmbeddedServletContainer().getPort());
	}

	protected String getPropertyName(EmbeddedWebApplicationContext context) {
		String name = context.getNamespace();
		if (StringUtils.isEmpty(name)) {
			name = "server";
		}
		return "local." + name + ".port";
	}

	private void setPortProperty(ApplicationContext context, String propertyName,
			int port) {
		if (context instanceof ConfigurableApplicationContext) {
			setPortProperty(((ConfigurableApplicationContext) context).getEnvironment(),
					propertyName, port);
		}
		if (context.getParent() != null) {
			setPortProperty(context.getParent(), propertyName, port);
		

initialize方法是向context中添加ApplicationListener(又来一个没见过的东西,什么是ApplicationListener?点这里)。Listener的onApplicationEvent方法就是ServerPortInfoApplicationContextInitializer的onApplicationEvent方法。方法逻辑大致在不配置context的命名空间的情况下,获取配置文件中的local.server.port中的value,然后将port的值设置到context中。

ParentContextApplicationContextInitializer


	@Override
	public void initialize(ConfigurableApplicationContext applicationContext) {
		if (applicationContext != this.parent) {
			applicationContext.setParent(this.parent);
			applicationContext.addApplicationListener(EventPublisher.INSTANCE);
		}
	}

设置context的父context,并设置事件发布者

ServletContextApplicationContextInitializer

	@Override
	public void initialize(ConfigurableWebApplicationContext applicationContext) {
		applicationContext.setServletContext(this.servletContext);
		if (this.addApplicationContextAttribute) {
			this.servletContext.setAttribute(
					WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
					applicationContext);
		}

	}

设置上下文的servletContext属性

SharedMetadataReaderFactoryContextInitializer

ConfigurationWarningsApplicationContextInitializer

	@Override
	public void initialize(ConfigurableApplicationContext context) {
		context.addBeanFactoryPostProcessor(
				new ConfigurationWarningsPostProcessor(getChecks()));
	}

	/**
	 * Returns the checks that should be applied.
	 * @return the checks to apply
	 */
	protected Check[] getChecks() {
		return new Check[] { new ComponentScanPackageCheck() };
	}

ConfigurationWarningsApplicationContextInitializer的作用是用来报告Spring容器的一些常见的错误配置的。这里initialize方法中向context中添加 校验 bean扫描路径是否合规 BeanFactoryPostProcessor(不懂BeanFactoryPostProcessor得同学可以自行百度下,这里不做过多描述)。

AutoConfigurationReportLoggingInitializer

	@Override
	public void initialize(ConfigurableApplicationContext applicationContext) {
		this.applicationContext = applicationContext;
		applicationContext.addApplicationListener(new AutoConfigurationReportListener());
		if (applicationContext instanceof GenericApplicationContext) {
			// Get the report early in case the context fails to load
			this.report = ConditionEvaluationReport
					.get(this.applicationContext.getBeanFactory());
		}
	}

往context中添加AutoConfigurationReportListener监听器(支持监听上下文刷新事件、应用失败事件)。

DelegatingApplicationContextInitializer

	
       private static final String PROPERTY_NAME = "context.initializer.classes";

        @Override
	public void initialize(ConfigurableApplicationContext context) {
		ConfigurableEnvironment environment = context.getEnvironment();
		List<Class<?>> initializerClasses = getInitializerClasses(environment);
		if (!initializerClasses.isEmpty()) {
			applyInitializerClasses(context, initializerClasses);
		}
	}

	private List<Class<?>> getInitializerClasses(ConfigurableEnvironment env) {
		String classNames = env.getProperty(PROPERTY_NAME);
		List<Class<?>> classes = new ArrayList<Class<?>>();
		if (StringUtils.hasLength(classNames)) {
			for (String className : StringUtils.tokenizeToStringArray(classNames, ",")) {
				classes.add(getInitializerClass(className));
			}
		}
		return classes;
	}

从properties文件中获取context.initializer.classes的属性值并按逗号进行分割,然后初始化得到的initializer类实例,并按照order进行排序,最后依次调用其initialize方法。

 

 

了解了每个springboot自带的ApplicationContextInitializer后,下面介绍下如何创建自己的ApplicationContextInitializer。

1、spring.factories方式

首先resource下面新建/META-INF/spring.factories文件,然后添加

org.springframework.context.ApplicationContextInitializer=com.aihai.demo.MyApplicationContextInitializer

package com.aihai.demo; 

public class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
 
    @Override
    public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
        System.out.println("自定义ApplicationContextInitializer");
    }
}

2、application.properties方式

application.properties文件中增加

 context.initializer.classes = com.aihai.demo.MyApplicationContextInitializer

3、启动类中直接添加Initializers方式
 

@SpringBootApplication
public class SpringMain{
 
    public static void main(String[] args) {
       
        SpringApplication springApplication = new SpringApplication(SpringMain.class);
        springApplication.addInitializers(new MyApplicationContextInitializer());
        springApplication.run(args);
 
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值