springboot源码分析14-事件发布机制以及应用监听器

摘要:事件驱动模型,也就是我们经常提到用到的观察者模式。当然也可以将其理解为发布-订阅模型。具体的实现要素有如下几个方面。

1、首先是一对多的关系,一是目标对象,多则是观察者对象。比如报社是一个,而订报者是多个。

2、当目标对象的行为发生变化的时候,多个观察者对象会级联触发并做出相应的处理。换言之,目标对象的行为发生变化的时候,只需要通知一下所有的观察者对象(订阅过的)即可。具体的各个观察者怎么去处理,使用什么方式去处理,并不是目标对象所需要考虑的范畴。也就是说目标与观察者的实现是低耦合。目标对象的职责就是去通知各个观察者,各个观察者的职责是具体做事情的。大家各司其职协调工作。

3、目标对象怎么能在自身状态发生变化的时候,去实时通知到各个观察者呢?无外乎就是如下的两种思路。

实现方案1:

     所有需要通知的观察者去目标对象中注册登记,当目标对象需要通知的时候,查询登记列表中的所有观察者,然后一个个的下发。

实现方案2:

所有需要通知的观察者去目标对象中注册登记,登记的时候告诉目标对象自己需要监听的事件类型,只有是自己注册的事件变化时,才接受通知,否则目标对象的其他事件不要通知这个观察者。

上述的两个方案,方案1强调的是目标对象只要发生行为状态改变,所有的观察者都可以收到通知,并自行处理。方案2有点类似精准投递,比如观察者对象1只监听a事件,那么当目标对象触发b事件的时候不需要通知观察者对象1。两种方案各有优缺点,我个人倾向使用方案2。因为该方案可以根据不同的事件源去通知不同的观察者。

了解了上述的内容之后,接下来我们看一下Springboot中所使用的事件发布机制以及ApplicationListener。

1.1. SpringApplicationRunListener

我们一步到位,直接定位到SpringApplication类中的run方法,相关实现代码如下:

public ConfigurableApplicationContext run(String... args) {

...

SpringApplicationRunListeners listeners = getRunListeners(args);

listeners.starting();

...

}

我们重点看一下getRunListeners方法的处理逻辑,该方法的实例代码如下:

private SpringApplicationRunListeners getRunListeners(String[] args) {

Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };

return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(

pringApplicationRunListener.class, types, this, args));

}

首先看一下getSpringFactoriesInstances方法,代码如下:

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,

Class<?>[] parameterTypes, Object... args) {

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

Set<String> names = new LinkedHashSet<>(

SpringFactoriesLoader.loadFactoryNames(type, classLoader));

List<T> instances = createSpringFactoriesInstances(type, parameterTypes,

classLoader, args, names);

AnnotationAwareOrderComparator.sort(instances);//order值越小,越靠前

return instances;

}

上述的代码,前面的系列文章也详细讲解过,就是查询所有META-INF/spring.factories配置文件中key为org.springframework.boot.SpringApplicationRunListener的值,我们找一下spring-boot-2.0.0.M7.jar中的META-INF/spring.factories配置文件,相关配置如下所示:

# Run Listeners

org.springframework.boot.SpringApplicationRunListener=\

org.springframework.boot.context.event.EventPublishingRunListener

因此上述代码执行完毕之后。会通过反射实例化EventPublishingRunListener类,该类的构造函数如下:

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);

}

}

首先,实例化SimpleApplicationEventMulticaster类,然后调用application对象中的getListeners()方法,并循环将该函数的返回值集合添加到initialMulticaster中。initialMulticaster我们可以将其理解为事件发布器。getListeners()方法中返回的集合在哪里初始化的呢?我们继续回到SpringApplication类的构造函数中。如下所示:

private List<ApplicationListener<?>> listeners;

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {

...

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

}

public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {

this.listeners = new ArrayList<>();

this.listeners.addAll(listeners);

}

