一文搞懂Spring Boot 事件监听机制

SpringBoot源码系列:
一文搞懂Spring Boot中java -jar启动jar包的原理
一文搞懂SpringBoot启动流程及自动配置
一文搞懂SpringBoot内嵌的Tomcat
一文搞懂SpringApplication对象的构建及spring.factories的加载时机

关于监听器模式、观察者模式请自行百度在此不再赘述。
1、Spring Boot 监听器模式要素

事件:SpringApplicationEvent,它是一个抽象类,继承了ApplicationEvent,是一个与SpringApplication相关的基类。

监听器:SpringApplicationRunListener,对SpringApplication的run方法进行监听。由SpringFactoriesLoader加载。

多播器:ApplicationEventMulticaster是一个接口,管理着多个ApplicationListener,并向其发布事件。他是一个应用程序事件发布者(ApplicationEventPublisher),通常一个ApplicationContext使用他来发布事件。

2、我们以Spring Boot中的应用启动事件来研究一下Spring Boot中的监听器机制。重点关注SpringApplication中的run()方法里面的下述内容。

public ConfigurableApplicationContext run(String... args) {
		......
		//获取SpringApplicationRunListeners 
		SpringApplicationRunListeners listeners = getRunListeners(args);
		//触发应用启动事件ApplicationStartingEvent
		listeners.starting(bootstrapContext, this.mainApplicationClass);
		......
	}

3、进入SpringApplicationRunListeners。

void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
		//创建一个新步骤他的名称是spring.boot.application.starting
		//访问SpringApplicationRunListener类型的对象,并执行监听器动作starting,(listener) -> listener.starting(bootstrapContext) 在run方法第一次启动时调用,完成早期的一些初始化。
		//访问StartupStep类型的对象,并给当前步骤打一个StartupStep.Tag,标记key为mainApplicationClass,value为我们的主启动类com.example.springbootdemo.SpringbootdemoApplication
		doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),
		//
				(step) -> {
					if (mainApplicationClass != null) {
						step.tag("mainApplicationClass", mainApplicationClass.getName());
					}
				});
	}

我们看一下上面的doWithListeners方法。看下面源码可知,这里使用了消费性接口java.util.function.Consumer,Consumer 表明我们想要访问SpringApplicationRunListener类型的对象,并对其执行某些操作如starting,Consumer stepAction表明我们想要访问StartupStep,并对其执行某些操作如打tag。

private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction,
			Consumer<StartupStep> stepAction) {
		//创建一个stepName步骤并且标记他开始。通过DefaultApplicationStartup的start方法创建一个默认启动步骤(DefaultStartupStep)。	
		StartupStep step = this.applicationStartup.start(stepName);
		//执行给定的监听器动作starting
		this.listeners.forEach(listenerAction);
		if (stepAction != null) {
		//stepAction 不为空,执行启动步骤(StartupStep)
			stepAction.accept(step);
		}
		//步骤结束。
		step.end();
	}

4、上面doWithListeners方法执行到this.listeners.forEach(listenerAction)时会执行给定的监听器动作starting,实际上执行的是SpringApplicationRunListener的实现类EventPublishingRunListener中的starting方法(run方法首次启动时调用starting方法,完成一些早期的初始),在该方法中通过SimpleApplicationEventMulticaster对象的multicastEvent方法多播应用启动事件ApplicationStartingEvent。

SimpleApplicationEventMulticaster中的starting方法

public void starting(ConfigurableBootstrapContext bootstrapContext) {
        //多播事件
        //这里构建的ApplicationStartingEvent继承自SpringApplicationEvent
		this.initialMulticaster
				.multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));
	}

