4. Spring Boot之SpringApplicationRunListeners

1,759 阅读5分钟

理解SpringApplicationRunListeners

我们知道Spring Boot应用的启动是依靠SpringApplication.run()来完成的,核心代码如下:

	public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			listeners.started(context);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

记得刚学习《Spring Boot编程思想》的时候,小马哥说Spring Boot应用和Spring 应用的区别在于Spring Boot应用是独立的Spring 应用,因为它不需要监听Tomcat/Jetty等Servlet容器来实现普通Spring Web MVC程序的双上下文启动,因为Spring Boot应用只需要run方法即可启动上下文。

对于双上下文的理解,其实读者只需关注Spring和Servlet容器的关系即可理解

  • Servlet容器启动,ContextLoaderListener监听到了,就会启动一个上下文
  • DispatcherServlet(Spring Web MVC应用)作为一个Servlet,随Servlet容器启动后会初始化(如有误请指正),初始化时会创建一个上下文

这就是双上下文的产生,能产生两个上下文主要还是因为普通的Spring MVC应用不是独立的Spring应用,它需要强烈关注Servlet容器,随着Servlet容器的启动来启动Spring上下文。

本篇文章会探讨SpringApplication.run()中的SpringApplicationRunListeners对象,通过对其如下的两个方法的剖析加深对SpringApplicationRunListeners的理解,从而对Spring Boot启动流程有一个更深的理解。(对于其在run方法中相关的详细代码不会作过多分析。大佬请绕道!!!)

  • SpringApplicationRunListeners listeners = getRunListeners(args);
  • listeners.starting()

SpringApplicationRunListeners#getRunListeners

创建SpringApplicationRunListeners

private SpringApplicationRunListeners getRunListeners(String[] args) {
       // 定义SpringApplicationRunListeners实现类构造器的入参类型和个数
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
     // this :SpringApplication对象
		return new SpringApplicationRunListeners(logger,
				getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
	}

该方法创建了一个SpringApplicationRunListeners对象,下面是SpringApplicationRunListeners类的主要属性和构造方法,其实SpringApplicationRunListeners对象内部包装了SpringApplicationRunListener类型的List集合对象。

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

回到getRunListeners方法

// this : SpringApplication对象
return new SpringApplicationRunListeners(logger,
				getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));

SpringApplicationRunListeners创建时,通过getSpringFactoriesInstances()获取了List<SpringApplicationRunListener>对象,我们来看下该方法的逻辑

getSpringFactoriesInstances

// type:SpringApplicationRunListener.class
// parameterTypes: Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
// args: SpringApplication对象
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        // 获取ClassLoader,详细代码很简单
		ClassLoader classLoader = getClassLoader();
		// 获取SpringApplicationRunListener类名的Set对象(去重)
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    // 创建List<SpringApplicationRunListener>对象
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

SpringFactoriesLoader.loadFactoryNames

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    }

核心代码就不继续追着看了,我们凭直觉应该能知道又是通过SpringFactoriesLoader工具类去spring.factories文件中寻找SpringApplicationRunListener的实现类类名,然后返回实现类的类名Set

createSpringFactoriesInstances

// type : SpringApplicationRunListener.class
// parameterTypes: Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
// names: 类名Set ,先破案,其实只有`EventPublishingRunListener`对象
// args: SpringApplication对象
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
			ClassLoader classLoader, Object[] args, Set<String> names) {
		List<T> instances = new ArrayList<>(names.size());
		for (String name : names) {
			try {
                // 根据类名Set对象加载SpringApplicationRunListener类
				Class<?> instanceClass = ClassUtils.forName(name, classLoader);
                // 判断类型
				Assert.isAssignable(type, instanceClass);
                // 获取EventPublishingRunListener的构造器
                // 其实就两个参数 SpringApplication ;String[] args
				Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
                // 根据构造器创建EventPublishingRunListener对象 ,只传入了SpringApplication对象
                // 所以构造器的第二个参数args就是空的String数组
				T instance = (T) BeanUtils.instantiateClass(constructor, args);
				instances.add(instance);
			}
			catch (Throwable ex) {
				throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
			}
		}
		return instances;
	}

该方法根据SpringFactoriesLoader.loadFactoryNames方法返回的List<String> (类名列表)对象,通过反射逐个创建SpringApplicationRunListener对象,所以SpringApplicationRunListener的实现类必须要有构造方法。

// EventPublishingRunListener的构造器
public EventPublishingRunListener(SpringApplication application, String[] args) {
		this.application = application;
		this.args = args;
    	// 下面的构造放到EventPublishimngRunListener部分理解
		this.initialMulticaster = new SimpleApplicationEventMulticaster();
		for (ApplicationListener<?> listener : application.getListeners()) {
			this.initialMulticaster.addApplicationListener(listener);
		}
	}
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

  • 截至目前,EventPublishingRunListener是Spring Boot官方唯一的SpringApplicationRunListener实现,所以正常情况下我们获取到的List<SpringApplicationRunListener>列表只有EventPublishingRunListener一个元素。

  • 当然,我们也可以自己实现SpringApplicationRunListener,并且声明在Spring Boot应用的META-INF/spring.factories文件中即可。

接下来看看SpringApplicationRunListener和它的实现EventPublishingRunListener

SpringApplicationRunListener

public interface SpringApplicationRunListener {