在SpringApplication类的构造函数中,也就直接通过getSpringFactoriesInstances方法直接获取到META-INF/spring.factories配置文件中key为org.springframework.context.ApplicationListener的值,我们找一下spring-boot-2.0.0.M7.jar中的META-INF/spring.factories配置文件,相关配置如下所示:

# Application Listeners

org.springframework.context.ApplicationListener=\

org.springframework.boot.ClearCachesApplicationListener,\

org.springframework.boot.builder.ParentContextCloserApplicationListener,\

org.springframework.boot.context.FileEncodingApplicationListener,\

org.springframework.boot.context.config.AnsiOutputApplicationListener,\

org.springframework.boot.context.config.ConfigFileApplicationListener,\

org.springframework.boot.context.config.DelegatingApplicationListener,\

org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\

org.springframework.boot.context.logging.LoggingApplicationListener,\

org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

上述的13个监听类均是监听不同的事件进行处理的,我们也可以自定义一些监听器进行业务处理,添加方式如下所示:

SpringApplication springApplication = new SpringApplication(DemoApplication.class);

   springApplication.addListeners(new ShareniuApplicationListener());

通过上述代码我们可以看出,所有的事件监听器最终存储在SpringApplication类中的listeners集合中。

1.2. Springboot中事件的发布以及监听

接下来,我们来看一下Springboot中事件的发布以及监听。我们继续回归到listeners.starting()方法中。

public void starting() {

for (SpringApplicationRunListener listener : this.listeners) {

listener.starting();

}

}

循环遍历所有的listeners,并依此调用listeners中的starting方法。

注意:listeners是SpringApplicationRunListener类型,并非是ApplicationListener类型,这点一定不要搞混淆了。SpringApplicationRunListener中持有所有的ApplicationListener类型监听器集合。EventPublishingRunListener类中的starting方法代码如下:

public void starting() {

this.initialMulticaster.multicastEvent(

new ApplicationStartingEvent(this.application, this.args));

}

注意这里产生的事件是ApplicationStartingEvent类型,因此只有监听到ApplicationStartingEvent事件的监听器才可以观察到进而进行自己的处理。this.initialMulticaster.multicastEvent方法实现如下:

public void multicastEvent(ApplicationEvent event) {

multicastEvent(event, resolveDefaultEventType(event));

}

public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {

ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));

for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {

Executor executor = getTaskExecutor();

if (executor != null) {

executor.execute(() -> invokeListener(listener, event));

}

else {

invokeListener(listener, event);

}

}

}

在构造ApplicationStartedEvent时传给它的基类EventObject的protected不可序列化属性source。实例化ApplicationStartedEvent后instance.getClass()并包装为ResolvableType类型以保存类型信息,并将它和event作为参数传入SimpleApplicationEventMulticaster的multicastEvent方法。multicastEvent首先获取ApplicationListener,使用getApplicationListeners方法,这个方法中抛开对listener做了一些缓存类工作外,主要就是将事件和对应的监听器做了下是否支持的验证,返回通过了retrieveApplicationListeners中通过了supportsEvent验证的监听器集合,这里就体现出了ResolvableType的作用,它保存了类型的信息同时对泛型类型也支持。

在整个SpringBoot启动的过程中,会先后出产生如下的几个事件。

ApplicationStartingEvent-->>ApplicationEnvironmentPreparedEvent-->>ApplicationPreparedEvent

ContextRefreshedEvent-->>ApplicationReadyEvent-->>ContextClosedEvent

后续的系列文章,我们对于核心的监听器一个个进行讲解,从而加深印象。

时间: 2024-07-30 11:32:26

springboot源码分析14-事件发布机制以及应用监听器的相关文章

springboot源码分析3-springboot之banner类架构以及原理

继续上文的<<springboot源码分析2-springboot 之banner定制以及原理章节>>进行讲解,上一节我们详细详解了banner的三种输出模式.banner的输出模式设置.banner类的架构.SpringApplicationBannerPrinter类.ImageBanner以及TextBanner的处理方式.本小节我们来重点讲解一下各种banner处理类的相关实现逻辑以及设计意图和职责. 1.1 SpringBootBanner类 SpringBootBann

