SpringApplication方法run的事件监听器机制

简介

Spring boot应用,这里具体的来说,是指类SpringApplication,在其run()方法运行过程中,主要围绕所使用ApplicationContext对象的生命周期事件,应用了一个事件监听器机制提供了相应的程序扩展入口点,开发人员可以基于此机制,在相应的事件发生时执行特定的逻辑。

SpringApplication应用的该事件监听器机制主要利用如下两个类/接口 :

  1. SpringApplicationRunListeners
  2. SpringApplicationRunListener

简单来讲,在SpringApplication的run()方法执行时,应用上下文ApplicationContext对象创建之前,应用程序会首先找到配置中所定义的所有SpringApplicationRunListener的实现类(这些实现类可能由Spring boot自身实现和提供,也可以由开发人员来实现和提供),创建他们的实例,然后把它们放在一个集合中,然后封装为一个 SpringApplicationRunListeners 实例。之后Spring boot应用开始创建ApplicationContext对象,然后在相应的ApplicationContext的生命周期事件发生时,程序会通过该SpringApplicationRunListeners 实例向各个 SpringApplicationRunListener 实例广播相应的事件,从而SpringApplicationRunListener 实现类中相应的监听器逻辑得到执行。

这里所提到的SpringApplicationContext的生命周期事件,具体来讲,是如下事件 :

发生顺序事件简介
1environmentPrepared环境已经准备好,但是ApplicationContext实例尚未被创建
2contextPreparedApplicationContext已经被创建并且准备就绪,但是sources尚未被加载
3contextLoadedApplicationContext实例的sources已经被加载,但是ApplicationContext实例尚未被刷新(refresh)
4finishedSpring应用程序启动完成时发送该事件,此时ApplicationContext实例已经成功创建完成,或者遇到异常创建失败。对于该ApplicationContext是一个WebApplicationContext的情况,此时内置的Web容器已经在其他线程启动处于服务就绪状态,而SpringApplication执行主线程已经完成自己的任务。

源代码分析

SpringApplication.run()方法中事件监听有关逻辑

// SpringApplication 类
public ConfigurableApplicationContext run(String... args) {
	StopWatch stopWatch = new StopWatch();
	stopWatch.start();
	ConfigurableApplicationContext context = null;
	FailureAnalyzers analyzers = null;
	configureHeadlessProperty();
	// 从配置中获取SpringApplicationRunListener实现类并实例化,
	// 最终把所有这些实例包装到一个SpringApplicationRunListeners实例来使用
	SpringApplicationRunListeners listeners = getRunListeners(args);
	// 启动各个SpringApplicationRunListener 监听器实例 
	listeners.starting();
	try {
		ApplicationArguments applicationArguments = 
					new DefaultApplicationArguments(args);
		// 准备环境
		// 结束时会向各个SpringApplicationRunListener发送事件 
		// environmentPrepared					
		ConfigurableEnvironment environment = 
					prepareEnvironment(listeners,applicationArguments);
		Banner printedBanner = printBanner(environment);
		context = createApplicationContext();// 这里创建应用上下文
		analyzers = new FailureAnalyzers(context);
		// 准备应用上下文,
		// 完成时会向各个SpringApplicationRunListener发送事件 
		// contextPrepared
		prepareContext(context, environment, listeners, 
					applicationArguments,printedBanner);
		refreshContext(context);
		afterRefresh(context, applicationArguments);
		// 应用上下文准备完成
		// 向各个SpringApplicationRunListener发送事件 
		// finished
		listeners.finished(context, null);
		stopWatch.stop();
		if (this.logStartupInfo) {
			new StartupInfoLogger(this.mainApplicationClass)
					.logStarted(getApplicationLog(), stopWatch);
		}
		return context;
	}
	catch (Throwable ex) {
		// 应用上下文准备遇到异常时,
		// 向各个SpringApplicationRunListener发送事件finished,
		// 携带响应异常信息
		handleRunFailure(context, listeners, analyzers, ex);
		throw new IllegalStateException(ex);
	}
}

SpringApplicationRunListener实例来自哪里?

上面对SpringApplication.run()的分析中我们提到"从配置中获取SpringApplicationRunListener实现类"并实例化,这里我们来看这些实现类具体在哪里配置,是怎样被获取的。

从上面提到的SpringApplication.getRunListeners()方法开始 :

private SpringApplicationRunListeners getRunListeners(String[] args) {
	Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
	// 1.创建一个实例SpringApplicationRunListeners,
	//   关于类(SpringApplicationRunListeners下面会有介绍)
	// 2.使用方法getSpringFactoriesInstances()获取配置中
	//   接口SpringApplicationRunListener实现的实例
	return new SpringApplicationRunListeners(logger, 
		getSpringFactoriesInstances(SpringApplicationRunListener.class, 
									types, this, args));
}