	/**
	 * 运行时机: Spring应用刚启动
	 */
	void starting();

	/**
	 * 运行时机:ConfigurableEnvironment准备妥当,允许将其调整
	 */
	void environmentPrepared(ConfigurableEnvironment environment);

	/**
	 * 运行时机:ConfigurableApplicationContext准备妥当,允许将其调整
	 */
	void contextPrepared(ConfigurableApplicationContext context);

	/**
	 * 运行时机:ConfigurableApplicationContext已经装载,但仍未启动
	 */
	void contextLoaded(ConfigurableApplicationContext context);

	/**
	 * 运行时机:ConfigurableApplicationContext已启动,此时Spring Bean已经初始化完成
	 */
	void started(ConfigurableApplicationContext context);

	/**
	 * 运行时机:Spring应用正在运行
	 */
	void running(ConfigurableApplicationContext context);

	/**
	 * 运行时机: Spring应用运行失败
	 */
	void failed(ConfigurableApplicationContext context, Throwable exception);

}

EventPublishingRunListener

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

	private final SpringApplication application;

	private final String[] args;

	private final SimpleApplicationEventMulticaster initialMulticaster;

	public EventPublishingRunListener(SpringApplication application, String[] args) {
		this.application = application;
		this.args = args;
        // SimpleApplicationEventMulticaster:ApplicationEventMulticaster的实现类;
        // 用来发布事件
		this.initialMulticaster = new SimpleApplicationEventMulticaster();
        // 遍历SpringApplication中的ApplicationListener列表对象将其添加给SimpleApplicationEventMulticaster
		for (ApplicationListener<?> listener : application.getListeners()) {
			this.initialMulticaster.addApplicationListener(listener);
		}
	}
...
}

前面在创建SpringApplicationRunListeners过程,我们已经简单过了一下EventPublishingRunListener的构造器,也就是前两行的内容

这是SpringApplication对象中的ApplicationListener列表。

我们认真理解了SpringApplicationRunListeners的创建过程,其实就是创建List<SpringApplicationRunListener>对象,再深了说就是创建EventPublishingRunListener对象;而EventPublishingRunListener对象的创建引入了SimpleApplicationEventMulticaster对象。我们再来看SpringApplication.run()方法:listeners.starting();方法

public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
    ...
}

SpringApplicationRunListeners#starting

SpringApplicationRunListeners#starting

public void starting() {
        // 遍历SpringApplicationRunListener列表 调用starting方法
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.starting();
		}
	}

EventPublishingRunListener#starting

@Override
	public void starting() {
        // 发布ApplicationStartingEvent事件 args为空
		this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
	}

SimpleApplicationEventMulticaster部分代码

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
    ...
        
    @Override
	public void multicastEvent(ApplicationEvent event) {
        // event : ApplicationStartingEvent
		multicastEvent(event, resolveDefaultEventType(event));
	}

	@Override
	public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
        //  executor 为空
		Executor executor = getTaskExecutor();
		for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			if (executor != null) {
				executor.execute(() -> invokeListener(listener, event));
			}
			else {
                // 调用ApplicationListener
				invokeListener(listener, event);
			}
		}
	}
    
    protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
		ErrorHandler errorHandler = getErrorHandler();
		if (errorHandler != null) {
			try {
				doInvokeListener(listener, event);
			}
			catch (Throwable err) {
				errorHandler.handleError(err);
			}
		}
		else {
			doInvokeListener(listener, event);
		}
	}
    
    	private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
		try {
			listener.onApplicationEvent(event);
		}
		catch (ClassCastException ex) {
			String msg = ex.getMessage();
			if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
				// Possibly a lambda-defined listener which we could not resolve the generic event type for
				// -> let's suppress the exception and just log a debug message.
				Log logger = LogFactory.getLog(getClass());
				if (logger.isTraceEnabled()) {
					logger.trace("Non-matching event type for listener: " + listener, ex);
				}
			}
			else {
				throw ex;
			}
		}
	}
    
}

列举了上述的代码可能有些杂乱,我们从SpringApplicationRunListeners.starting()开始整理一下调用链,清晰一下流程

  • SpringApplicationRunListeners.starting()
    • EventPublishingRunListener#starting
      • SimpleApplicationEventMulticaster#multicastEvent()
        • SimpleApplicationEventMulticaster#invokeListener(listener, event)
          • doInvokeListener(listener, event)
            • listener.onApplicationEvent(event)

本质上就是EventPublishingRunListener#starting()方法发布了ApplicationStartingEvent事件,然后获取ApplicationListener列表并遍历执行ApplicationListener#onApplicationEvent(event)方法。

总结

我们通过SpringApplicationRunListeners的两个方法,理解了EventPublishingRunListener的创建逻辑,并探讨了SimpleApplicationEventMulticaster发布ApplicationStartingEvent事件的原理,接着剖析了SimpleApplicationEventMulticaster触发ApplicationListener列表依次监听ApplicationStartingEvent事件的流程。

​ Spring Boot这样的设计其实就是创建一个监听对象SpringApplicationRunListeners,这个监听对象内部封装了SpringApplicationRunListener的List对象,其实只包含EventPublishingRunListener对象;而EventPublishingRunListener内部有引入了SimpleApplicationEventMulticaster来发布事件,当事件发布后再触发ApplicationListener监听。其实就是自己发布事件再触发事件的监听,这是把所有的累活都一个人干了。