Spring系列之事件机制详解
事件模式中的几个概念
事件源:事件的触发者,比如上面的注册器就是事件源。
事件:描述发生了什么事情的对象,比如上面的:xxx注册成功的事件
事件监听器:监听到事件发生的时候,做一些处理,比如上面的:路人A、路人B
下面我们使用事件模式实现用户注册的业务,我们先来定义和事件相关的几个类。
事件对象:表示所有事件的父类,内部有个source字段,表示事件源;我们自定义的事件需要继承这个类。
总结:使用spring事件场景是:在单体应用中,使用spring事件好处是
- 1:异步处理
- 2:解耦,方便维护
package com.javacode2018.lesson003.demo1.test0.event; /** * 事件对象 */ public abstract class AbstractEvent { //事件源 protected Object source; public AbstractEvent(Object source) { this.source = source; } public Object getSource() { return source; } public void setSource(Object source) { this.source = source; } }
事件监听器:我们使用一个接口来表示事件监听器,是个泛型接口,后面的类型E表示当前监听器需要监听的事件类型,此接口中只有一个方法,用来实现处理事件的业务;其定义的监听器需要实现这个接口。
package com.javacode2018.lesson003.demo1.test0.event; /** * 事件监听器 * * @param <E> 当前监听器感兴趣的事件类型 */ public interface EventListener<E extends AbstractEvent> { /** * 此方法负责处理事件 * * @param event 要响应的事件对象 */ void onEvent(E event); }
事件广播器
- 负责事件监听器的管理(注册监听器&移除监听器,将事件和监听器关联起来)
- 负责事件的广播(将事件广播给所有的监听器,对该事件感兴趣的监听器会处理该事件)
package com.javacode2018.lesson003.demo1.test0.event; /** * 事件广播器: * 1.负责事件监听器的管理(注册监听器&移除监听器,将事件和监听器关联起来) * 2.负责事件的广播(将事件广播给所有的监听器,对该事件感兴趣的监听器会处理该事件) */ public interface EventMulticaster { /** * 广播事件给所有的监听器,对该事件感兴趣的监听器会处理该事件 * * @param event */ void multicastEvent(AbstractEvent event); /** * 添加一个事件监听器(监听器中包含了监听器中能够处理的事件) * * @param listener 需要添加监听器 */ void addEventListener(EventListener<?> listener); /** * 将事件监听器移除 * * @param listener 需要移除的监听器 */ void removeEventListener(EventListener<?> listener); }
事件广播默认实现
package com.example.springbootstudy.test.event; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * 事件广播器简单实现 */ public class SimpleEventMulticaster implements EventMulticaster { private Map<Class<?>, List<EventListener>> eventObjectEventListenerMap = new ConcurrentHashMap<>(); @Override public void multicastEvent(AbstractEvent event) { List<EventListener> eventListeners = this.eventObjectEventListenerMap.get(event.getClass()); if (eventListeners != null) { for (EventListener eventListener : eventListeners) { eventListener.onEvent(event); } } } @Override public void addEventListener(EventListener<?> listener) { Class<?> eventType = this.getEventType(listener); List<EventListener> eventListeners = this.eventObjectEventListenerMap.get(eventType); if (eventListeners == null) { eventListeners = new ArrayList<>(); this.eventObjectEventListenerMap.put(eventType, eventListeners); } eventListeners.add(listener); } @Override public void removeEventListener(EventListener<?> listener) { Class<?> eventType = this.getEventType(listener); List<EventListener> eventListeners = this.eventObjectEventListenerMap.get(eventType); if (eventListeners != null) { eventListeners.remove(listener); } } /** * 获取事件监听器需要监听的事件类型 * * @param listener * @return */ protected Class<?> getEventType(EventListener listener) { ParameterizedType parameterizedType = (ParameterizedType) listener.getClass().getGenericInterfaces()[0]; Type eventType = parameterizedType.getActualTypeArguments()[0]; return (Class<?>) eventType; } }
上面3个类支撑了整个时间模型,下面我们使用上面三个类来实现注册的功能,目标是:高内聚低耦合,让注册逻辑方便扩展。
自定义用户注册成功事件类
继承了AbstractEvent类
package com.javacode2018.lesson003.demo1.test0.userregister; import com.javacode2018.lesson003.demo1.test0.event.AbstractEvent; /** * 用户注册成功事件 */ public class UserRegisterSuccessEvent extends AbstractEvent { //用户名 private String userName; /** * 创建用户注册成功事件对象 * * @param source 事件源 * @param userName 当前注册的用户名 */ public UserRegisterSuccessEvent(Object source, String userName) { super(source); this.userName = userName; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } }
用户注册服务
负责实现用户注册逻辑
package com.example.springbootstudy.test.event; /** * 用户注册服务 */ public class UserRegisterService { //事件发布者 private EventMulticaster eventMulticaster; //@0 /** * 注册用户 * * @param userName 用户名 */ public void registerUser(String userName) { //@1 //用户注册(将用户信息入库等操作) System.out.println(String.format("用户【%s】注册成功", userName)); //@2 //广播事件 this.eventMulticaster.multicastEvent(new UserRegisterSuccessEvent(this, userName)); //@3 } public EventMulticaster getEventMulticaster() { return eventMulticaster; } public void setEventMulticaster(EventMulticaster eventMulticaster) { this.eventMulticaster = eventMulticaster; } }
@0:事件发布者
@1:registerUser这个方法负责用户注册,内部主要做了2个事情
@2:模拟将用户信息落库
@3:使用事件发布者eventPublisher发布用户注册成功的消息:
下面我们使用spring来将上面的对象组装起来
package com.example.springbootstudy.test.event; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import java.util.List; @Configuration @ComponentScan public class MainConfig0 { /** * 注册一个bean:事件发布者 * * @param eventListeners * @return */ @Bean @Autowired(required = false) public EventMulticaster eventMulticaster(List<EventListener> eventListeners) { //@1 EventMulticaster eventPublisher = new SimpleEventMulticaster(); if (eventListeners != null) { eventListeners.forEach(eventPublisher::addEventListener); } return eventPublisher; } /** * 注册一个bean:用户注册服务 * * @param eventMulticaster * @return */ @Bean public UserRegisterService userRegisterService(EventMulticaster eventMulticaster) { //@2 UserRegisterService userRegisterService = new UserRegisterService(); userRegisterService.setEventMulticaster(eventMulticaster); return userRegisterService; } }
上面有2个方法,负责向spring容器中注册2个bean。
@1:向spring容器中注册了一个bean:事件发布者,方法传入了EventListener类型的List,这个地方会将容器中所有的事件监听器注入进来,丢到EventMulticaster中。
@2:向spring容器中注册了一个bean:用户注册服务
来个测试用例模拟用户注册
package com.example.springbootstudy.test.event; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; @RestController @RequestMapping("/user") public class UserController { @Resource UserRegisterService userRegisterService; @GetMapping("/testEvent") public void testEvent() { // 模拟用户注册 userRegisterService.registerUser("路人甲Java"); } }
运行输出
用户【路人甲Java】注册成功
添加注册成功发送邮件功能
下面添加一个注册成功发送邮件的功能,只需要自定义一个监听用户注册成功事件的监听器就可以了,其他代码不需要任何改动,如下
package com.example.springbootstudy.test.event; import org.springframework.stereotype.Component; /** * 用户注册成功事件监听器->负责给用户发送邮件 */ @Component public class SendEmailOnUserRegisterSuccessListener implements EventListener<UserRegisterSuccessEvent> { @Override public void onEvent(UserRegisterSuccessEvent event) { System.out.println( String.format("给用户【%s】发送注册成功邮件!", event.getUserName())); } }
上面这个类使用了@Component,会被自动扫描注册到spring容器。
再次运行测试用例输出
用户【路人甲Java】注册成功
给用户【路人甲Java】发送注册成功邮件!
小结
上面将注册的主要逻辑(用户信息落库)和次要的业务逻辑(发送邮件)通过事件的方式解耦了。次要的业务做成了可插拔的方式,比如不想发送邮件了,只需要将邮件监听器上面的@Component注释就可以了,非常方便扩展。上面用到的和事件相关的几个类,都是我们自己实现的,其实这些功能在spring中已经帮我们实现好了,用起来更容易一些,下面带大家来体验一下。
Spring中实现事件模式
事件相关的几个类
Spring中事件相关的几个类需要先了解一下,下面来个表格,将spring中事件相关的类和我们上面自定义的类做个对比,方便大家理解.这些类和我们自定义的类中代码有点类似,有兴趣的可以去看一下源码,这里就不列出来了。
硬编码的方式使用spring事件3步骤
步骤1:定义事件
自定义事件,需要继承ApplicationEvent类,
步骤2:定义监听器
自定义事件监听器,需要实现ApplicationListener接口,这个接口有个方法onApplicationEvent需要实现,用来处理感兴趣的事件。
地址:https://blog.csdn.net/weixin_46228112/article/details/124644633