Spring Boot 源码分析 (2) -- 初始化器和监听器

Spring Boot 源码分析 (2) -- 初始化器和监听器

在SpringBootApplication分析中我们说过,ApplicationContextInitializer和ApplicationListener的获取,是从Jar包中的/META-INF/spring.factories文件中解析出来的。这个功能属于Spring Boot自动配置的部分。在Spring Boot启动过程中使用了一些通用的初始化器和监听器。还是以下面的这段代码作为示例代码。

@SpringBootApplication
public class Application {

	/**
	 * 启动Spring Boot应用
	 */
	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
}

初始化器

下面的表格是Spring Boot启动时所加载到的初始化器。首先我们要明白ApplicationContextInitializer接口的功能定义,ApplicationContextInitializer是属于Spring的一个接口。接口定义是这样的:用于初始化ConfigurableApplicationContext类型的Spring上下文对象,在调用refresh方法之前执行。另外,ApplicationContextInitializer实现类需要排序,实现Ordered接口或使用@Order注解。

类名
org.springframework.boot.context.config.DelegatingApplicationContextInitializer
org.springframework.boot.context.ContextIdApplicationContextInitializer
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer
org.springframework.boot.context.embedded.ServerPortInfoApplicationContextInitializer
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer
org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer

功能描述:

  • DelegatingApplicationContextInitializer: 委派处理ApplicationContext初始化器,其需要委派处理的初始化器来自Spring环境中的context.initializer.classes属性,该属性可以使用逗号分隔多个初始化器。
  • ContextIdApplicationContextInitializer:为ApplicationContext设置id。根据以下的配置顺序来设置,spring.application.name、vcap.application.name、spring.config.name,如果环境配置中都没有这些配置,则默认使用“application”来表示,另外还会将profiles也加入到id中去。
  • ConfigurationWarningsApplicationContextInitializer:输出警告日志信息。
  • ServerPortInfoApplicationContextInitializer:添加一个EmbeddedServletContainerInitializedEvent事件监听,触发设置嵌入的WEB服务启动端口。通过属性local.[namespace].port来设置启动端口,其中namespace为ApplicationContext指定的命名空间,如果命名空间为空,则使用local.server.port属性来表示配置的端口。
  • SharedMetadataReaderFactoryContextInitializer:和Spring Boot共享CachingMetadataReaderFactory。
  • AutoConfigurationReportLoggingInitializer:添加一个通用的事件监听Springboot自动配置的报表日志输出。

在上面的初始化器分析中,我们可以看到,通过一些属性的读取可以设置Spring Boot启动过程中的一些参数。初始化器的功能可以在Spring Boot启动前对ApplicationContext进行一些自定义操作。初始化器是在准备上下文阶段调用的。

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);
}

监听器

上面讲初始化器的执行时在Spring调用refresh方法之前,那么监听器是如何执行的呢?下面是SpringBoot在启动过程中使用的监听器列表。

监听器列表:

类名
org.springframework.boot.context.config.ConfigFileApplicationListener
org.springframework.boot.context.config.AnsiOutputApplicationListener
org.springframework.boot.logging.LoggingApplicationListener
org.springframework.boot.logging.ClasspathLoggingApplicationListener
org.springframework.boot.autoconfigure.BackgroundPreinitializer
org.springframework.boot.context.config.DelegatingApplicationListener
org.springframework.boot.builder.ParentContextCloserApplicationListener
org.springframework.boot.ClearCachesApplicationListener
org.springframework.boot.context.FileEncodingApplicationListener

功能描述:

  • ConfigFileApplicationListener:监听配置文件,'application.properties' 属性文件就是在这里处理的。

  • LoggingApplicationListener:日志监听

  • DelegatingApplicationListener:通过环境变量中的context.listener.classes属性,来委派处理配置的监听器。

  • FileEncodingApplicationListener:监听spring.mandatoryFileEncoding配置的文件编码和系统文件编码是否一致。

Spring Boot提供的监听器怎么处理呢?在run(String[] agas)中我们可以看到监听器的入口在SpringApplicationRunListeners listeners = getRunListeners(args),该方法获取了所有监听器。我们看看这个方法。

