有这么一个需求,客户注册的时候,产品经理要求给客户发送短信,发送优惠券,还有就是发送积分。根据xp极限编程原则,只管今天不管明天,伪代码原则上
java">//1,注册
register();
//2,发送优惠券
sendCoupon();
//3,发送短信
sendSMS();
//4,发送积分
sendIntegral();
看得出来,这个代码逻辑一点问题都没有,但是
产品经理说:喂,最近公司效益不好,积分和优惠券就不要发送了。
于是,你又可能去注释掉发送优惠券和积分的代码,这没什么问题,然后组织测试人员在测试一波,简单就可以上线。
于是又过了几天。
产品经理又说:喂!最近效益不好,需要刺激用户消费,注册的时候继续发送优惠券和积分。
你说: 好的!
于是你又把注释的代码放开!!感觉so easy!
于是又过了几天。
产品说:喂,注册的功能很慢,而且经常会失败,你知道这会让很多客户流失,给公司造成很大损失。
然后你带着问题,去查询日志,最后发现是调用第三方短信服务特别慢,有时候还有失败,然后你就理直气壮的耍锅。
你说:经理,这个跟我写的代码没有关系,是因为第三方短信平台不稳定,日志都复制给你了,您瞧瞧!!
产品经理撇了你一眼。
说:短信发不发的无所谓,核心注册一定要成功!!
这个时候,你应该感觉到,这个注册逻辑变来变去,而且产品的需求也是合情合理,所以本着事不过三,三则重构的原则。前面简单的代码堆砌方式已经不能满足我们的需求变化了,所以我们要想怎么样优化自己的代码。
我们的代码逻辑没什么问题,矛盾在于,我们的主干逻辑和一些次要逻辑耦合在一起。使得主干逻辑一直没有变,次要的功能确实频繁的变化,这个时候,我们学习的监听者模式就派上用场了,然后主干逻辑和次要功能解耦。
我们这可以这样做,有四个人:注册器,工具人A,工具人B,工具人C,注册器负责主要逻辑,工具人A负责发送优惠券,工具人B负责发送积分,工具人C负责发送短信,当我的注册器有用户注册的时候,就广播给其他的工具人,让他们各司其职,该干嘛就干嘛。这个时候,我们发现,我们的注册逻辑主要和广播器耦合,负责帮我们广播信息就可以,至于具体工具人做什么事情,他是不知道的。而我们频繁去修改次要功能的时候,也不需要去修改我们的主干逻辑部分的代码。
事件模式中的几个概念
事件源:事件的触发者,也就是上面的注册器。
事件:描述一个动作,这里就是我们可以理解为注册这个动作。
事件监听器:监听到事件后,做的一些处理,就是上面的工具人。
事件广播器:负责广播信息给监听者。
下面我们使用监听者模式实现用户注册的业务
我们先来定义和事件相关的几个类
事件对象
表示所有事件的父类,内部有个source字段,表示事件源;我们自定义的事件需要继承这个类
java">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 表示当前监听器需要监听的事件类型,此接口中只有一个方法,用来实现处理事件的业务;其定义的监听器需要实现这个接口。
java">/*** @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);
}
事件广播器
- 负责事件监听器的管理(注册监听器&移除监听器,将事件和监听器关联起来)
- 负责事件的广播(将事件广播给所有的监听器,对该事件感兴趣的监听器会处理该事件)
java">/*** @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);
广播器的简单实现
java">/*** @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);
广播器的简单实现
java">/*** @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<>();@Overridepublic void multicastEvent(AbstractEvent event) {List<EventListener> eventListeners = eventObjectEventListenerMap.get(event.getClass());if (eventListeners != null) {eventListeners.parallelStream().forEach(e -> e.onEvent(event));}}@Overridepublic void addEventListener(EventListener<?> eventListener) {Class<?> eventType = this.getEventType(eventListener);List<EventListener> listeners = this.eventObjectEventListenerMap.computeIfAbsent(eventType, e -> new ArrayList<>());listeners.add(eventListener);}@Overridepublic 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 类
java">/*** @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;}
}
用户注册服务
负责实现用户注册逻辑
java">
/*** @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));}
}
自定义监听者
发送优惠券
java">/*** @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> {@Overridepublic 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> {@Overridepublic 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*/@Beanpublic RegisterService registerService(EventMulticaster eventMulticaster) {RegisterService registerService = new RegisterService();registerService.setEventMulticaster(eventMulticaster);return registerService;}@Beanpublic EventListener<RegisterSuccessEvent> successEventEventListener() {return new SendSMSUserRegisterSuccessListener();}@Beanpublic EventListener<RegisterSuccessEvent> eventEventListener() {return new GetCouponRegisterSuccessListener();}
}
测试代码如下
java">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中已经帮我们实现好了,用起来更容易一些,下面带大家来体验一下。