Skip to content

Commit 9da0e31

Browse files
committed
initial commit。
1 parent 944a914 commit 9da0e31

3 files changed

+861
-4
lines changed

README.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,17 @@
1-
公众号【源码笔记】,专注于Java后端系列框架的源码分析。
2-
![](https://user-gold-cdn.xitu.io/2020/3/15/170dd9bb2b5b59de?w=142&h=135&f=png&s=39743)
1+
微信公众号【**源码笔记**】,专注于Java后端系列框架的源码分析。每周持续推出SpringBoot,Spring,Mybatis,Dubbo,RocketMQ,Jdk 和Netty等Java后端系列框架的源码分析文章。
2+
3+
4+
5+
**公众号**
6+
7+
![img](https://common-ymbj.oss-cn-beijing.aliyuncs.com/%E6%BA%90%E7%A0%81%E7%AC%94%E8%AE%B0%E5%85%AC%E4%BC%97%E5%8F%B7%E4%BA%8C%E7%BB%B4%E7%A0%81.PNG)
8+
9+
**联系我**
10+
11+
######
12+
13+
14+
15+
16+
17+
![img](https://common-ymbj.oss-cn-beijing.aliyuncs.com/%E7%88%B1%E7%BC%96%E7%A0%81%E7%9A%84%E7%A0%81%E5%86%9C%E4%BA%8C%E7%BB%B4%E7%A0%81.PNG)
Lines changed: 334 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,334 @@
1-
公众号【源码笔记】,专注于Java后端系列框架的源码分析。
2-
![](https://user-gold-cdn.xitu.io/2020/3/15/170dd9bb2b5b59de?w=142&h=135&f=png&s=39743)
1+
![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)**注意: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+
![img](https://user-gold-cdn.xitu.io/2020/2/11/1703314099225c59?w=637&h=332&f=png&s=24496)![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)
22+
23+
**1,Event接口**
24+
25+
面向接口编程,首先先定义一个Event接口,该接口没有任何方法,可以说是事件的标志接口。
26+
27+
```
28+
public interface Event extends Serializable {
29+
}
30+
```
31+
32+
![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)
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+
![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)
51+
52+
**3,ContextStartEvent**
53+
54+
ContextStartEvent事件是AbstractContextEvent具体实现类,容器开始启动时触发,这里为了demo简单,这里不再定义任何事件逻辑,只是代表容器启动时的一个标志事件类。
55+
56+
```
57+
public class ContextStartEvent extends AbstractContextEvent {
58+
}
59+
```
60+
61+
![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)
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+
![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)
77+
78+
**5,ContextDestroyEvent**
79+
80+
81+
82+
ContextDestroyEvent事件是AbstractContextEvent具体实现类,容器销毁时触发,这里为了demo简单,这里不再定义任何事件逻辑,只是代表容器销毁时的一个标志事件类。
83+
84+
```
85+
public class ContextDestroyEvent extends AbstractContextEvent {
86+
}
87+
```
88+
89+
![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)
90+
91+
## 2.2 事件监听器
92+
93+
先来看一下事件监听器的整体架构图,让大家先有对事件监听器有一个整体的认识,如下图:
94+
95+
![img](https://user-gold-cdn.xitu.io/2020/2/11/1703314096a6a5e1?w=758&h=289&f=png&s=25133)![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)
96+
97+
其中EventListener是所有事件监听器的基类接口,,是事件监听器的标志类接口,被所有具体的事件监听器实现。然后ContextListener接口是容器事件监听器接口,继承了EventListener,主要定义了如下事件监听方法:
98+
99+
```
100+
void onApplicationEvent(T event);
101+
```
102+
103+
![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)
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+
![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)
170+
171+
## 2.3 事件发布器
172+
173+
先看下类图:
174+
175+
![img](https://user-gold-cdn.xitu.io/2020/2/11/1703314099136a60?w=538&h=63&f=png&s=7799)![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)
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+
![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)
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+
![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)
314+
315+
通过运行测试方法testContextLifecycleEventInSync(),运行结果如下截图:
316+
317+
![img](https://user-gold-cdn.xitu.io/2020/2/11/1703314099333a6b?w=621&h=128&f=png&s=12253)![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)
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+
![img](https://user-gold-cdn.xitu.io/2020/2/15/17046e9b1cf0506e?w=258&h=258&f=jpeg&s=26882)

0 commit comments

Comments
 (0)