手写事件发布订阅框架(二)

276 阅读5分钟

一、背景

之前写了一篇《手写事件发布订阅框架》,虽然可以用但代码写的比较粗糙,且存在优化的空间,于是对其进行了重构主要包括以下几点:

  1. 面向接口编程,包结构更加清晰。
  2. 框架改成spring-boot-starter的形式实现即插即用。
  3. 对核心类EventManager的代码进行了部分剥离,使其更符合职责单一原则。
  4. 发布事件时事件类不用再继承Event类,用户可以随意自定义。

新的项目结构如下图:

event-all.png

二、代码介绍

之前的EventManager(现EventListenerManager)承载了很多不该属于它的功能,比如初始化监听器配置信息,操作Spring上下文等,优化后通过OnceApplicationContextEventListener来进行监听器配置的初始化,EventListenerManager只用于管理事件监听器。

EventListenerManager

 package cn.sp.manager;
 ​
 import cn.sp.domain.EventListenerRegistration;
 import cn.sp.event.Event;
 import cn.sp.listener.EventListener;
 ​
 /**
  * @author Ship
  * @version 1.0.0
  * @description:
  * @date 2022/05/05 
  */
 public interface EventListenerManager {
 ​
 ​
     /**
      * 注册一个事件监听器
      *
      * @param clazz         事件类型
      * @param eventListener
      * @param <E>
      */
     <E extends Event> void registerListener(Class<? extends Event> clazz, EventListener<E> eventListener);
 ​
     /**
      * 通知所有该事件的监听器
      *
      * @param e
      * @param <E>
      */
     <E extends Event> void notifyListener(E e);
 ​
     /**
      * 注册一个事件监听器
      *
      * @param eventClazz 事件类型
      * @param listenerRegistration
      */
     void registerListener(Class<?> eventClazz, EventListenerRegistration listenerRegistration);
 ​
     /**
      * 通知所有该事件的监听器
      *
      * @param event
      */
     void notifyListener(Object event);
 ​
 }
 ​

DefaultEventListenerManager增加了一个新的listenerMap用于维护用户自定义的事件注册信息

 /**
  * @author 2YSP
  * @date 2022/4/16 
  */
 public class DefaultEventListenerManager implements EventListenerManager {
     /**
      * 事件map
      */
     private static Map<Class<? extends Event>, List<EventListener>> map = new HashMap<>(64);
     /**
      * 事件监听器map,key:事件类型
      */
     private static Map<Class<?>, List<EventListenerRegistration>> listenerMap = new HashMap<>(64);
 ​
 ​
     /**
      * 注册一个事件监听器
      *
      * @param clazz
      * @param eventListener
      * @param <E>
      */
     @Override
     public <E extends Event> void registerListener(Class<? extends Event> clazz, EventListener<E> eventListener) {
         List<EventListener> list = map.get(clazz);
         if (CollectionUtils.isEmpty(list)) {
             map.put(clazz, Lists.newArrayList(eventListener));
         } else {
             list.add(eventListener);
             map.put(clazz, list);
         }
     }
 ​
     /**
      * 移除一个事件监听器
      *
      * @param clazz
      * @param <E>
      */
     public <E extends Event> void removeListener(Class<E> clazz) {
         map.remove(clazz);
     }
 ​
     /**
      * 通知所有该事件的监听器
      *
      * @param <E>
      */
     @Override
     public <E extends Event> void notifyListener(E e) {
         List<EventListener> eventListeners = map.get(e.getClass());
         if (CollectionUtils.isEmpty(eventListeners)) {
             return;
         }
         eventListeners.forEach(eventListener -> {
             boolean async = false;
             try {
                 Method method = eventListener.getClass().getDeclaredMethod(EventConstants.EVENT_METHOD_NAME, Event.class);
                 AsyncExecute asyncExecute = AnnotationUtils.findAnnotation(method, AsyncExecute.class);
                 async = asyncExecute != null;
             } catch (NoSuchMethodException ex) {
                 ex.printStackTrace();
             }
             if (!async) {
                 // 同步执行
                 eventListener.onEvent(e);
             } else {
                 // 异步执行
                 EventPoolManager.INSTANCE.execute(() -> eventListener.onEvent(e));
             }
         });
     }
 ​
 ​
     @Override
     public void registerListener(Class<?> eventClazz, EventListenerRegistration listenerRegistration) {
         if (listenerMap.containsKey(eventClazz)) {
             List<EventListenerRegistration> configList = listenerMap.get(eventClazz);
             configList.add(listenerRegistration);
             listenerMap.put(eventClazz, configList);
         } else {
             listenerMap.put(eventClazz, Lists.newArrayList(listenerRegistration));
         }
     }
 ​
     @Override
     public void notifyListener(Object event) {
         Class<?> eventClass = event.getClass();
         List<EventListenerRegistration> eventListenerRegistrations = listenerMap.get(eventClass);
         eventListenerRegistrations.forEach(config -> {
             Class<?> clazz = config.getClazz();
             Object bean = config.getBean();
             Assert.notNull(bean, "the bean of event listener can not be null!");
             Method method = null;
             try {
                 method = clazz.getMethod(config.getMethodName(), eventClass);
             } catch (NoSuchMethodException e) {
                 e.printStackTrace();
             }
             if (config.getAsync()) {
                 Method method2 = method;
                 EventPoolManager.INSTANCE.execute(() -> invoke(method2, bean, event));
             } else {
                 invoke(method, bean, event);
             }
         });
     }
 ​
     private void invoke(Method method, Object target, Object args) {
         try {
             method.invoke(target, args);
         } catch (IllegalAccessException e) {
             e.printStackTrace();
         } catch (InvocationTargetException e) {
             e.printStackTrace();
         }
     }
 }
 ​

