spring boot原理分析(七):spring boot运行时事件的监听

前言

    在原理分析(六)介绍spring boot启动流程中涉及到的组件或者模块的准备,事件监听器就是其中的一块。事件监听器的运行主要包括三个部分。首先第一部分是用来处理事件的监听器的初始化,这是在SpringApplication的构造函数中完成的。

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
  ......
  setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
  ......
}

第二部分是构造并初始化了事件监听器的管理器列表,这一步是在SpringApplication的run方法调用getRunListeners实现。

public ConfigurableApplicationContext run(String... args) {
  ......
  SpringApplicationRunListeners listeners = getRunListeners(args);
  listeners.starting();
  try {
    ......
    ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
    ......
    prepareContext(context, environment, listeners, applicationArguments, printedBanner);
    refreshContext(context);
    ......
    listeners.started(context);
    callRunners(context, applicationArguments);
  }
  catch (Throwable ex) {
    handleRunFailure(context, ex, exceptionReporters, listeners);
    throw new IllegalStateException(ex);
  }
    ......
    listeners.running(context);
    ......
  return context;
}

最后一个部分是事件监听器的管理器发送各种事件,这些操作是分布在run方法和其他run方法中调用的传入listeners的方法中。但是spring boot的事件监听不仅仅局限在SpringApplication中SpringApplicationRunListeners。无论是环境environment还是上下文context的准备(prepare)方法中都只是调用却没有持有listeners,这是因为context自己初始化和构造了另一套事件监听系统。虽然这部分内容属于上下文context的相关部分,但是也会在补充介绍一些。

事件

事件基础定义
//SpringApplicationEvent.java
public abstract class SpringApplicationEvent extends ApplicationEvent {

	private final String[] args;

	public SpringApplicationEvent(SpringApplication application, String[] args) {
		super(application);
		this.args = args;
	}

	public SpringApplication getSpringApplication() {
		return (SpringApplication) getSource();
	}

	public final String[] getArgs() {
		return this.args;
	}
}
//ApplicationEvent.java
public abstract class ApplicationEvent extends EventObject {
	private final long timestamp;

	public ApplicationEvent(Object source) {
		super(source);
		this.timestamp = System.currentTimeMillis();
	}

	public final long getTimestamp() {
		return this.timestamp;
	}
}

    spring boot中事件都是继承自SpringApplicationEvent的抽象类,命名显示这类事件主要是反馈spring boot应用运行时的状态的改变。SpringApplicationEvent持有了一个字符串用来存储应用传入的参数,除此之外,就是继承了ApplicationEvent抽象类。ApplicationEvent持有一个时间戳,存储事件对象被构造的时间。构造方法传入的Object source代表事件被发出的类,这个Object对象是由父类EventObject持有的。需要注意的是,ApplicationEvent是由spring-context jar包提供的,这意味着这种事件处理的模式是spring-context提供的能力。

事件的类型

    事件的类型可以由ApplicationEvent的子类例举出来,当然上文提到SpringApplicationEvent也是子类之一。
spring boot运行时事件的监听-ApplicationEvent.png
    上图展示了ApplicationEvent一些子类。虽有些并不是处于spring boot包,这也无伤大雅。根据图片可以很容易将这些子类分门别类,有些子类的名字也比较露骨,几乎一眼能识别出主要在什么情况下发挥作用。

  • SpringApplicationEvent
        SpringApplicationEvent被使用在SpringApplication类中,用来反馈应用启动状态变化的系列事件。当spring boot在启动过程中,应用状态发生变化时,相应的子类Event就会被发出,开发者可以通过编写事件处理器,在合适的阶段插入自己的代码。比如我有一篇文章讲了使用bytebuddy解决spring AOP嵌套方法不生效的问题,代码注入需要在上下文准备之前,所以需要监听ApplicationEnvironmentPreparedEvent。
        下面依次介绍每个事件抛出具体场景。在上面run方法中,其实已经出现了大部分的事件。listeners.starting()方法抛出了ApplicationStartingEvent事件,代表应用正在启动中;prepareEnvironment方法中传入了listeners,抛出了ApplicationEnvironmentPreparedEvent,代表环境已经准备就绪。prepareContext方法中抛出了两个事件,
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
    SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
  context.setEnvironment(environment);
  postProcessApplicationContext(context);
  applyInitializers(context);
  listeners.contextPrepared(context);
  ......
  listeners.contextLoaded(context);
}

其中listeners.contextPrepared抛出了ApplicationContextInitializedEvent代表上下文已经初始化完成,listeners.contextLoaded抛出了ApplicationPreparedEvent代表上下文加载完成,应用准备完毕;
接着run方法中调用listeners.started抛出ApplicationStartedEvent,代表应用启动准备完成;在try catch中的handleRunFailure方法传入了listeners,内部调用了listeners.failed方法,这个毫无疑问是抛出了ApplicationFailedEvent事件,代表应用启动失败;最后run方法中调用listeners.running方法,抛出了ApplicationReadyEvent事件,代表应用启动成功。

  • ApplicationContextEvent
        ApplicationContextEvent系列的事件是用来反馈上下文加载过程中状态的变化,主要是在AbstractApplicationContext上下文抽象类中被子类调用并发送事件。同理,根据事件的名称也容易判断事件什么时候被抛出,具体内容还是放在context相关的文章中详细解释。

  • ParentContextAvailableEvent
        这个事件用来反馈存在父上下文并且已经准备就绪。

  • WebServerInitializedEvent
        这个系列的两个事件,看了在原理分析(六)应该有印象,这里分别代表SERVLET、REACTIVE的web服务已经初始化完成。

  • 其他
        RequestHandledEvent系列是处理请求完成后会发成的事件。
        DataSourceSchemaCreatedEvent是和数据库相关的,当DataSourceSchema创建完成后会抛出这个事件。
        ExitCodeEvent是当应用退出时,产生了退出码之后会抛出的事件,可以根据退出码判断应用退出的原因。
        JobExecutionEvent是job执行发出的事件,这里的JobExcution是spring batch中提供的。
        PayloadApplicationEvent不是在特定情况下被发出,只是当需要传递一个负载信息(Payload)给特定处理器时,可以使用这个事件。PayloadApplicationEvent具有比较强的通用性。