接下来我们看方法getSpringFactoriesInstances具体都做了些什么 :

private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
		Class<?>[] parameterTypes, Object... args) {
	ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
	// Use names and ensure unique to protect against duplicates
	Set<String> names = new LinkedHashSet<String>(
	//使用工具SpringFactoriesLoader.loadFactoryNames获取指定类type的全限定名,
	//而针对本文的情况,这里类type其实就是接口SpringApplicationRunListener
			SpringFactoriesLoader.loadFactoryNames(type, classLoader));
	// 实例化这些加载到的类
	// 针对本文的情况,在缺省配置Spring boot应用中,获取到的类是
	// org.springframework.boot.context.event.EventPublishingRunListener,
	// 这里创建该类的一个实例		
	List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
			classLoader, args, names);
	AnnotationAwareOrderComparator.sort(instances);
	return instances;
}

org.springframework.boot.context.event.EventPublishingRunListener其是存在于配置文件中:

# spring-boot-1.x.x.RELEASE.jar!\META-INF\spring.factories 配置文件片段
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

上面分析中还提到了工具类SpringFactoriesLoader,关于它可以参考这篇文章,感兴趣的话可以看看。

SpringApplicationRunListeners介绍

该类主要是将一组SpringApplicationRunListener聚合到一起,然后使用统一的方式传播事件。

其实现逻辑如下,主要就是将事件传播到各个SpringApplicationRunListener实例。

package org.springframework.boot;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.commons.logging.Log;

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.util.ReflectionUtils;

/**
 * A collection of SpringApplicationRunListener.
 *
 * @author Phillip Webb
 */
class SpringApplicationRunListeners {

	private final Log log;

	private final List<SpringApplicationRunListener> listeners;

	SpringApplicationRunListeners(Log log,
			Collection<? extends SpringApplicationRunListener> listeners) {
		this.log = log;
		this.listeners = new ArrayList<SpringApplicationRunListener>(listeners);
	}

	public void starting() {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.starting();
		}
	}

	public void environmentPrepared(ConfigurableEnvironment environment) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.environmentPrepared(environment);
		}
	}

	public void contextPrepared(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.contextPrepared(context);
		}
	}

	public void contextLoaded(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.contextLoaded(context);
		}
	}

	public void finished(ConfigurableApplicationContext context, Throwable exception) {
		for (SpringApplicationRunListener listener : this.listeners) {
			callFinishedListener(listener, context, exception);
		}
	}

	private void callFinishedListener(SpringApplicationRunListener listener,
			ConfigurableApplicationContext context, Throwable exception) {
		try {
			listener.finished(context, exception);
		}
		catch (Throwable ex) {
			if (exception == null) {
				ReflectionUtils.rethrowRuntimeException(ex);
			}
			if (this.log.isDebugEnabled()) {
				this.log.error("Error handling failed", ex);
			}
			else {
				String message = ex.getMessage();
				message = (message == null ? "no error message" : message);
				this.log.warn("Error handling failed (" + message + ")");
			}
		}
	}

}

接口SpringApplicationRunListener介绍

package org.springframework.boot;

import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.support.SpringFactoriesLoader;

/**
 * SpringApplication run 方法的事件监听器
 * SpringApplicationRunListeners are loaded via the SpringFactoriesLoader
 * and should declare a public constructor that accepts a SpringApplication
 * instance and a String[] of arguments. A new
 * SpringApplicationRunListener instance will be created for each run.
 *
 */
public interface SpringApplicationRunListener {

	/**
	 * Called immediately when the run method has first started. Can be used for very
	 * early initialization.
	 */
	void starting();

	/**
	 * Called once the environment has been prepared, but before the
	 * ApplicationContext has been created.
	 * 环境对象准备好后,应用上下文创建前触发该事件
	 * @param environment the environment
	 */
	void environmentPrepared(ConfigurableEnvironment environment);

	/**
	 * Called once the ApplicationContext has been created and prepared, but
	 * before sources have been loaded.
	 * 应用上下文创建和准备好后,尚未加载sources之前触发该事件
	 * @param context the application context
	 */
	void contextPrepared(ConfigurableApplicationContext context);

	/**
	 * Called once the application context has been loaded but before it has been
	 * refreshed.
	 * 应用上下文加载sources后,执行refresh之前触发该事件
	 * @param context the application context
	 */
	void contextLoaded(ConfigurableApplicationContext context);

	/**
	 * Called immediately before the run method finishes.
	 * 
	 * SpringApplication 的run方法已经完成自己的任务即将结束之前触发该事件。
	 * SpringApplication 的run方法即将结束并不代表程序即将退出,只表示run方法任务的完成
	 * (成功完成或者失败完成)和方法的退出。
	 * 该事件触发时,应用上下文ApplicationContext要么已经创建成功要么已经创建失败,
	 * 并且如果是Web应用的情况下,此时Web应用应该已经处于伺服状态(此时应该是存在于其他线程中),
	 * 而整个进程并不会退出,而当前SpringApplication.run()即将退出。
	 * @param context the application context or null if a failure occurred before the
	 * context was created
	 * @param exception any run exception or null if run completed successfully.
	 */
	void finished(ConfigurableApplicationContext context, Throwable exception);

}

