概念描述
什么是发布订阅模式(摘自百度):消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。而是将发布的消息分为不同的类别,无需了解哪些订阅者(如果有的话)可能存在。同样的,订阅者可以表达对一个或多个类别的兴趣,只接收感兴趣的消息,无需了解哪些发布者(如果有的话)存在。
代码实现
下面是代码的结构图
其中EventHandler与Subscribe为注解,分别为作用于类与方法,表明该类有订阅事件的方法,代码如下:
/**
* 事件处理者,标注在类上,表示该类含有事件监听器,需要配合Subscribe 一起使用
*
* @author gaowu
* @date 2020/9/2 10:31
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EventHandler {
}
/**
* 订阅该时事件的方法,需要配合EventHandler一起使用
* value 事件类型不填默认为默认的事件源参数类型
* order 事件执行顺序 order值越小越先执行(需要调用EventRegister.sort进行排序)
*
* @author gaowu
* @date 2020/9/2 10:32
*/
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Subscribe {
Class<?>[] value() default {};
int order() default 0;
}
EventSubscriberFactory 事件订阅者的工厂,其中eventSubscriberMap该key值为事件源的类型或者为 Subscribe中的value值, value值为一个事件订阅者的set集合(不支持重复订阅)。该map可以保证不同的类之间可以订阅同一种事件。(事件/事件源类型即带有@Subscribe方法的参数类型)
package cn.gw.framework.mode.event;
import java.util.*;
/**
* 支持不同类中订阅同一种事件
*
* @author gaowu
* @date 2020/9/2 19:49
*/
public class EventSubscriberFactory {
/**
* key值为事件源,value为事件的订阅者重复订阅会被忽略
*/
private static final Map<Class<?>, List<EventSubscriber>> eventSubscriberMap = new HashMap<>(16);
public void register(Class<?> eventSource, EventSubscriber eventSubscriber) {
List<EventSubscriber> eventSubscribers = new ArrayList<>();
eventSubscribers.add(eventSubscriber);
register(eventSource, eventSubscribers);
}
public void register(Class<?> eventSource, List<EventSubscriber> eventSubscriberList) {
if (eventSource == null || eventSubscriberList == null || eventSubscriberList.isEmpty()) {
return;
}
List<EventSubscriber> eventSubscribers = eventSubscriberMap.computeIfAbsent(eventSource, list -> new ArrayList<>());
// 去重
Set<EventSubscriber> eventSubscriberSet = new LinkedHashSet<>(eventSubscribers);
eventSubscriberSet.addAll(eventSubscriberList);
eventSubscriberMap.put(eventSource, new ArrayList<>(eventSubscriberSet));
}
public void unRegister(Class<?> eventSource) {
if (eventSource == null) {
return;
}
eventSubscriberMap.remove(eventSource);
}
public Map<Class<?>, List<EventSubscriber>> getEventSubscriberMap() {
return eventSubscriberMap;
}
public List<EventSubscriber> getEventSubscriberListByEventSource(Class<?> eventSource) {
List<EventSubscriber> eventSubscribers = eventSubscriberMap.get(eventSource);
return eventSubscribers == null ? new ArrayList<>() : eventSubscribers;
}
}
EventSubscriber:事件的订阅者;eventHandler:点阅事件方法所对应的实体;method:订阅事件的方法,该方法会针对该事件进行一些逻辑操作;order:对订阅事件的方法执行顺序进行排序(未实现);该类重写了hashcode与equals方法,保证该每个订阅者在集合中只存在一个,防止重复执行。
package cn.gw.framework.mode.event;
import java.lang.reflect.Method;
import java.util.Objects;
/**
* @author gaowu
* @date 2020/9/2 19:17
*/
public class EventSubscriber {
/**
* method所在类的实体
*/
private Object eventHandler;
/**
* 事件监听的方法
*/
private Method method;
/**
* 方法对应的执行顺序
*/
private Integer order;
/**
* 重写hashCode与equals方法,防止重复注册
*
* @return
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((eventHandler == null) ? 0 : eventHandler.getClass().hashCode());
result = prime * result + ((method == null) ? 0 : method.hashCode());
result = prime * result + ((order == null) ? 0 : order.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (this == obj) {
return true;
}
if (!(obj instanceof EventSubscriber)) {
return false;
}
EventSubscriber other = (EventSubscriber) obj;
return (eventHandler != null && other.getEventHandler() != null && Objects.equals(eventHandler.getClass(), other.getEventHandler().getClass()))
&& Objects.equals(method, other.getMethod())
&& Objects.equals(order, other.getOrder());
}
public Object getEventHandler() {
return eventHandler;
}
public EventSubscriber setEventHandler(Object eventHandler) {
this.eventHandler = eventHandler;
return this;
}
public Method getMethod() {
return method;
}
public EventSubscriber setMethod(Method method) {
this.method = method;
return this;
}
public Integer getOrder() {
return order;
}
public EventSubscriber setOrder(Integer order) {
this.order = order;
return this;
}
}
EventRegister 事件注册器 scannerSubscribeMethod该方法会扫描带有@EventHandler类中所有带有Subscribe的方法,并且判断方法是否合规,获取事件源类型,创建一个EventSubscriber实体注册进工厂中。
package cn.gw.framework.mode.event;
import cn.gw.framework.mode.event.annotation.EventHandler;
import cn.gw.framework.mode.event.annotation.Subscribe;
import lombok.extern.slf4j.Slf4j;
import org.reflections.Reflections;
import org.reflections.scanners.TypeAnnotationsScanner;
import org.reflections.util.ConfigurationBuilder;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
/**
* 事件注册器,将事件注册EventSubscriberFactory
*
* @author gaowu
* @date 2020/7/23 20:32
*/
@Slf4j
public class EventRegister {
/**
* 取消注册
*
* @param event
*/
public void undoRegistration(Object event) {
if (event == null) {
return;
}
new EventSubscriberFactory().unRegister(event.getClass());
}
/**
* 注册
*
* @param eventHandler
* @return
*/
public void doRegistration(Object eventHandler) {
if (eventHandler == null || !eventHandler.getClass().isAnnotationPresent(EventHandler.class)) {
return;
}
Map<Class<?>, List<EventSubscriber>> eventSubscriberMap = scannerSubscribeMethod(eventHandler);
EventSubscriberFactory eventSubscriberFactory = new EventSubscriberFactory();
eventSubscriberMap.forEach(eventSubscriberFactory::register);
}
public void doRegistration(List<Object> eventHandlers) {
if (eventHandlers == null || eventHandlers.isEmpty()) {
return;
}
List<CompletableFuture> futures = new ArrayList<>();
for (Object eventHandler : eventHandlers) {
CompletableFuture future = CompletableFuture.runAsync(() -> {
doRegistration(eventHandler);
});
futures.add(future);
}
futures.stream().map(CompletableFuture::join).collect(Collectors.toList());
}
/**
* 自动注册
*/
public void doAutoRegistration(String... packageNames) {
if (packageNames == null) {
return;
}
URL baseURL = Thread.currentThread().getContextClassLoader().getResource("");
Set<URL> urlSet = new HashSet<>();
for (String packageName : packageNames) {
String path = baseURL + packageName.trim().replace(".", "/");
try {
URL url = new URL(path);
urlSet.add(url);
} catch (MalformedURLException e) {
log.error("包名转换url异常 packageName -> {}", packageName);
}
}
Reflections reflections = new Reflections(new ConfigurationBuilder()
.addUrls(urlSet)
.setScanners(new TypeAnnotationsScanner()));
Set<Class<?>> eventHandlerClassSet = reflections.getTypesAnnotatedWith(EventHandler.class, true);
if (eventHandlerClassSet != null) {
List<CompletableFuture> futures = new ArrayList<>();
for (Class<?> eventHandlerClass : eventHandlerClassSet) {
try {
Object eventHandler = eventHandlerClass.newInstance();
CompletableFuture future = CompletableFuture.runAsync(() -> {
doRegistration(eventHandler);
});
futures.add(future);
} catch (IllegalAccessException | InstantiationException e) {
log.error("无法正确通过class创建对象 class -> {}", eventHandlerClass);
}
}
futures.stream().map(CompletableFuture::join).collect(Collectors.toList());
}
sort();
}
/**
* 循环扫描当前实体@Subscribe方法
*
* @param eventHandler
* @return
*/
private Map<Class<?>, List<EventSubscriber>> scannerSubscribeMethod(Object eventHandler) {
Class<?> eventHandlerClass = eventHandler.getClass();
Method[] methods = eventHandlerClass.getDeclaredMethods();
Map<Class<?>, List<EventSubscriber>> eventSubscriberMap = new HashMap<>();
for (Method method : methods) {
// 判断方法是否合规 包含Subscribe注解 参数只能有一个 非桥接方法
boolean isInvalidMethod = !method.isAnnotationPresent(Subscribe.class) || method.getParameters().length != 1 || method.isBridge();
if (isInvalidMethod) {
continue;
}
// 获取方法类型 Subscribe有value值优先使用,没有取参数类型
Subscribe subscribe = method.getAnnotation(Subscribe.class);
Class<?>[] subscribeValue = subscribe.value();
Class<?>[] eventSourceTypes = method.getParameterTypes();
Class<?> eventSource = subscribeValue.length == 0 ? eventSourceTypes[0] : subscribeValue[0];
// 不校验方法权限
method.setAccessible(true);
int order = subscribe.order();
// 生成订阅实体
EventSubscriber eventSubscriber = new EventSubscriber();
eventSubscriber.setOrder(order)
.setEventHandler(eventHandler)
.setMethod(method);
eventSubscriberMap.computeIfAbsent(eventSource, subscriberSet -> new ArrayList<>()).add(eventSubscriber);
}
return eventSubscriberMap;
}
public void sort() {
EventSubscriberFactory eventSubscriberFactory = new EventSubscriberFactory();
Map<Class<?>, List<EventSubscriber>> eventSubscriberMap = eventSubscriberFactory.getEventSubscriberMap();
eventSubscriberMap.forEach((eventSource, subscribers) -> {
if (subscribers != null) {
eventSubscriberMap.put(eventSource, sortEventSubscribers(subscribers));
}
});
}
public void sort(Class<?>... classes) {
if (classes == null) {
return;
}
EventSubscriberFactory eventSubscriberFactory = new EventSubscriberFactory();
Map<Class<?>, List<EventSubscriber>> eventSubscriberMap = eventSubscriberFactory.getEventSubscriberMap();
for (Class<?> eventSource : classes) {
List<EventSubscriber> subscribers = eventSubscriberMap.get(eventSource);
if (subscribers != null) {
eventSubscriberMap.put(eventSource, sortEventSubscribers(subscribers));
}
}
}
private List<EventSubscriber> sortEventSubscribers(List<EventSubscriber> eventSubscribers) {
eventSubscribers.sort(Comparator.comparing(EventSubscriber::getOrder));
return eventSubscribers;
}
}
EventBus 事件总线,派发事件,方法中通过该事件的类型,获取对应订阅事件的订阅人集合,通过执行该EventSubscriber中的invoke方法,去执行具体的订阅该事件方法中的逻辑。
package cn.gw.framework.mode.event;
import cn.gw.framework.base.util.ReflectUtil;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
/**
* @author gaowu
* @date 2020/7/23 19:01
*/
@Slf4j
public class EventBus {
private static EventSubscriberFactory eventSubscriberFactory = new EventSubscriberFactory();
/**
* 同步派发给所有
*
* @param eventSource
*/
public void distributeAll(Object eventSource) {
List<EventSubscriber> eventSubscribers = eventSubscriberFactory.getEventSubscriberListByEventSource(eventSource.getClass());
eventSubscribers.forEach(eventSubscriber -> {
try {
ReflectUtil.invokeMethod(eventSubscriber.getEventHandler(), eventSubscriber.getMethod(), new Object[]{eventSource});
} catch (RuntimeException ex) {
log.error("EventBus err distributeAll " + ex.getMessage(), ex.getCause());
throw ex;
}
});
}
/**
* 只派发给第一个
*
* @param eventSource
*/
public void distribute(Object eventSource) {
List<EventSubscriber> eventSubscribers = eventSubscriberFactory.getEventSubscriberListByEventSource(eventSource.getClass());
if (eventSubscribers.isEmpty()) {
log.warn("无法推送该事件 : {}", eventSource);
return;
}
EventSubscriber eventSubscriber = eventSubscribers.get(0);
try {
ReflectUtil.invokeMethod(eventSubscriber.getEventHandler(), eventSubscriber.getMethod(), new Object[]{eventSource});
} catch (RuntimeException ex) {
log.error("EventBus err distribute " + ex.getMessage(), ex.getCause());
throw ex;
}
}
/**
* 异步推送
*
* @param eventSource
*/
public void asyncDistributeAll(Object eventSource) {
List<EventSubscriber> eventSubscribers = eventSubscriberFactory.getEventSubscriberListByEventSource(eventSource.getClass());
eventSubscribers.forEach(eventSubscriber -> {
EventThreadPool.getInstance().execute(() -> {
try {
ReflectUtil.invokeMethod(eventSubscriber.getEventHandler(), eventSubscriber.getMethod(), new Object[]{eventSource});
} catch (RuntimeException ex) {
log.error("EventBus err asyncDistribute msg->{} , cause->{}", ex.getMessage(), ex.getCause());
throw ex;
}
});
});
}
/**
* 异步推送第一个
*
* @param eventSource
*/
public void asyncDistribute(Object eventSource) {
List<EventSubscriber> eventSubscribers = eventSubscriberFactory.getEventSubscriberListByEventSource(eventSource.getClass());
if (eventSubscribers.isEmpty()) {
log.warn("查询该事件为空,无法推送该事件 : {}", eventSource);
return;
}
EventSubscriber eventSubscriber = eventSubscribers.get(0);
EventThreadPool.getInstance().execute(() -> {
try {
ReflectUtil.invokeMethod(eventSubscriber.getEventHandler(), eventSubscriber.getMethod(), new Object[]{eventSource});
} catch (RuntimeException ex) {
log.error("EventBus err asyncDistribute msg->{} , cause->{}", ex.getMessage(), ex.getCause());
throw ex;
}
});
}
}
EventThreadPool 事件线程池类
package cn.gw.framework.mode.event;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* @author gaowu
* @date 2021/7/18 20:34
*/
@Slf4j
public class EventThreadPool {
private static final Integer POOL_SIZE = Runtime.getRuntime().availableProcessors() * 2;
private ExecutorService executor;
private static volatile EventThreadPool instance;
private EventThreadPool() {
executor = new ThreadPoolExecutor(POOL_SIZE, POOL_SIZE * 2, 0,
TimeUnit.MICROSECONDS, new LinkedBlockingDeque<>(1000));
}
void execute(Runnable runnable) {
executor.execute(runnable);
}
public void shutdown() {
executor.shutdown();
try {
if (executor.awaitTermination(30, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
} catch (InterruptedException i) {
log.error("EventThreadPool 线程池关闭失败,正在强制关闭");
executor.shutdownNow();
}
}
public static EventThreadPool getInstance() {
if (instance == null) {
synchronized (EventThreadPool.class) {
if (instance == null) {
instance = new EventThreadPool();
}
}
}
return instance;
}
}
ReflectUtil.invokeMethod方法如下
public static <T> T invokeMethod(Object obj, Method method, Object[] args) {
if (obj == null || method == null) {
return null;
}
try {
return (T) method.invoke(obj, args);
} catch (Exception e) {
String msg = "object -> " + obj + " method ->" + method + " args -> " + Arrays.toString(args);
throw convertReflectionExceptionToUnchecked(msg, e);
}
}
以上一个简单的发布订阅模型就设计完成,下面是具体的测试方法,首先创建两个事件源,如下:
/**
* @author gaowu
* @date 2020/9/2 15:37
*/
public class BeanFirst {
private String name ;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
/**
* @author gaowu
* @date 2020/9/2 17:08
*/
public class EventSecond {
}
创建两个执行订阅该事件的类与方法,如下:
/**
* @author gaowu
* @date 2020/9/2 15:36
*/
@EventHandler
public class EventHandlerFirst {
@Subscribe(EventFirst.class)
public void test(EventFirst first) {
System.out.println("first -> ");
}
@Subscribe
public void test2(EventFirst first) {
System.out.println("EventHandlerFirst first 2");
}
@Subscribe
public void test3(EventSecond second) {
System.out.println("EventHandlerFirst second 1");
}
@Subscribe
public void test4(EventSecond second) {
System.out.println("EventHandlerFirst second 2");
}
}
/**
* @author gaowu
* @date 2020/9/2 17:10
*/
@EventHandler
public class EventHandlerSecond {
@Subscribe
public void test1(EventSecond second){
System.out.println("EventHandlerSecond second 1 ");
}
@Subscribe
public void test2(EventSecond second){
System.out.println("EventHandlerSecond second 2");
throw new RuntimeException("test ex");
}
}
创建main方法进行测试:
public class Main {
public static void main(String[] args) {
EventRegister eventRegister = new EventRegister();
eventRegister.doRegistration(new EventHandlerFirst());
eventRegister.doRegistration(new EventHandlerSecond());
EventBus.distributeAll(new EventFirst());
System.out.println("----------");
EventBus.distributeAll(new EventSecond());
System.out.println("----------");
EventBus.asyncDistribute(new EventSecond());
}
}
运行结果如下:
Connected to the target VM, address: '127.0.0.1:52646', transport: 'socket'
EventHandlerFirst first 2
first ->
----------
EventHandlerSecond second 1
EventHandlerFirst second 2
EventHandlerSecond second 2
运行异常
EventHandlerFirst second 1
----------
EventHandlerFirst second 2
EventHandlerSecond second 2
EventHandlerSecond second 1
EventHandlerFirst second 1
运行异常
如果在spring的环境下@EventHandler可以配合@Component一起使用(这里可以直接在@EventHandler里加上@Component注解,这样只要写@EventHandler就可以),如下:
/**
* @author gaowu
* @date 2020/9/11 15:25
*/
@EventHandler
@Component
public class SysMsgHandler {
@Subscribe
public void sendSysMsg(SysMsgEvent sysMsgEvent) {
System.out.println("one ->" + sysMsgEvent.toString());
}
@Subscribe
public void sendSysMsg2(SysMsgEvent sysMsgEvent) {
System.out.println("two2 ->" + sysMsgEvent.toString());
}
}
/**
* @author gaowu
* @date 2020/9/11 15:23
*/
@EventHandler
@Component
public class OrderMsgHandler {
@Subscribe
public void sendOrderMsg(OrderMsgEvent orderMsgEvent){
System.out.println("one ->"+orderMsgEvent.toString());
}
@Subscribe
public void sendOrderMsg2(OrderMsgEvent orderMsgEvent){
System.out.println("two ->"+orderMsgEvent.toString());
}
}
通过spring方式注册事件:
/**
* @author gaowu
* @date 2020/5/1 2:40
*/
@Configuration
public class BootStrap {
@Bean
public void initEvent(){
EventRegister eventRegister = new EventRegister();
Map<String,Object> map = ApplicationContextHelper.getApplicationContext().getBeansWithAnnotation(EventHandler.class);
map.values().forEach(eventRegister::doRegistration);
}
}
编写测试方法:
@Test
public void testEvent(){
new EventBus().distributeAll(new OrderMsgEvent("orderNo","msg"));
System.out.println("---------");
EventBus.asyncDistribute(new SysMsgEvent("title","msg"));
}
测试结果
one ->OrderMsgEvent(orderNo=orderNo, msg=msg)
two ->OrderMsgEvent(orderNo=orderNo, msg=msg)
---------
one ->SysMsgEvent(title=title, msg=msg)
two2 ->SysMsgEvent(title=title, msg=msg)
测试完成,证明该发布订阅模型是可行的。目前自动注册方法(doAutoRegistration)是引入了反射包进行处理。当然我实现的该模型也会存在一些问题,慢慢完善吧。