前言
需求:当新用户注册时,需要给用户发放各种礼品、积分、短信、邀请人奖励等。
常见写法
直接将上述后续操作堆到注册方法里,搞的注册方法又臭又长;当有其他注册如app注册、小程序注册、第三方注册时,然后将同样的发放逻辑复制的到处都是,可读性、维护性极差
优化:可以将发放逻辑抽成一个方法,然后供调用,这个应该是最常用的;
但随着业务的扩张,可能不止普通用户、还有承包商、供应商等。可能都已经是不同的表了,他们也有不同的发放逻辑。
然后发放逻辑建好几个方法,将之前的逻辑复制过去改一改;最后这个发放逻辑就会变得很臃肿难以维护。
再优化:可以套用事件驱动模式肢解该逻辑,当然也可以不使用。一切都要从业务出发,业务不复杂就完全没必要使用。因为不管用到什么设计模式都会建好多个类,虽然在单个类里维护性很强,但找起来可能不方便、整体使用还是不如直接调用方便。
设计模式优势
- 一般来说使用设计模式可以提高代码的三性:
- 提高代码的可重用性
- 提高代码的可读性
- 提高代码的可靠性
事件驱动模式处理
事件源:用户
事件:用户注册
事件监听器:监听到用户注册时调用奖励发放功能
步骤
1. 建UserRegisterEvent类继承ApplicationEvent
1 | /** |
2. 用户注册事件发布
1 | /** |
3. 用户注册事件监听
1 | /** |
4. 测试执行
1 | /** |
5. 当有其他监听者时,如邀请人奖励发放
1 | /** |
监听同一事件对象,业务代码相对解耦
原理
点这个方法进去applicationEventPublisher.publishEvent
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
// Decorate event as an ApplicationEvent if necessary
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
}
}
// Multicast right now if possible - or lazily once the multicaster is initialized
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
// !! 主要看这里面 !!
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
// Publish event via parent context as well...
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}获取事件的监听器执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
// 如果初始化异步任务,就会异步执行每个监听器,否则就是同步执行
Executor executor = getTaskExecutor();
// 此处根据事件对象获取它的所有监听器
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}执行UserRegisterEventListener实现的接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21"rawtypes", "unchecked"}) ({
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
// 最后就是这里去执行UserRegisterEventListener实现的接口
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
String msg = ex.getMessage();
if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
// Possibly a lambda-defined listener which we could not resolve the generic event type for
// -> let's suppress the exception and just log a debug message.
Log logger = LogFactory.getLog(getClass());
if (logger.isTraceEnabled()) {
logger.trace("Non-matching event type for listener: " + listener, ex);
}
}
else {
throw ex;
}
}
}
异步
1. 怎么异步使用监听器
可以自己注入一个SimpleApplicationEventMulticaster类,给里面设置上线程即可
1 | /** |
总结
优点
- 从以上步骤中可以看到事件对象、事件发布、事件监听器代码相对解耦
- 当有新的业务时如邀请人事件监听,只需要添加一个事件监听器即可,无需修改原有代码。减少对现有业务的影响,相对扩展性较好
- 可以更容易开发和维护不可预知的服务或异步服务
缺点
- 同一事件的监听器尽量放在同一目录,有可能会出现同一事件的不同监听器散落在不同的模块中,增加业务逻辑的复杂度