5、我们进一步研究SimpleApplicationEventMulticaster对象的multicastEvent方法

    @Override
	public void multicastEvent(ApplicationEvent event) {
	    //多播的event为org.springframework.boot.context.event.ApplicationStartingEvent[source=org.springframework.boot.SpringApplication]
	    //默认的事件类型为org.springframework.boot.context.event.ApplicationStartingEvent
		multicastEvent(event, resolveDefaultEventType(event));
	}
	
    @Override
	public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
	    //此时eventType不为空所以type 此时为org.springframework.boot.context.event.ApplicationStartingEvent
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		//获取任务线程池,让监听器在不同的线程中执行。从而可以避免恶意监听器阻塞整个应用程序的危险。
		//此时获取Executor的是null
		Executor executor = getTaskExecutor();
		//getApplicationListeners方法返回与给定类型匹配的ApplicationListener集合,不匹配的提前排除
		for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			//此时获取的listener是LoggingApplicationListener
			if (executor != null) {
				executor.execute(() -> invokeListener(listener, event));
			}
			else {
			//根据给定事件org.springframework.boot.context.event.ApplicationStartingEvent调用给定监听器LoggingApplicationListener
				invokeListener(listener, event);
			}
		}
	}

6、继续看SimpleApplicationEventMulticaster中是如何调用监听器的。

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
		//获取此多播器的错误处理程序,此时为null
		ErrorHandler errorHandler = getErrorHandler();
		if (errorHandler != null) {
		//ErrorHandler 不为空需要处理error
			try {
				doInvokeListener(listener, event);
			}
			catch (Throwable err) {
				errorHandler.handleError(err);
			}
		}
		else {
		    //调用监听器具体逻辑
			doInvokeListener(listener, event);
		}
	}

进入具体调用监听器的方法,重点关注listener.onApplicationEvent(event);

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
		try {
		    //LoggingApplicationListener 调用onApplicationEvent方法来处理应用启动事件(ApplicationStartingEvent)
			listener.onApplicationEvent(event);
		}
		catch (ClassCastException ex) {
			......
		}
	}

7、接下来看一下LoggingApplicationListener 中的onApplicationEvent方法,此时我们的事件是ApplicationStartingEvent所以此时会进入onApplicationStartingEvent方法。

public void onApplicationEvent(ApplicationEvent event) {
		if (event instanceof ApplicationStartingEvent) {
			onApplicationStartingEvent((ApplicationStartingEvent) event);
		}
		else if (event instanceof ApplicationEnvironmentPreparedEvent) {
			onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
		}
		else if (event instanceof ApplicationPreparedEvent) {
			onApplicationPreparedEvent((ApplicationPreparedEvent) event);
		}
		else if (event instanceof ContextClosedEvent
				&& ((ContextClosedEvent) event).getApplicationContext().getParent() == null) {
			onContextClosedEvent();
		}
		else if (event instanceof ApplicationFailedEvent) {
			onApplicationFailedEvent();
		}
	}

进入onApplicationStartingEvent方法。

private void onApplicationStartingEvent(ApplicationStartingEvent event) {
        //获取从之前/META-INF/spring.factories加载出来的LoggingSystemFactory
        //LoggingSystemFactory 该日志系统工厂支持生产提下三种日志系统
        //LogbackLoggingSystem
        //Log4J2LoggingSystem
        //JavaLoggingSystem
        //此处我们初始化loggingSystem 获取到的是LogbackLoggingSystem
		this.loggingSystem = LoggingSystem.get(event.getSpringApplication().getClassLoader());
		//初始化LoggingInitializationContext之前调用beforeInitialize方法来减少不必要的日志输出,直至系统初始化完成。
		this.loggingSystem.beforeInitialize();
	}

8、到此doWithListeners方法中this.listeners.forEach(listenerAction)执行结束。接下来就执行doWithListeners方法中的stepAction.accept(step);

private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction,
			Consumer<StartupStep> stepAction) {
		StartupStep step = this.applicationStartup.start(stepName);
		this.listeners.forEach(listenerAction);
		if (stepAction != null) {
			stepAction.accept(step);
		}
		step.end();
	}

执行stepAction.accept(step)相当于执行上述第三步中的starting方法的下面lambada表达式,因为我们使用了消费性接口java.util.function.Consumer。

		//当前步骤打一个StartupStep.Tag,标记key为mainApplicationClass,value为 	 	com.example.springbootdemo.SpringbootdemoApplication
		 (step) -> {
					if (mainApplicationClass != null) {
						step.tag("mainApplicationClass", mainApplicationClass.getName());
					}
				}

至此SpringBoot中的事件监听机制分析完毕。
总结上述流程:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值