|
1 |
| -公众号【源码笔记】,专注于Java后端系列框架的源码分析。 |
2 |
| - |
| 1 | +**注意:spring源码分析文章对应spring版本为 5.1.x** |
| 2 | + |
| 3 | +# 1,概述 |
| 4 | + |
| 5 | +要想理解spring的事件机制,我觉得首先自己动手去撸一套简单的自定义事件驱动编程demo还是非常有必要滴,因为这样有助于理解spring事件机制。当然,这里也是模仿spring的事件机制的代码,不过下面看代码实现时可以先抛开spring的事件机制相关代码,将注意力集中到这个简单demo上即可。 |
| 6 | + |
| 7 | +在看这个自定义事件驱动编程时,首先要熟悉观察者设计模式,因为事件驱动编程可以说是观察者(发布订阅)模式的具体实现。推荐我的另一篇翻译的博文:[观察者模式--设计模式(一)](https://blog.csdn.net/biaolianlao0449/article/details/104246763),有需要的小伙伴们可以先学习下哈。 |
| 8 | + |
| 9 | +下面正式开始手撸代码实现,首先先放上下面代码的github地址: |
| 10 | + |
| 11 | +https://github.com/jinyue233/java-demo/tree/master/spring5-demo/src/main/java/com/jinyue/spring/event/mockspringevent |
| 12 | + |
| 13 | +# 2,自定义事件驱动编程 |
| 14 | + |
| 15 | +因为这篇文章是spring事件机制的前置文章,因此这里自定义实现一个模拟容器(可以理解为spring容器,servltet容器等)的生命周期事件的简单demo。 |
| 16 | + |
| 17 | +## 2.1 事件 |
| 18 | + |
| 19 | +先来看一下事件的整体架构图,让大家先有对事件有一个整体的认识,如下图: |
| 20 | + |
| 21 | + |
| 22 | + |
| 23 | +**1,Event接口** |
| 24 | + |
| 25 | +面向接口编程,首先先定义一个Event接口,该接口没有任何方法,可以说是事件的标志接口。 |
| 26 | + |
| 27 | +``` |
| 28 | +public interface Event extends Serializable { |
| 29 | +} |
| 30 | +``` |
| 31 | + |
| 32 | + |
| 33 | + |
| 34 | +**2,AbstractContextEvent** |
| 35 | + |
| 36 | +AbstractContextEvent是容器事件的基本抽象类,因为事件也可以携带数据,因此这里定义了一个timestamp属性,用来记录事件的发生时间。 |
| 37 | + |
| 38 | +``` |
| 39 | +public class AbstractContextEvent implements Event { |
| 40 | + private static final long serialVersionUID = -6159391039546783871L; |
| 41 | +
|
| 42 | + private final long timestamp = System.currentTimeMillis(); |
| 43 | +
|
| 44 | + public final long getTimestamp() { |
| 45 | + return this.timestamp; |
| 46 | + } |
| 47 | +} |
| 48 | +``` |
| 49 | + |
| 50 | + |
| 51 | + |
| 52 | +**3,ContextStartEvent** |
| 53 | + |
| 54 | +ContextStartEvent事件是AbstractContextEvent具体实现类,容器开始启动时触发,这里为了demo简单,这里不再定义任何事件逻辑,只是代表容器启动时的一个标志事件类。 |
| 55 | + |
| 56 | +``` |
| 57 | +public class ContextStartEvent extends AbstractContextEvent { |
| 58 | +} |
| 59 | +``` |
| 60 | + |
| 61 | + |
| 62 | + |
| 63 | + |
| 64 | + |
| 65 | +**4,ContextRunningEvent** |
| 66 | + |
| 67 | + |
| 68 | + |
| 69 | +ContextRunningEvent事件是AbstractContextEvent具体实现类,容器启动后时触发,这里为了demo简单,这里不再定义任何事件逻辑,只是代表容器启动运行时时的一个标志事件类。 |
| 70 | + |
| 71 | +``` |
| 72 | +public class ContextRunningEvent extends AbstractContextEvent { |
| 73 | +} |
| 74 | +``` |
| 75 | + |
| 76 | + |
| 77 | + |
| 78 | +**5,ContextDestroyEvent** |
| 79 | + |
| 80 | + |
| 81 | + |
| 82 | +ContextDestroyEvent事件是AbstractContextEvent具体实现类,容器销毁时触发,这里为了demo简单,这里不再定义任何事件逻辑,只是代表容器销毁时的一个标志事件类。 |
| 83 | + |
| 84 | +``` |
| 85 | +public class ContextDestroyEvent extends AbstractContextEvent { |
| 86 | +} |
| 87 | +``` |
| 88 | + |
| 89 | + |
| 90 | + |
| 91 | +## 2.2 事件监听器 |
| 92 | + |
| 93 | +先来看一下事件监听器的整体架构图,让大家先有对事件监听器有一个整体的认识,如下图: |
| 94 | + |
| 95 | + |
| 96 | + |
| 97 | +其中EventListener是所有事件监听器的基类接口,,是事件监听器的标志类接口,被所有具体的事件监听器实现。然后ContextListener接口是容器事件监听器接口,继承了EventListener,主要定义了如下事件监听方法: |
| 98 | + |
| 99 | +``` |
| 100 | + void onApplicationEvent(T event); |
| 101 | +``` |
| 102 | + |
| 103 | + |
| 104 | + |
| 105 | +然后ContextListener接口被三个具体的容器生命周期事件监听器实现,分别是ContextStartEventListener(监听容器启动时的ContextStartEvent),ContextRunningEventListener(监听容器启动后运行时的ContextRunningEvent)和ContextDestroyEventListener(监听容器销毁时的ContextDestroyEvent)。 |
| 106 | + |
| 107 | +下面看具体的代码实现: |
| 108 | + |
| 109 | +``` |
| 110 | +public interface EventListener { |
| 111 | +} |
| 112 | +
|
| 113 | +
|
| 114 | +public interface ContextListener<T extends AbstractContextEvent> extends EventListener { |
| 115 | + /** |
| 116 | + * Handle an application event. |
| 117 | + * @param event the event to respond to |
| 118 | + */ |
| 119 | + void onApplicationEvent(T event); |
| 120 | +} |
| 121 | +
|
| 122 | +
|
| 123 | +public class ContextStartEventListener implements ContextListener<AbstractContextEvent> { |
| 124 | + /** |
| 125 | + * Handle an application event. |
| 126 | + * |
| 127 | + * @param event the event to respond to |
| 128 | + */ |
| 129 | + public void onApplicationEvent(AbstractContextEvent event) { |
| 130 | + if (event instanceof ContextStartEvent) { |
| 131 | + System.out.println("容器启动。。。,启动时间为:" + event.getTimestamp()); |
| 132 | + } |
| 133 | + } |
| 134 | +} |
| 135 | +
|
| 136 | +public class ContextRunningEventListener implements ContextListener<AbstractContextEvent> { |
| 137 | + /** |
| 138 | + * Handle an application event. |
| 139 | + * |
| 140 | + * @param event the event to respond to |
| 141 | + */ |
| 142 | + public void onApplicationEvent(AbstractContextEvent event) { |
| 143 | + if (event instanceof ContextRunningEvent) { |
| 144 | + System.out.println("容器开始运行。。。"); |
| 145 | + try { |
| 146 | + Thread.sleep(3000); |
| 147 | + System.out.println("容器运行结束。。。"); |
| 148 | + } catch (InterruptedException e) { |
| 149 | + e.printStackTrace(); |
| 150 | + } |
| 151 | + } |
| 152 | + } |
| 153 | +} |
| 154 | +
|
| 155 | +public class ContextDestroyEventListener implements ContextListener<AbstractContextEvent> { |
| 156 | + /** |
| 157 | + * Handle an application event. |
| 158 | + * |
| 159 | + * @param event the event to respond to |
| 160 | + */ |
| 161 | + public void onApplicationEvent(AbstractContextEvent event) { |
| 162 | + if (event instanceof ContextDestroyEvent) { |
| 163 | + System.out.println("容器销毁。。。,销毁时间为:" + event.getTimestamp()); |
| 164 | + } |
| 165 | + } |
| 166 | +} |
| 167 | +``` |
| 168 | + |
| 169 | + |
| 170 | + |
| 171 | +## 2.3 事件发布器 |
| 172 | + |
| 173 | +先看下类图: |
| 174 | + |
| 175 | + |
| 176 | + |
| 177 | +ApplicationEventMulticaster是发布事件的父类接口,主要定义了增加,删除,获取等操作事件监听器的的方法接口,此外,还定义了一个发布事件的方法。 |
| 178 | + |
| 179 | +SimpleApplicationEventMulticaster是ApplicationEventMulticaster事件发布器接口的默认实现类,主要承担发布事件的功能。其内部维护了一个事件监听器列表contextListeners,当发布事件时会遍历这些列表,然后再向每个监听器发布事件,通过设置async属性来决定同步广播事件还是异步广播事件。 |
| 180 | + |
| 181 | +下面看看实现代码: |
| 182 | + |
| 183 | +``` |
| 184 | +public interface ApplicationEventMulticaster { |
| 185 | + void addContextListener(ContextListener<?> listener); |
| 186 | +
|
| 187 | + void removeContextListener(ContextListener<?> listener); |
| 188 | +
|
| 189 | + void removeAllListeners(); |
| 190 | +
|
| 191 | + void multicastEvent(AbstractContextEvent event); |
| 192 | +
|
| 193 | +} |
| 194 | +
|
| 195 | +
|
| 196 | +
|
| 197 | +public class SimpleApplicationEventMulticaster implements ApplicationEventMulticaster { |
| 198 | + // 是否异步发布事件 |
| 199 | + private boolean async = false; |
| 200 | + // 线程池 |
| 201 | + private Executor taskExecutor = new ThreadPoolExecutor(5, 5, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()); |
| 202 | + // 事件监听器列表 |
| 203 | + private List<ContextListener<?>> contextListeners = new ArrayList<ContextListener<?>>(); |
| 204 | +
|
| 205 | + |
| 206 | + public void addContextListener(ContextListener<?> listener) { |
| 207 | + contextListeners.add(listener); |
| 208 | + } |
| 209 | +
|
| 210 | + public void removeContextListener(ContextListener<?> listener) { |
| 211 | + contextListeners.remove(listener); |
| 212 | + } |
| 213 | +
|
| 214 | + public void removeAllListeners() { |
| 215 | + contextListeners.clear(); |
| 216 | + } |
| 217 | +
|
| 218 | + public void multicastEvent(AbstractContextEvent event) { |
| 219 | + doMulticastEvent(contextListeners, event); |
| 220 | + } |
| 221 | +
|
| 222 | + private void doMulticastEvent(List<ContextListener<?>> contextListeners, AbstractContextEvent event) { |
| 223 | + for (ContextListener contextListener : contextListeners) { |
| 224 | + // 异步广播事件 |
| 225 | + if (async) { |
| 226 | + taskExecutor.execute(() -> invokeListener(contextListener, event)); |
| 227 | + // new Thread(() -> invokeListener(contextListener, event)).start(); |
| 228 | + // 同步发布事件,阻塞的方式 |
| 229 | + } else { |
| 230 | + invokeListener(contextListener, event); |
| 231 | + } |
| 232 | + } |
| 233 | + } |
| 234 | +
|
| 235 | + private void invokeListener(ContextListener contextListener, AbstractContextEvent event) { |
| 236 | + contextListener.onApplicationEvent(event); |
| 237 | + } |
| 238 | +
|
| 239 | + public void setAsync(boolean async) { |
| 240 | + this.async = async; |
| 241 | + } |
| 242 | +} |
| 243 | +``` |
| 244 | + |
| 245 | + |
| 246 | + |
| 247 | +## 2.4 测试自定义的容器生命周期事件 |
| 248 | + |
| 249 | +那么直接上测试代码,下面只演示同步发布事件的功能: |
| 250 | + |
| 251 | +``` |
| 252 | +public class MockSpringEventTest { |
| 253 | +
|
| 254 | + @Test |
| 255 | + public void testContextLifecycleEventInSync() { |
| 256 | + // 新建SimpleApplicationEventMulticaster对象,并添加容器生命周期监听器 |
| 257 | + ApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster(); |
| 258 | + eventMulticaster.addContextListener(new ContextStartEventListener()); |
| 259 | + eventMulticaster.addContextListener(new ContextRunningEventListener()); |
| 260 | + eventMulticaster.addContextListener(new ContextDestroyEventListener()); |
| 261 | + // 发射容器启动事件ContextStartEvent |
| 262 | + eventMulticaster.multicastEvent(new ContextStartEvent()); |
| 263 | + // 发射容器正在运行事件ContextRunningEvent |
| 264 | + eventMulticaster.multicastEvent(new ContextRunningEvent()); |
| 265 | + // 发射容器正在运行事件ContextDestroyEvent |
| 266 | + eventMulticaster.multicastEvent(new ContextDestroyEvent()); |
| 267 | + } |
| 268 | +
|
| 269 | + @Test |
| 270 | + public void testContextLifecycleEventInAsync() throws InterruptedException { |
| 271 | + // 新建SimpleApplicationEventMulticaster对象,并添加容器生命周期监听器 |
| 272 | + ApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster(); |
| 273 | + eventMulticaster.addContextListener(new ContextStartEventListener()); |
| 274 | + eventMulticaster.addContextListener(new ContextRunningEventListener()); |
| 275 | + eventMulticaster.addContextListener(new ContextDestroyEventListener()); |
| 276 | +
|
| 277 | + ((SimpleApplicationEventMulticaster) eventMulticaster).setAsync(true); |
| 278 | +
|
| 279 | + // 发射容器启动事件ContextStartEvent |
| 280 | + eventMulticaster.multicastEvent(new ContextStartEvent()); |
| 281 | + // 发射容器正在运行事件ContextRunningEvent |
| 282 | + eventMulticaster.multicastEvent(new ContextRunningEvent()); |
| 283 | + // 发射容器正在运行事件ContextDestroyEvent |
| 284 | + eventMulticaster.multicastEvent(new ContextDestroyEvent()); |
| 285 | + // 这里没明白在没有用CountDownLatch的情况下为何主线程退出,非后台线程的子线程也会退出???为了测试,所有先用CountDownLatch锁住main线程先 |
| 286 | + // 经过测试,原来是因为用了junit的方法,test方法线程退出后,test方法线程产生的非后台线程也随之退出,而下面的main方法启动的非后台线程则不会 |
| 287 | + // TODO 这是为什么呢???难道是A子线程(非main线程)启动的B子线程会随着A子线程退出而退出?还没验证 |
| 288 | + CountDownLatch countDownLatch = new CountDownLatch(1); |
| 289 | + countDownLatch.await(); |
| 290 | +
|
| 291 | + } |
| 292 | +
|
| 293 | + public static void main(String[] args) throws InterruptedException { |
| 294 | + // 新建SimpleApplicationEventMulticaster对象,并添加容器生命周期监听器 |
| 295 | + ApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster(); |
| 296 | + eventMulticaster.addContextListener(new ContextStartEventListener()); |
| 297 | + eventMulticaster.addContextListener(new ContextRunningEventListener()); |
| 298 | + eventMulticaster.addContextListener(new ContextDestroyEventListener()); |
| 299 | +
|
| 300 | + ((SimpleApplicationEventMulticaster) eventMulticaster).setAsync(true); |
| 301 | +
|
| 302 | + // 发射容器启动事件ContextStartEvent |
| 303 | + eventMulticaster.multicastEvent(new ContextStartEvent()); |
| 304 | + // 发射容器正在运行事件ContextRunningEvent |
| 305 | + eventMulticaster.multicastEvent(new ContextRunningEvent()); |
| 306 | + // 发射容器正在运行事件ContextDestroyEvent |
| 307 | + eventMulticaster.multicastEvent(new ContextDestroyEvent()); |
| 308 | +
|
| 309 | + } |
| 310 | +} |
| 311 | +``` |
| 312 | + |
| 313 | + |
| 314 | + |
| 315 | +通过运行测试方法testContextLifecycleEventInSync(),运行结果如下截图: |
| 316 | + |
| 317 | + |
| 318 | + |
| 319 | +# 3,结语 |
| 320 | + |
| 321 | +好了,自定义事件驱动编程的简单demo就已经实现了。 |
| 322 | + |
| 323 | +这只是spring事件机制源码分析的前置文章,真正的源码分析请见下一篇博文: |
| 324 | + |
| 325 | +**[Spring事件相关类关系源码解析--Spring的事件机制源码分析(二](https://blog.csdn.net/biaolianlao0449/article/details/104246732)**) |
| 326 | + |
| 327 | +**原创不易,帮忙点个赞呗。** |
| 328 | + |
| 329 | +\---------------------------------------------------------------------- |
| 330 | + |
| 331 | +微信公众号:**源码笔记** |
| 332 | +探讨更多源码知识,关注“源码笔记”微信公众号,每周持续推出SpringBoot,Spring,Mybatis,Dubbo,RocketMQ,Jdk 和Netty等源码系列文章。 |
| 333 | + |
| 334 | + |
0 commit comments