前言:

我们知道在 Spring 、Spring Boot 的启动源码中都大量的使用了事件监听机制,也就是我们说的的监听器,监听器的实现基于观察者模式,也就是我们所说的发布订阅模式,这种模式可以在一定程度上实现代码的解耦,但如果想要实现系统层面的解耦,那么就要使用消息队列了,本篇从详细分析一下监听器的原理。

Spring Boot 系列文章传送门

Spring Boot 启动流程源码分析(2)

Spring Boot 启动流程源码分析(2)

Spring Boot 自动配置实现原理(源码分析)

Spring Boot 自定义 starter 启动器

事件监听器的核心元素

  • 事件(ApplicationEvent):监听器触发的原因,当事件源发生了某个事件,对应的监听器就会被触发,Spring 中场常见的事件 ContextRefreshEvent、ContextRStartEvent、ContextStoppedEvent、ContextCloseEvent、ReqyestHandlerEvent 等事件。
  • 监听器(ApplicationListener):监听特定事件,并在事件内部定义了事件发生后的响应逻辑,对应观察者模式的观察者。
  • 事件发布器(ApplicationEventMulticaster):负责发布事件,维护事件和事件监听器之间的关系,并在事件发生时通知相关监听器,对应观察者模式中的被观察者。

监听器的工作流程

  1. 事件监听器注册到事件发起器,用于监听事件。
  2. 事件源产生事件,然后像发布器发布事件。
  3. 事件发布器回调事件监听器的回调方法。
  4. 事件监听器的回调方法被调用,执行业务。

事件发布器的初始化时机

事件发布器又叫事件多播器,我们分析了事件监听器的工作流程,其中事件监听器是要往事件发布器中注册的,那意味着事件监听器开始注册之前已经有了事件发布器,那事件发布器是什么时候初始化的呢?事件发布器是在 AbstractApplicationContext#refresh 方法中调用了 AbstractApplicationContext#registerListeners 方法,完成事件发布器的初始化,如下:

//初始化一个事件多播器
protected void initApplicationEventMulticaster() {
    //获取 beanFactory
    ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
    //beanFactory 中是否有 应用程序事件多播器
    if (beanFactory.containsLocalBean("applicationEventMulticaster")) {
        //有直接赋值
        this.applicationEventMulticaster = (ApplicationEventMulticaster)beanFactory.getBean("applicationEventMulticaster", ApplicationEventMulticaster.class);
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
        }
    } else {
        //没有就创建一个 应用程序事件多播器
        this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
        //应用程序事件多播器 注册到 beanFactory 中
        beanFactory.registerSingleton("applicationEventMulticaster", this.applicationEventMulticaster);
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("No 'applicationEventMulticaster' bean, using [" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
        }
    }

}

事件监听器的注册时机

事件监听器需要注册到事件发布器中,那事件监听器是设么时候注册的?监听器的注册是在 Spring 容器刷新的时候完成的,AbstractApplicationContext#refresh 方法中调用了 AbstractApplicationContext#registerListeners 方法完成注册,如下:

//注册监听器
protected void registerListeners() {
    //准备遍历监听器
    Iterator var1 = this.getApplicationListeners().iterator();

    while(var1.hasNext()) {
        ApplicationListener<?> listener = (ApplicationListener)var1.next();
        //往 应用程序事件多播器 中添加监听器
        this.getApplicationEventMulticaster().addApplicationListener(listener);
    }
    //获取所有监听器
    String[] listenerBeanNames = this.getBeanNamesForType(ApplicationListener.class, true, false);
    String[] var7 = listenerBeanNames;
    int var3 = listenerBeanNames.length;

    for(int var4 = 0; var4 < var3; ++var4) {
        String listenerBeanName = var7[var4];
        //往 应用程序事件多播器 中添加监听器
        this.getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
    }
    //早期要处理的事件
    Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
    this.earlyApplicationEvents = null;
    if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
        //有早期需要处理的事件
        Iterator var9 = earlyEventsToProcess.iterator();
        //遍历派发出去
        while(var9.hasNext()) {
            ApplicationEvent earlyEvent = (ApplicationEvent)var9.next();
            //使用 应用程序事件多播器 将事件派发出去  重点关注 multicastEvent 方法
            this.getApplicationEventMulticaster().multicastEvent(earlyEvent);
        }
    }

}

事件监听器在完成事件注册的时候,最后会调用 SimpleApplicationEventMulticaster#multicastEvent 方法(Spring Boot 源码中也会调用这段方法)发布事件,如下:

//org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent)
public void multicastEvent(ApplicationEvent event) {
	//调用 简单应用程序事件多播器 SimpleApplicationEventMulticaster#multicastEvent
	this.multicastEvent(event, this.resolveDefaultEventType(event));
}


//org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
	//解析事件类型
	ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
	//获取线程池
	Executor executor = this.getTaskExecutor();
	//迭代遍历监听器
	Iterator var5 = this.getApplicationListeners(event, type).iterator();

	while(var5.hasNext()) {
		//监听器
		ApplicationListener<?> listener = (ApplicationListener)var5.next();
		if (executor != null) {
			//线程池异步发送监听事件
			executor.execute(() -> {
				this.invokeListener(listener, event);
			});
		} else {
			//同步发送监听事件
			this.invokeListener(listener, event);
		}
	}

}

SimpleApplicationEventMulticaster#multicastEvent 方法中可以发布异步事件,如果事件发布器有线程池就可以发布异步事件。

事件发布器发布完成事件后又是怎么完成事件回调的?