EventListenerRegistration是一个简单的实体类,定义了一些事件监听器注册信息的属性。

 package cn.sp.domain;
 ​
 /**
  * @author Ship
  * @version 1.0.0
  * @description: 事件监听器注册信息
  * @date 2022/04/24 
  */
 public class EventListenerRegistration {
 ​
     /**
      * 类
      */
     private Class<?> clazz;
     /**
      * 方法名
      */
     private String methodName;
     /**
      * 是否异步执行
      */
     private Boolean async;
     /**
      * 事件监听器对象
      */
     private Object bean;
 ​
 ​
     public EventListenerRegistration() {
     }
 ​
    
     public EventListenerRegistration(Class<?> clazz, String methodName, Boolean async, Object bean) {
         this.clazz = clazz;
         this.methodName = methodName;
         this.async = async;
         this.bean = bean;
     }
 ​
    
    // 省略getter/setter方法
 }
 ​

然后在项目启动时,OnceApplicationContextEventListener分别根据接口和注解初始化监听器配置信息

 /**
  * @author Ship
  * @version 1.0.0
  * @description:
  * @date 2022/05/05 
  */
 public class OnceApplicationContextEventListener implements ApplicationListener, ApplicationContextAware {
 ​
     private static ApplicationContext applicationContext;
 ​
     private static EventListenerManager eventListenerManager;
 ​
     private static Logger logger = LoggerFactory.getLogger(OnceApplicationContextEventListener.class);
 ​
     @Override
     public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
         OnceApplicationContextEventListener.applicationContext = applicationContext;
     }
 ​
     @Override
     public void onApplicationEvent(ApplicationEvent event) {
         if (isOriginalEventSource(event) && event instanceof ApplicationContextEvent) {
             onApplicationContextEvent((ApplicationContextEvent) event);
         }
     }
 ​
     private void onApplicationContextEvent(ApplicationContextEvent event) {
         OnceApplicationContextEventListener.eventListenerManager = applicationContext.getBean(EventListenerManager.class);
         logger.info("start to init event spring boot starter config...");
         initConfig();
         logger.info("init event spring boot starter config end.");
     }
 ​
     private boolean isOriginalEventSource(ApplicationEvent event) {
         boolean originalEventSource = nullSafeEquals(getApplicationContext(), event.getSource());
         if (!originalEventSource) {
             if (logger.isDebugEnabled()) {
                 logger.debug("The source of event[" + event.getSource() + "] is not original!");
             }
         }
         return originalEventSource;
     }
 ​
     public ApplicationContext getApplicationContext() {
         if (applicationContext == null) {
             throw new NullPointerException("applicationContext must be not null, it has to invoke " +
                     "setApplicationContext(ApplicationContext) method first if "
                     + ClassUtils.getShortName(getClass()) + " instance is not a Spring Bean");
         }
         return applicationContext;
     }
 ​
     /**
      * 初始化配置
      */
     private void initConfig() {
         // 根据接口注册监听器
         registerListenerByInterface();
         // 根据注解注册监听器
         registerListenerByAnnotation();
     }
 ​
     private void registerListenerByInterface() {
         Map<String, EventListener> beanMap = applicationContext.getBeansOfType(EventListener.class);
         if (beanMap == null) {
             return;
         }
         beanMap.forEach((key, value) -> {
             // 反射获取onEvent方法的参数类型
             Method[] methods = value.getClass().getDeclaredMethods();
             for (Method method : methods) {
                 if (method.getName().equals(EventConstants.EVENT_METHOD_NAME)) {
                     Parameter parameter = method.getParameters()[0];
                     // 参数必须为Event的子类
                     if (parameter.getType().getName().equals(Event.class.getName())) {
                         continue;
                     }
                     eventListenerManager.registerListener((Class<? extends Event>) parameter.getType(), value);
                 }
             }
         });
     }
 ​
     private void registerListenerByAnnotation() {
         Map<String, Object> map = applicationContext.getBeansWithAnnotation(MyEventListener.class);
         if (map == null) {
             return;
         }
         map.forEach((key, value) -> {
             // 获取所有method
             Class<?> listenerClazz = value.getClass();
             Method[] methods = listenerClazz.getDeclaredMethods();
             for (Method method : methods) {
                 MyEventListener myEventListener = AnnotationUtils.findAnnotation(method, MyEventListener.class);
                 if (myEventListener == null) {
                     continue;
                 }
                 Parameter parameter = method.getParameters()[0];
                 Class<?> eventClazz = parameter.getType();
                 EventListenerRegistration registration = new EventListenerRegistration(listenerClazz, method.getName(),
                         myEventListener.async(), value);
                 eventListenerManager.registerListener(eventClazz, registration);
             }
         });
     }
 }
 ​