事件的处理

注册一个事件监听的处理器
public class AppEnvListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
    @Override
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent applicationEnvironmentPreparedEvent) {
    }
}

    ApplicationListener是一个接口模板,泛型参数需要传入这个监听器监听的事件,比如这里监听ApplicationEnvironmentPreparedEvent。ApplicationListener接口中就只有一个方法onApplicationEvent,在相应监听事件被发出时,就会调用子类的onApplicationEvent方法,传入对应的事件实例。在实现onApplicationEvent方法时,可以使用事件实例中包含的参数,比如ApplicationEnvironmentPreparedEvent就会包含环境Environment的实例。
    最后,不要忘记,要使这个监听器生效,需要在spring.factories中注册。

org.springframework.context.ApplicationListener=\
com.xiaojukeji.epower.aegis.utils.context.AppEnvListener

关于spring.factories文件中的配置能够生效的原理已经在原理分析(三)原理分析(四)中介绍过了。

事件监听的处理器如何处理事件

    事件的发送和处理是由ApplicationEventMulticaster来实现的,这个组件同样也是由spring-context包提供的。

public interface ApplicationEventMulticaster {

	void addApplicationListener(ApplicationListener<?> listener);

	void addApplicationListenerBean(String listenerBeanName);

	void removeApplicationListener(ApplicationListener<?> listener);


	void removeApplicationListenerBean(String listenerBeanName);

	void removeAllListeners();

	void multicastEvent(ApplicationEvent event);

	void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);

}

    由接口定义的方法可以看出,ApplicationEventMulticaster子类中需要维护注册的ApplicationListener的集合,当抛出事件时使用multicastEvent方法传入事件。其子类AbstractApplicationEventMulticaster也确实是这样做的,只是使用ListenerRetriever对ApplicationListener进行了封装。

public abstract class AbstractApplicationEventMulticaster
		implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {
        .....
	private final ListenerRetriever defaultRetriever = new ListenerRetriever(false);

	final Map<ListenerCacheKey, ListenerRetriever> retrieverCache = new ConcurrentHashMap<>(64);
        .....
}

    ApplicationEventMulticaster类中包含一个默认的ListenerRetriever和一个键值是ListenerCacheKey的ListenerRetriever的map。先看一下ListenerRetriever的定义。

private class ListenerRetriever {

  public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();

  public final Set<String> applicationListenerBeans = new LinkedHashSet<>();

  private final boolean preFiltered;

  public ListenerRetriever(boolean preFiltered) {
    this.preFiltered = preFiltered;
  }

  public Collection<ApplicationListener<?>> getApplicationListeners() {
    ......
    return allListeners;
  }
}

ListenerRetriever比较简单,就是封装了ApplicationListener和ApplicationListener bean name的集合,实现getApplicationListeners方法能够返回所有的ApplicationListener。那么ListenerCacheKey是什么呢?这个key其实就是对ListenerRetriever进行分类,生成标准是事件的类型和事件产生的类的类型(event type and source type),这样在处理事件时,可以加快搜索对应的ApplicationListener。
    最后spring boot中使用的ApplicationEventMulticaster的实现类是SimpleApplicationEventMulticaster,

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {

	@Nullable
	private Executor taskExecutor;

	@Nullable
	private ErrorHandler errorHandler;
        ......
	@Override
	public void multicastEvent(ApplicationEvent event) {
		multicastEvent(event, resolveDefaultEventType(event));
	}

	@Override
	public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		Executor executor = getTaskExecutor();
		for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			if (executor != null) {
				executor.execute(() -> invokeListener(listener, event));
			}
			else {
				invokeListener(listener, event);
			}
		}
	}
        ......
}

其允许传入Executor taskExecutor和ErrorHandler errorHandler用来异步执行Listener对事件的响应和错误的处理。multicastEvent就是用来抛出事件,并调用相应的Listener来处理事件的方法。

ApplicationContextEvent事件监听实例

    ApplicationContextEvent的事件监听处理器Listeners在SpringApplication的构造函数获取并初始化,这在前言中已经说过。接下来在run方法中,事件的发送和处理借用的是SpringApplicationRunListeners。

class SpringApplicationRunListeners {
        ......
	private final List<SpringApplicationRunListener> listeners;
        ......
}

SpringApplicationRunListeners其实只是对SpringApplicationRunListener的列表封装了一下,作为代理所有操作还是遍历每一个SpringApplicationRunListener来处理的。

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;
		this.initialMulticaster = new SimpleApplicationEventMulticaster();
		for (ApplicationListener<?> listener : application.getListeners()) {
			this.initialMulticaster.addApplicationListener(listener);
		}
	}
  
	@Override
	public void starting() {
		this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
	}
         ......
}

    事实上,默认情况下,继承自SpringApplicationRunListener的子类只有一个,就是EventPublishingRunListener。也是在这个子类中,构造了SimpleApplicationEventMulticaster的实例,并将注册的事件监听处理器Listener传了进去,用来发送和处理事件。比如对应run方法中的starting()方法,这里就直接使用initialMulticaster.multicastEvent处理了ApplicationStartingEvent的事件。对应environmentPrepared、contextPrepared、started等方法也是同样的道理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值