前面我们在分析事件发布的时候最后调用了 SimpleApplicationEventMulticaster#multicastEvent 方法,SimpleApplicationEventMulticaster#multicastEvent 方法中又调用了 SimpleApplicationEventMulticaster#invokeListener 方法,如下:

//org.springframework.context.event.SimpleApplicationEventMulticaster#invokeListener
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
	//获取 ErrorHandler
	ErrorHandler errorHandler = this.getErrorHandler();
	//ErrorHandler  为空判断
	if (errorHandler != null) {
		try {
			//不为空 调用 doInvokeListener 如果有异常 调用errorHandler 来处理
			this.doInvokeListener(listener, event);
		} catch (Throwable var5) {
			errorHandler.handleError(var5);
		}
	} else {
		//没有 errorHandler 
		this.doInvokeListener(listener, event);
	}

}


//org.springframework.context.event.SimpleApplicationEventMulticaster#doInvokeListener
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
	try {
		//执行监听器方法
		listener.onApplicationEvent(event);
	} catch (ClassCastException var6) {
		String msg = var6.getMessage();
		if (msg != null && !this.matchesClassCastMessage(msg, event.getClass())) {
			throw var6;
		}

		Log logger = LogFactory.getLog(this.getClass());
		if (logger.isTraceEnabled()) {
			logger.trace("Non-matching event type for listener: " + listener, var6);
		}
	}

}

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    void onApplicationEvent(E var1);
}

SimpleApplicationEventMulticaster#invokeListener 方法会对是否有 ErrorHandler 进行不同的处理,但最终都会调用 SimpleApplicationEventMulticaster#doInvokeListener 方法,完成监听器方法的调用。

Spring Boot 是如何完成事件监听的?

Spring Boot 在启动时候调用了 SpringApplication#run 方法,SpringApplication#run 方法中有如下两行代码:

//获取 SpringApplicationRunListener 实例数组 默认获取的是 EventPublishRunListener
SpringApplicationRunListeners listeners = this.getRunListeners(args);
//启动监听 重点关注
listeners.starting(bootstrapContext, this.mainApplicationClass);

我们接着分析 SpringApplicationRunListeners#starting 方法。

SpringApplicationRunListeners#starting 方法源码分析

SpringApplicationRunListeners#starting 方法最终调用了 SimpleApplicationEventMulticaster#multicastEvent 方法,这个方法我们刚刚在上面分析了的,至此回到了 Spring 的方法,Spring Boot 和 Spring 事件监听机制原理基本相同。

//org.springframework.boot.SpringApplicationRunListeners#starting
void starting() {
	//迭代遍历所有监听器
	Iterator var1 = this.listeners.iterator();

	while(var1.hasNext()) {
		SpringApplicationRunListener listener = (SpringApplicationRunListener)var1.next();
		//调用 事件发布运行监听器 EventPublishingRunListener#starting 方法
		listener.starting();
	}

}

//org.springframework.boot.context.event.EventPublishingRunListener#starting
public void starting() {
	//调用 简单应用程序事件多播器 SimpleApplicationEventMulticaster#multicastEvent 多播事件 也就是启动监听事件
	this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}

Spring Boot 中事件发布器何时完成初始化的?

我们知道 Spring 中事件发布器是在 AbstractApplicationContext#refresh 方法中完成初始化的的,刚刚上面分析 Spring Boot 的事件发布流程中似乎没有时间发布器的初始化操作,那 Spring Boot 事件发布器是何时初始化的?请看如下代码:

//org.springframework.boot.SpringApplicationRunListeners#starting
void starting() {
	//迭代遍历所有监听器
	Iterator var1 = this.listeners.iterator();

	while(var1.hasNext()) {
		SpringApplicationRunListener listener = (SpringApplicationRunListener)var1.next();
		//调用 事件发布运行监听器 EventPublishingRunListener#starting 方法
		listener.starting();
	}

}

//org.springframework.boot.context.event.EventPublishingRunListener#starting
public void starting() {
	//调用 简单应用程序事件多播器 SimpleApplicationEventMulticaster#multicastEvent 多播事件 也就是启动监听事件
	this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}

EventPublishingRunListener#starting 方法中直接使用了 SimpleApplicationEventMulticaster#multicastEvent 发布事件,SimpleApplicationEventMulticaster 作为 EventPublishingRunListener 类的一个属性,在创建 EventPublishingRunListener 对象时候已经初始化了

EventPublishingRunListener 类源码分析

EventPublishingRunListener 这里只是看下构造方法,事件发布器 SimpleApplicationEventMulticaster 作为 EventPublishingRunListener 的一个属性,在 EventPublishingRunListener 完成创建的时候已经初始化了,而 EventPublishingRunListener 又是通过 Spring Boot 自动注入完成创建的。

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();
        Iterator var3 = application.getListeners().iterator();

        while(var3.hasNext()) {
            ApplicationListener<?> listener = (ApplicationListener)var3.next();
            this.initialMulticaster.addApplicationListener(listener);
        }

    }
	//省略部分代码。。。。
}

总结:Spring Boot 事件监听机制工作原理其实和 Spring 的事件监听机制原理一样,Spring Boot 只是通过自动配置简化了一些复杂的配置而已,如果我们熟读 Spring 的源码,那对 Spring Boot 的源码解读是有极大帮助的,Spring Boot 很多功能都是基于 Spring 的,最后本篇关于 Spring Boot 事件监听机制工作原理的分享,希望可以帮助到需要的小伙伴。

如有不正确的地方请各位指出纠正。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