private SpringApplicationRunListeners getRunListeners(String[] args) {
	Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
	return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
			SpringApplicationRunListener.class, types, this, args));
}

这个方法从/META-INF/spring.fatories中获取了所有配置的SpringApplicationRunListener接口实例。这里是EventPublishingRunListener实例对象。将this和args参数作为构造参数传入。可以看EventPublishingRunListener构造方法得到证实。

public EventPublishingRunListener(SpringApplication application, String[] args) {
	this.application = application;
	this.args = args;
	this.initialMulticaster = new SimpleApplicationEventMulticaster();
	for (ApplicationListener<?> listener : application.getListeners()) {
		this.initialMulticaster.addApplicationListener(listener);
	}
}

也就是说,Spring Boot通过EventPublishingRunListener来进行事件的发布。将所有的listener发布到SimpleApplicationEventMulticaster中。事件的通知通过调用multicastEvent方法来告诉监听器哪种事件应该被监听到。

下面我们继续看Spring Boot发布的事件。

Spring Boot启动事件 ApplicationStartedEvent

ApplicationStartedEvent事件在Spring Boot 1.5版本之后被ApplicationStartingEvent取代。

public void starting() {
	this.initialMulticaster
			.multicastEvent(new ApplicationStartedEvent(this.application, this.args));
}
Spring Boot 环境准备完成事件 ApplicationEnvironmentPreparedEvent
public void environmentPrepared(ConfigurableEnvironment environment) {
	this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
			this.application, this.args, environment));
}
Spring Boot 上下文准备完成事件

​ 这个是一个空方法。

public void contextPrepared(ConfigurableApplicationContext context) {
}
Spring Boot 上下文加载完成事件 ApplicationPreparedEvent
public void contextLoaded(ConfigurableApplicationContext context) {
	for (ApplicationListener<?> listener : this.application.getListeners()) {
		if (listener instanceof ApplicationContextAware) {
			((ApplicationContextAware) listener).setApplicationContext(context);
		}
		context.addApplicationListener(listener);
	}
	this.initialMulticaster.multicastEvent(
			new ApplicationPreparedEvent(this.application, this.args, context));
}
Spring Boot 启动完成事件ApplicationReadyEvent和启动失败事件ApplicationFailedEvent
public void finished(ConfigurableApplicationContext context, Throwable exception) {
	SpringApplicationEvent event = getFinishedEvent(context, exception);
	if (context != null && context.isActive()) {
		// 发布注册到上下文中可以发布的事件
		context.publishEvent(event);
	}
	else {
		// An inactive context may not have a multicaster so we use our multicaster to
		// call all of the context's listeners instead
		if (context instanceof AbstractApplicationContext) {
			for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
					.getApplicationListeners()) {
				this.initialMulticaster.addApplicationListener(listener);
			}
		}
		if (event instanceof ApplicationFailedEvent) {
			this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
		}
		this.initialMulticaster.multicastEvent(event);
	}
}

private SpringApplicationEvent getFinishedEvent(
		ConfigurableApplicationContext context, Throwable exception) {
        // 如果存在异常返回失败事件
	if (exception != null) {
		return new ApplicationFailedEvent(this.application, this.args, context,
				exception);
	}
        // 返回成功事件
	return new ApplicationReadyEvent(this.application, this.args, context);
}

总结

本篇主要分析了SpringBoot在启动过程中的ApplicationContextInitializer和ApplicationListener。在SpringBoot中初始化器和监听器都是可以通过Jar包中的/META-INF/spring.factories文件来配置单。初始化器的调用是在容器加载之前调用的,可以对ApplicationContext在启动前做一些自定义初始化处理。监听器可以监听SpringBoot提供的事件,在SpringBoot中新增了几种事件类型,上面已经列出来了。总之,通过这两个接口,可以对SpringBoot在启动和运行中很轻松地做到自定义开发功能。

转载于:https://my.oschina.net/xiaoqiyiye/blog/1624181

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值