上面registerListenerByAnnotation()方法中出现的**@MyEventListener**注解是为了标记出事件监听器类和方法,为了方便注入Spring容器所以加了@Component元注解,代码如下:

 /**
  * @author Ship
  * @version 1.0.0
  * @description: 事件监听标记注解
  * @date 2022/04/24 
  */
 @Component
 @Documented
 @Target(value = {ElementType.TYPE, ElementType.METHOD})
 @Retention(RetentionPolicy.RUNTIME)
 public @interface MyEventListener {
 ​
     /**
      * 是否异步执行,默认否
      *
      * @return
      */
     boolean async() default false;
 ​
 }

最后,在自动配置类EventStarterAutoConfigure中注入需要的bean。

 @Configuration
 public class EventStarterAutoConfigure {
 ​
     @Bean
     public EventListenerManager eventListenerManager() {
         return new DefaultEventListenerManager();
     }
 ​
     @Bean
     public EventPublisher eventPublisher(@Autowired EventListenerManager eventListenerManager) {
         return new DefaultEventPublisher(eventListenerManager);
     }
 ​
     @Bean
     public OnceApplicationContextEventListener onceApplicationListener() {
         return new OnceApplicationContextEventListener();
     }
 }

至此核心代码就结束了,还有一个小优化是用枚举来实现线程池的单例模式。

 public enum EventPoolManager {
 ​
     INSTANCE;
 ​
     /**
      * 事件执行线程池
      */
     private final static ExecutorService EVENT_POOL = new ThreadPoolExecutor(4,
             8, 30L, TimeUnit.SECONDS,
             new ArrayBlockingQueue<>(512), new ThreadFactoryBuilder().setNameFormat("event-pool-%d").build());
 ​
     /**
      * 执行任务
      *
      * @param command
      */
     public void execute(Runnable command) {
         EVENT_POOL.execute(command);
     }
 }

三、测试

测试过程比较简单,首先新建测试项目event-spring-boot-starter-sample并引入依赖

  <dependency>
             <groupId>cn.sp</groupId>
             <artifactId>event-spring-boot-starter</artifactId>
             <version>0.0.1-SNAPSHOT</version>
         </dependency>

然后发布事件

@Component
public class Test implements CommandLineRunner {

    @Resource
    private OrderService orderService;

    @Override
    public void run(String... args) throws Exception {
        orderService.create(new Order());
    }
}


@Service
public class OrderService {

    @Resource
    private EventPublisher publisher;


    /**
     * 创建订单
     *
     * @param order
     */
    public void create(Order order) {
        // 发送订单创建事件
        order.setOrderNo("sssss");
//        publisher.publish(new OrderCreateEvent(this, order));
        publisher.publish(order);
    }
}

再监听事件

/**
 * @author Ship
 * @version 1.0.0
 * @description:
 * @date 2022/04/24 
 */
@MyEventListener
public class OrderCreateEventListener3 {

    @MyEventListener(async = true)
    public void onListen(Order order){
        System.out.println(Thread.currentThread().getName() + "--监听订单创建事件3。。。。。。。。。");
    }
}

最后启动项目,控制台输出如下

2022-05-09 22:49:25.804  INFO 7845 --- [           main] .EventSpringBootStarterSampleApplication : Started EventSpringBootStarterSampleApplication in 1.815 seconds (JVM running for 2.789)
event-pool-0--监听订单创建事件3。。。。。。。。。

可以看到OrderCreateEventListener3成功的监听到了创建订单事件。

四、总结

如果大家对该项目有其他建议欢迎评论,该项目代码已经上传至github,点击查看。第一次在掘金写文章,感觉这个编辑器还挺好用的😏。