有这么一个需求,客户注册的时候,产品经理要求给客户发送短信,发送优惠券,还有就是发送积分。根据xp极限编程原则,只管今天不管明天,伪代码原则上
//1,注册
register();
//2,发送优惠券
sendCoupon();
//3,发送短信
sendSMS();
//4,发送积分
sendIntegral();
看得出来,这个代码逻辑一点问题都没有,但是
产品经理说:喂,最近公司效益不好,积分和优惠券就不要发送了。
于是,你又可能去注释掉发送优惠券和积分的代码,这没什么问题,然后组织测试人员在测试一波,简单就可以上线。
于是又过了几天。
产品经理又说:喂!最近效益不好,需要刺激用户消费,注册的时候继续发送优惠券和积分。
你说: 好的!
于是你又把注释的代码放开!!感觉so easy!
于是又过了几天。
产品说:喂,注册的功能很慢,而且经常会失败,你知道这会让很多客户流失,给公司造成很大损失。
然后你带着问题,去查询日志,最后发现是调用第三方短信服务特别慢,有时候还有失败,然后你就理直气壮的耍锅。
你说:经理,这个跟我写的代码没有关系,是因为第三方短信平台不稳定,日志都复制给你了,您瞧瞧!!
产品经理撇了你一眼。
说:短信发不发的无所谓,核心注册一定要成功!!
这个时候,你应该感觉到,这个注册逻辑变来变去,而且产品的需求也是合情合理,所以本着事不过三,三则重构的原则。前面简单的代码堆砌方式已经不能满足我们的需求变化了,所以我们要想怎么样优化自己的代码。
我们的代码逻辑没什么问题,矛盾在于,我们的主干逻辑和一些次要逻辑耦合在一起。使得主干逻辑一直没有变,次要的功能确实频繁的变化,这个时候,我们学习的监听者模式就派上用场了,然后主干逻辑和次要功能解耦。
我们这可以这样做,有四个人:注册器,工具人A,工具人B,工具人C,注册器负责主要逻辑,工具人A负责发送优惠券,工具人B负责发送积分,工具人C负责发送短信,当我的注册器有用户注册的时候,就广播给其他的工具人,让他们各司其职,该干嘛就干嘛。这个时候,我们发现,我们的注册逻辑主要和广播器耦合,负责帮我们广播信息就可以,至于具体工具人做什么事情,他是不知道的。而我们频繁去修改次要功能的时候,也不需要去修改我们的主干逻辑部分的代码。
事件模式中的几个概念
事件源:事件的触发者,也就是上面的注册器。
事件:描述一个动作,这里就是我们可以理解为注册这个动作。
事件监听器:监听到事件后,做的一些处理,就是上面的工具人。
事件广播器:负责广播信息给监听者。
下面我们使用监听者模式实现用户注册的业务
我们先来定义和事件相关的几个类
事件对象
表示所有事件的父类,内部有个source字段,表示事件源;我们自定义的事件需要继承这个类
package com.shiguiwu.springmybatis.spring.event.pattern;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* @description: 事件对象
* @author: stone
* @date: Created by 2021/4/8 10:21
* @version: 1.0.0
* @pakeage: com.shiguiwu.springmybatis.spring.event.pattern
*/
@Data
@AllArgsConstructor
public abstract class AbstractEvent {
//事件源
//事件源:事件的触发者,比如上面的注册器就是事件源。
private Object source;
}
事件监听器
我们使用一个接口来表示事件监听器,是个泛型接口,后面的类型 E 表示当前监听器需要监听的事件类型,此接口中只有一个方法,用来实现处理事件的业务;其定义的监听器需要实现这个接口。
/**
* @description: 事件监听
*
* :监听到事件发生的时候,做一些处理,比如上面的:路人A、路人B
* @author: stone
* @date: Created by 2021/4/8 10:29
* @version: 1.0.0
* @pakeage: com.shiguiwu.springmybatis.spring.event.pattern
*/
public interface EventListener<E extends AbstractEvent> {
/**
* 此方法负责处理事件
* @param e 事件对象
*/
public void onEvent(E e);
}
事件广播器
- 负责事件监听器的管理(注册监听器&移除监听器,将事件和监听器关联起来)
- 负责事件的广播(将事件广播给所有的监听器,对该事件感兴趣的监听器会处理该事件)
/**
* @description: 事件广播
* @author: stone
* @date: Created by 2021/4/8 10:36
* @version: 1.0.0
* @pakeage: com.shiguiwu.springmybatis.spring.event.pattern
*
**事件广播器:
**1.负责事件监听器的管理(注册监听器&移除监听器,将事件和监听器关联起来)
**2.负责事件的广播(将事件广播给所有的监听器,对该事件感兴趣的监听器会处理该事件)
**/
public interface EventMulticaster {
/**
* 广播事件所有的监听器,对该事件感兴趣的监听会处理该事件
* @param event
*/
public void multicastEvent(AbstractEvent event);
/**
* 添加一个事件监听器
* @param eventListener
*/
public void addEventListener(EventListener<?> eventListener);
/**
* 将一个监听器移除
* @param eventListener
*/
public void removeEventListener(EventListener<?> eventListener);
广播器的简单实现
/**
* @description: 事件广播
* @author: stone
* @date: Created by 2021/4/8 10:36
* @version: 1.0.0
* @pakeage: com.shiguiwu.springmybatis.spring.event.pattern
*
**事件广播器:
**1.负责事件监听器的管理(注册监听器&移除监听器,将事件和监听器关联起来)
**2.负责事件的广播(将事件广播给所有的监听器,对该事件感兴趣的监听器会处理该事件)
**/
public interface EventMulticaster {
/**
* 广播事件所有的监听器,对该事件感兴趣的监听会处理该事件
* @param event
*/
public void multicastEvent(AbstractEvent event);
/**
* 添加一个事件监听器
* @param eventListener
*/
public void addEventListener(EventListener<?> eventListener);
/**
* 将一个监听器移除
* @param eventListener
*/
public void removeEventListener(EventListener<?> eventListener);
广播器的简单实现
/**
* @description: 简单事件广播
* @author: stone
* @date: Created by 2021/4/8 11:04
* @version: 1.0.0
* @pakeage: com.shiguiwu.springmybatis.spring.event
*/
public class SimpleEventMulticaster implements EventMulticaster {
private Map<Class<?>, List<EventListener>> eventObjectEventListenerMap = new ConcurrentHashMap<>();
@Override
public void multicastEvent(AbstractEvent event) {
List<EventListener> eventListeners = eventObjectEventListenerMap.get(event.getClass());
if (eventListeners != null) {
eventListeners.parallelStream().forEach(e -> e.onEvent(event));
}
}
@Override
public void addEventListener(EventListener<?> eventListener) {
Class<?> eventType = this.getEventType(eventListener);
List<EventListener> listeners = this.eventObjectEventListenerMap.computeIfAbsent(eventType, e -> new ArrayList<>());
listeners.add(eventListener);
}
@Override
public void removeEventListener(EventListener<?> eventListener) {
Class<?> eventType = this.getEventType(eventListener);
List<EventListener> eventListeners = this.eventObjectEventListenerMap.get(eventType);
if (eventListeners != null) {
eventListeners.remove(eventListener);
}
}
/**
* 获取事件的类型,这里的代码可能不是很常见,其实就是获取泛型类型而已
* @param eventListener
* @return
*/
protected Class<?> getEventType(EventListener<? extends AbstractEvent> eventListener) {
ParameterizedType parameterizedType = (ParameterizedType) eventListener.getClass().getGenericInterfaces()[0];
Type actualTypeArgument = parameterizedType.getActualTypeArguments()[0];
return (Class<?>) actualTypeArgument;
}
}
上面3个类支撑了整个时间模型,下面我们使用上面三个类来实现注册的功能,目标是:高内聚低耦合,让注册逻辑方便扩展。
自定义用户注册成功事件类
继承了 AbstractEvent 类
/**
* @description: 用户注册事件
* @author: stone
* @date: Created by 2021/4/8 11:56
* @version: 1.0.0
* @pakeage: com.shiguiwu.springmybatis.spring.event
*/
@Getter
public class RegisterSuccessEvent extends AbstractEvent {
private String username;
public RegisterSuccessEvent(Object source, String username) {
super(source);
this.username = username;
}
}
用户注册服务
负责实现用户注册逻辑
/**
* @description: 用户注册服务
* @author: stone
* @date: Created by 2021/4/8 14:14
* @version: 1.0.0
* @pakeage: com.shiguiwu.springmybatis.spring.event
*/
@Data
public class RegisterService {
private EventMulticaster eventMulticaster;
public void register(String username) {
//用户注册,将数据写人到数据库中
System.out.println("用户注册成功。。。。" + username);
//事件广播
//使用事件发布者eventPublisher发布用户注册成功的消息:
this.eventMulticaster.multicastEvent(new RegisterSuccessEvent(this, username));
}
}
自定义监听者
发送优惠券
/**
* @description: 注册成功后发优惠券
* @author: stone
* @date: Created by 2021/4/8 15:28
* @version: 1.0.0
* @pakeage: com.shiguiwu.springmybatis.spring.event
*/
public class GetCouponRegisterSuccessListener implements EventListener<RegisterSuccessEvent> {
@Override
public void onEvent(RegisterSuccessEvent event) {
System.out.println(event.getUsername() + "注册成功,赠送优惠券。。。。。");
}
}
发短信
package com.shiguiwu.springmybatis.spring.event;
import com.shiguiwu.springmybatis.spring.event.pattern.EventListener;
/**
* @description: 注册成功后发短信
* @author: stone
* @date: Created by 2021/4/8 15:28
* @version: 1.0.0
* @pakeage: com.shiguiwu.springmybatis.spring.event
*/
public class SendSMSUserRegisterSuccessListener implements EventListener<RegisterSuccessEvent> {
@Override
public void onEvent(RegisterSuccessEvent event) {
System.out.println(event.getUsername() + "注册成功,发送短信。。。。。");
}
}
``
#### 下面我们使用spring来将上面的对象组装起来
```java
package com.shiguiwu.springmybatis.spring.event;
import com.baomidou.mybatisplus.extension.api.R;
import com.shiguiwu.springmybatis.spring.event.pattern.EventListener;
import com.shiguiwu.springmybatis.spring.event.pattern.EventMulticaster;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.List;
/**
* @description: 配置类
* @author: stone
* @date: Created by 2021/4/8 14:30
* @version: 1.0.0
* @pakeage: com.shiguiwu.springmybatis.spring.event
*/
@Configuration("eventConfig1")
public class EventConfig {
/**
* 注册一个事件发布者bean
* @param eventListeners
* @return
*/
@Bean
@Autowired(required = false)
public EventMulticaster eventMulticaster(List<EventListener> eventListeners) {
SimpleEventMulticaster simpleEventMulticaster = new SimpleEventMulticaster();
if (eventListeners != null) {
eventListeners.parallelStream().forEach(simpleEventMulticaster::addEventListener);
}
return simpleEventMulticaster;
}
/**
* 用户注册服务
* @param eventMulticaster
* @return
*/
@Bean
public RegisterService registerService(EventMulticaster eventMulticaster) {
RegisterService registerService = new RegisterService();
registerService.setEventMulticaster(eventMulticaster);
return registerService;
}
@Bean
public EventListener<RegisterSuccessEvent> successEventEventListener() {
return new SendSMSUserRegisterSuccessListener();
}
@Bean
public EventListener<RegisterSuccessEvent> eventEventListener() {
return new GetCouponRegisterSuccessListener();
}
}
测试代码如下
package com.shiguiwu.springmybatis.spring.event;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* @description: spring事件模式
*
* 事件源:事件的触发者,比如上面的注册器就是事件源。
* 事件:描述发生了什么事情的对象,比如上面的:xxx注册成功的事件
* 事件监听器:监听到事件发生的时候,做一些处理,比如上面的:路人A、路人B
* @author: stone
* @date: Created by 2021/4/8 10:16
* @version: 1.0.0
* @pakeage: com.shiguiwu.springmybatis.spring.event
*/
public class EventTests {
public static void main(String[] args) {
//模拟用户注册
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(EventConfig.class);
RegisterService bean = context.getBean(RegisterService.class);
//用户注册
bean.register("administrator");
}
}
小结
上面将注册的主要逻辑(用户信息落库)和次要的业务逻辑(发送短信)通过事件的方式解耦了。次要的业务做成了可插拔的方式,比如不想发送短信了,只需要将邮件监听器上面的 @Component 注释就可以了,非常方便扩展。上面用到的和事件相关的几个类,都是我们自己实现的,其实这些功能在spring中已经帮我们实现好了,用起来更容易一些,下面带大家来体验一下。