EventPublishingRunListener

package org.springframework.boot.context.event;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.util.ErrorHandler;

/**
 * 接口SpringApplicationRunListener的实现类,用于发布各种SpringApplicationEvent事件。
 * SpringApplicationEvent事件也是ApplicationEvent事件,但主要是跟踪SpringApplication运行。
 * 
 * 内部使用一个 ApplicationEventMulticaster 实例用于在应用上下文真正被刷新前发布各种事件。
 * ApplicationEventMulticaster 实例会管理多个ApplicationListener对象并向它们发布事件。
 * 一般应用上下文的事件发布并不是自身直接发布给监听器,而是委托给ApplicationEventMulticaster
 * 发布这些事件。 
 * 
 * @author Phillip Webb
 * @author Stephane Nicoll
 */
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
	// 所属SpringApplication对象
	private final SpringApplication application;
	// 运行参数
	private final String[] args;
	// 真正用于发布事件所使用的 ApplicationEventMulticaster ,
	// 其实是一个 SimpleApplicationEventMulticaster
	private final SimpleApplicationEventMulticaster initialMulticaster;

	public EventPublishingRunListener(SpringApplication application, String[] args) {
		this.application = application;
		this.args = args;
		this.initialMulticaster = new SimpleApplicationEventMulticaster();
		// 从appliction中获取已经根据配置实例化的各个ApplicationListener,
		// 将它们设置到事件多播器,随后的各种事件发布通过该事件多播器向这些
		// ApplicationListener发布
		for (ApplicationListener<?> listener : application.getListeners()) {
			this.initialMulticaster.addApplicationListener(listener);
		}
	}

	@Override
	public int getOrder() {
		return 0;
	}

	@Override
	@SuppressWarnings("deprecation")
	public void starting() {
		// 发布事件 ApplicationStartedEvent
		this.initialMulticaster
				.multicastEvent(new ApplicationStartedEvent(this.application, this.args));
	}

	@Override
	public void environmentPrepared(ConfigurableEnvironment environment) {
		// 发布事件 ApplicationEnvironmentPreparedEvent
		this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
				this.application, this.args, environment));
	}

	@Override
	public void contextPrepared(ConfigurableApplicationContext context) {
		
	}

	@Override
	public void contextLoaded(ConfigurableApplicationContext context) {
		// 在应用上下文已经加载但是尚未刷新前将SpringApplication对象中所加载的
		// ApplicationListener和应用上下文做关联
		for (ApplicationListener<?> listener : this.application.getListeners()) {
			if (listener instanceof ApplicationContextAware) {
				((ApplicationContextAware) listener).setApplicationContext(context);
			}
			context.addApplicationListener(listener);
		}
		// 发布事件 ApplicationPreparedEvent
		this.initialMulticaster.multicastEvent(
				new ApplicationPreparedEvent(this.application, this.args, context));
	}

	@Override
	public void finished(ConfigurableApplicationContext context, Throwable exception) {
		SpringApplicationEvent event = getFinishedEvent(context, exception);
		if (context != null && context.isActive()) {
			// 现在应用上下文存在并且活跃,而且
			// 所有的事件监听器都已经关联到应用上下文上面了,所以直接利用应用上下文发布事件
			context.publishEvent(event);
		}
		else {
			// 一个不活跃的应用上下文不一定有多播器,所以这里我们使用自己的多播器
			// initialMulticaster发布事件
			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);
		}
	}

	// 根据上下文和异常情况判断当前是哪种应用程序启动过程结束 : 
	// 1. SpringApplication启动异常结束,一般启动异常结束也表示程序运行将要结束
	// 2. SpringApplication启动正常结束
	// 注意:启动正常结束不表示程序运行结束,相反,可能表示程序运行刚就绪开始提供服务
	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);
	}

	private static class LoggingErrorHandler implements ErrorHandler {

		private static Log logger = LogFactory.getLog(EventPublishingRunListener.class);

		@Override
		public void handleError(Throwable throwable) {
			logger.warn("Error calling ApplicationEventListener", throwable);
		}

	}

}

从上面EventPublishingRunListener的代码可以看出 :
1.EventPublishingRunListener主要任务是被SpringApplication.run()调用,负责发布相应事件;
2. EventPublishingRunListener发布的事件的监听器是SpringApplication从配置中获取的监听器;
3. EventPublishingRunListener不仅负责发布事件,而且在合适的时机将SpringApplication所获取的监听器和应用上下文作关联。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值