设计一个简单实用的发布订阅模型(基于注解)。

563 阅读5分钟

概念描述

什么是发布订阅模式(摘自百度):消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。而是将发布的消息分为不同的类别,无需了解哪些订阅者(如果有的话)可能存在。同样的,订阅者可以表达对一个或多个类别的兴趣,只接收感兴趣的消息,无需了解哪些发布者(如果有的话)存在。

代码实现

下面是代码的结构图

image.png

其中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)是引入了反射包进行处理。当然我实现的该模型也会存在一些问题,慢慢完善吧。