您的位置:首页 > 编程语言 > Java开发

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

2017-12-16 11:49 1796 查看
摘要:事件驱动模型,也就是我们经常提到用到的观察者模式。当然也可以将其理解为发布-订阅模型。具体的实现要素有如下几个方面。
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
后续的系列文章,我们对于核心的监听器一个个进行讲解,从而加深印象。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  源码 spring springboot