模仿Spring事件机制实现自定义事件驱动编程--Spring的事件机制源码分析(一)
2020/2/11 17:09:02
本文主要是介绍模仿Spring事件机制实现自定义事件驱动编程--Spring的事件机制源码分析(一),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
微信公众号:IT学习笔记
学习更多源码知识,欢迎关注。
注意:spring源码分析文章对应spring版本为 5.1.x
目录
1,概述
要想理解spring的事件机制,我觉得首先自己动手去撸一套简单的自定义事件驱动编程demo还是非常有必要滴,因为这样有助于理解spring事件机制。当然,这里也是模仿spring的事件机制的代码,不过下面看代码实现时可以先抛开spring的事件机制相关代码,将注意力集中到这个简单demo上即可。
在看这个自定义事件驱动编程时,首先要熟悉观察者设计模式,因为事件驱动编程可以说是观察者(发布订阅)模式的具体实现。推荐我的另一篇翻译的博文:观察者模式--设计模式(一),有需要的小伙伴们可以先学习下哈。
下面正式开始手撸代码实现,首先先放上下面代码的github地址:
2,自定义事件驱动编程
因为这篇文章是spring事件机制的前置文章,因此这里自定义实现一个模拟容器(可以理解为spring容器,servltet容器等)的生命周期事件的简单demo。
2.1 事件
先来看一下事件的整体架构图,让大家先有对事件有一个整体的认识,如下图:
1,Event接口
面向接口编程,首先先定义一个Event接口,该接口没有任何方法,可以说是事件的标志接口。
public interface Event extends Serializable { }复制代码
2,AbstractContextEvent
AbstractContextEvent是容器事件的基本抽象类,因为事件也可以携带数据,因此这里定义了一个timestamp属性,用来记录事件的发生时间。
public class AbstractContextEvent implements Event { private static final long serialVersionUID = -6159391039546783871L; private final long timestamp = System.currentTimeMillis(); public final long getTimestamp() { return this.timestamp; } }复制代码
3,ContextStartEvent
ContextStartEvent事件是AbstractContextEvent具体实现类,容器开始启动时触发,这里为了demo简单,这里不再定义任何事件逻辑,只是代表容器启动时的一个标志事件类。
public class ContextStartEvent extends AbstractContextEvent { }复制代码
4,ContextRunningEvent
ContextRunningEvent事件是AbstractContextEvent具体实现类,容器启动后时触发,这里为了demo简单,这里不再定义任何事件逻辑,只是代表容器启动运行时时的一个标志事件类。
public class ContextRunningEvent extends AbstractContextEvent { }复制代码
5,ContextDestroyEvent
ContextDestroyEvent事件是AbstractContextEvent具体实现类,容器销毁时触发,这里为了demo简单,这里不再定义任何事件逻辑,只是代表容器销毁时的一个标志事件类。
public class ContextDestroyEvent extends AbstractContextEvent { }复制代码
2.2 事件监听器
先来看一下事件监听器的整体架构图,让大家先有对事件监听器有一个整体的认识,如下图:
其中EventListener是所有事件监听器的基类接口,,是事件监听器的标志类接口,被所有具体的事件监听器实现。然后ContextListener接口是容器事件监听器接口,继承了EventListener,主要定义了如下事件监听方法:
void onApplicationEvent(T event);复制代码
然后ContextListener接口被三个具体的容器生命周期事件监听器实现,分别是ContextStartEventListener(监听容器启动时的ContextStartEvent),ContextRunningEventListener(监听容器启动后运行时的ContextRunningEvent)和ContextDestroyEventListener(监听容器销毁时的ContextDestroyEvent)。
下面看具体的代码实现:
public interface EventListener { } public interface ContextListener<T extends AbstractContextEvent> extends EventListener { /** * Handle an application event. * @param event the event to respond to */ void onApplicationEvent(T event); } public class ContextStartEventListener implements ContextListener<AbstractContextEvent> { /** * Handle an application event. * * @param event the event to respond to */ public void onApplicationEvent(AbstractContextEvent event) { if (event instanceof ContextStartEvent) { System.out.println("容器启动。。。,启动时间为:" + event.getTimestamp()); } } } public class ContextRunningEventListener implements ContextListener<AbstractContextEvent> { /** * Handle an application event. * * @param event the event to respond to */ public void onApplicationEvent(AbstractContextEvent event) { if (event instanceof ContextRunningEvent) { System.out.println("容器开始运行。。。"); try { Thread.sleep(3000); System.out.println("容器运行结束。。。"); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class ContextDestroyEventListener implements ContextListener<AbstractContextEvent> { /** * Handle an application event. * * @param event the event to respond to */ public void onApplicationEvent(AbstractContextEvent event) { if (event instanceof ContextDestroyEvent) { System.out.println("容器销毁。。。,销毁时间为:" + event.getTimestamp()); } } }复制代码
2.3 事件发布器
先看下类图:
ApplicationEventMulticaster是发布事件的父类接口,主要定义了增加,删除,获取等操作事件监听器的的方法接口,此外,还定义了一个发布事件的方法。
SimpleApplicationEventMulticaster是ApplicationEventMulticaster事件发布器接口的默认实现类,主要承担发布事件的功能。其内部维护了一个事件监听器列表contextListeners,当发布事件时会遍历这些列表,然后再向每个监听器发布事件,通过设置async属性来决定同步广播事件还是异步广播事件。
下面看看实现代码:
public interface ApplicationEventMulticaster { void addContextListener(ContextListener<?> listener); void removeContextListener(ContextListener<?> listener); void removeAllListeners(); void multicastEvent(AbstractContextEvent event); } public class SimpleApplicationEventMulticaster implements ApplicationEventMulticaster { // 是否异步发布事件 private boolean async = false; // 线程池 private Executor taskExecutor = new ThreadPoolExecutor(5, 5, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()); // 事件监听器列表 private List<ContextListener<?>> contextListeners = new ArrayList<ContextListener<?>>(); public void addContextListener(ContextListener<?> listener) { contextListeners.add(listener); } public void removeContextListener(ContextListener<?> listener) { contextListeners.remove(listener); } public void removeAllListeners() { contextListeners.clear(); } public void multicastEvent(AbstractContextEvent event) { doMulticastEvent(contextListeners, event); } private void doMulticastEvent(List<ContextListener<?>> contextListeners, AbstractContextEvent event) { for (ContextListener contextListener : contextListeners) { // 异步广播事件 if (async) { taskExecutor.execute(() -> invokeListener(contextListener, event)); // new Thread(() -> invokeListener(contextListener, event)).start(); // 同步发布事件,阻塞的方式 } else { invokeListener(contextListener, event); } } } private void invokeListener(ContextListener contextListener, AbstractContextEvent event) { contextListener.onApplicationEvent(event); } public void setAsync(boolean async) { this.async = async; } } 复制代码
2.4 测试自定义的容器生命周期事件
那么直接上测试代码,下面只演示同步发布事件的功能:
public class MockSpringEventTest { @Test public void testContextLifecycleEventInSync() { // 新建SimpleApplicationEventMulticaster对象,并添加容器生命周期监听器 ApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster(); eventMulticaster.addContextListener(new ContextStartEventListener()); eventMulticaster.addContextListener(new ContextRunningEventListener()); eventMulticaster.addContextListener(new ContextDestroyEventListener()); // 发射容器启动事件ContextStartEvent eventMulticaster.multicastEvent(new ContextStartEvent()); // 发射容器正在运行事件ContextRunningEvent eventMulticaster.multicastEvent(new ContextRunningEvent()); // 发射容器正在运行事件ContextDestroyEvent eventMulticaster.multicastEvent(new ContextDestroyEvent()); } @Test public void testContextLifecycleEventInAsync() throws InterruptedException { // 新建SimpleApplicationEventMulticaster对象,并添加容器生命周期监听器 ApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster(); eventMulticaster.addContextListener(new ContextStartEventListener()); eventMulticaster.addContextListener(new ContextRunningEventListener()); eventMulticaster.addContextListener(new ContextDestroyEventListener()); ((SimpleApplicationEventMulticaster) eventMulticaster).setAsync(true); // 发射容器启动事件ContextStartEvent eventMulticaster.multicastEvent(new ContextStartEvent()); // 发射容器正在运行事件ContextRunningEvent eventMulticaster.multicastEvent(new ContextRunningEvent()); // 发射容器正在运行事件ContextDestroyEvent eventMulticaster.multicastEvent(new ContextDestroyEvent()); // 这里没明白在没有用CountDownLatch的情况下为何主线程退出,非后台线程的子线程也会退出???为了测试,所有先用CountDownLatch锁住main线程先 // 经过测试,原来是因为用了junit的方法,test方法线程退出后,test方法线程产生的非后台线程也随之退出,而下面的main方法启动的非后台线程则不会 // TODO 这是为什么呢???难道是A子线程(非main线程)启动的B子线程会随着A子线程退出而退出?还没验证 CountDownLatch countDownLatch = new CountDownLatch(1); countDownLatch.await(); } public static void main(String[] args) throws InterruptedException { // 新建SimpleApplicationEventMulticaster对象,并添加容器生命周期监听器 ApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster(); eventMulticaster.addContextListener(new ContextStartEventListener()); eventMulticaster.addContextListener(new ContextRunningEventListener()); eventMulticaster.addContextListener(new ContextDestroyEventListener()); ((SimpleApplicationEventMulticaster) eventMulticaster).setAsync(true); // 发射容器启动事件ContextStartEvent eventMulticaster.multicastEvent(new ContextStartEvent()); // 发射容器正在运行事件ContextRunningEvent eventMulticaster.multicastEvent(new ContextRunningEvent()); // 发射容器正在运行事件ContextDestroyEvent eventMulticaster.multicastEvent(new ContextDestroyEvent()); } } 复制代码
通过运行测试方法testContextLifecycleEventInSync(),运行结果如下截图:
3,结语
好了,自定义事件驱动编程的简单demo就已经实现了。
这只是spring事件机制源码分析的前置文章,真正的源码分析请见下一篇博文:
Spring事件相关类关系源码解析--Spring的事件机制源码分析(二)
这篇关于模仿Spring事件机制实现自定义事件驱动编程--Spring的事件机制源码分析(一)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-05-09“2024鸿蒙零基础快速实战-仿抖音App开发(ArkTS版)”实战课程已上线
- 2024-05-09聊聊如何通过arthas-tunnel-server来远程管理所有需要arthas监控的应用
- 2024-05-09log4j2这么配就对了
- 2024-05-09nginx修改Content-Type
- 2024-05-09Redis多数据源,看这篇就够了
- 2024-05-09Google Chrome驱动程序 124.0.6367.62(正式版本)去哪下载?
- 2024-05-09有没有大佬知道这种数据应该怎么抓取呀?
- 2024-05-09这种运行结果里的10.100000001,怎么能最快改成10.1?
- 2024-05-09企业src漏洞挖掘-有意思的命令执行
- 2024-05-08阿里云域名注册流程,分享给第一次购买域名的新手站长!