springboot源码分析11-ApplicationContextInitializer原理

摘要:springboot源码分析10-ApplicationContextInitializer使用一文中,我们详细地讲解了ApplicationContextInitializer的三种使用方式,本文我们重点看一下为何这三种方式都可以使用,也就是框架是如何处理的.包括内置的ContextIdApplicationContextInitializer.DelegatingApplicationContextInitializer. 1.1. 用户手动添加ApplicationContextIn

springboot源码分析2-springboot 之banner定制以及原理

1. springboot源码分析2-springboot 之banner定制以及原理 springboot在启动的时候,默认会在控制台输出默认的banner.也就是我们经常所说的图案,输出的图案如下所示:   .   ____          _            __ _ _  /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \  \\/  ___)| |_)| | | |

springboot源码分析6-springboot之PropertySource类初探

摘要:本小节重点梳理一下PropertySource类的相关结构以及职责,本文的学习前提是学习了springboot源码分析5-springboot之命令行参数以及原理一文. 在springboot源码分析5-springboot之命令行参数以及原理一文中,我们看到了实例化Source类的时候,会去先实例化其父类SimpleCommandLinePropertySource.SimpleCommandLinePropertySource类的构造函数中直接解析了命令行参数以及值,然后返回封装好的C

springboot源码分析10-ApplicationContextInitializer使用

摘要:spring中ApplicationContextInitializer接口是在ConfigurableApplicationContext刷新之前初始化ConfigurableApplicationContext的回调接口.当spring框架内部执行 ConfigurableApplicationContext#refresh() 方法的时候回去回调. 1.1. 实现方式一 首先,我们需要自定义一个类并且实现ApplicationContextInitializer接口.示例代码如下:

springboot源码分析7-环境属性构造过程(上)

使用springboot的目的就是在项目开发中,快速出东西,因此springboot对于配置文件的格式支持是非常丰富的,最常见的配置文件后缀有如下四种:properties.xml.yml.yaml,比如我们在springboot项目根目录中配置了一个application.properties文件,则springboot项目启动的时候就会自动将该文件的内容解析并设置到环境中,这样后续需要使用该文件中配置的属性的时候,只需要使用@value即可.同理application.xml.applica

springboot源码分析14-ApplicationContextInitializer原理Springboot中PropertySource注解多环境支持以及原理

摘要:Springboot中PropertySource注解的使用一文中,详细讲解了PropertySource注解的使用,通过PropertySource注解去加载指定的资源文件.然后将加载的属性注入到指定的配置类,@value以及@ConfigurationProperties的使用.但是也遗留一个问题,PropertySource注解貌似是不支持多种环境的动态切换?这个问题该如何解决呢?我们需要从源码中看看他到底是否支持. 首先,我们开始回顾一下上节课说的PropertySource注解的

源码-Android中事件传递机制原理

问题描述 Android中事件传递机制原理 我们知道,所有的控件直接或间接的继承子View,View的子类有ViewGroup,并且ViewGroup的子类也会有其他的子View,那么他们之间事件的传递机制是怎样的?对源码有研究的吗? 解决方案 android事件传递机制Android 事件的传递机制Android之事件传递机制 解决方案二: http://blog.csdn.net/pi9nc/article/details/9281829http://www.csdn123.com/html

springboot源码分析9-random的使用以及原理

摘要:springboot框架为我们提供了很多的便利,其中有一个非常有意思的功能,那就是可以通过变量的方式来配置一个随机数random,然后使用random随机出各式各样数值.本位重点讲解一下random的使用以及框架内部的实现机制. 1.1. Springboot中random的使用 首先我们定义一个配置类,如下所示: 1 @Component 2 public class Config { 3  @Value("${random.value}") 4  private String