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


    一、背景

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

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

    二、代码介绍

    之前的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,点击查看

  • 相关阅读:
    欧拉工程第72题:Counting fractions
    欧拉工程第71题:Ordered fractions
    欧拉工程第70题:Totient permutation
    欧拉工程第69题:Totient maximum
    欧拉工程第68题:Magic 5-gon ring
    欧拉工程第67题:Maximum path sum II
    欧拉工程第66题:Diophantine equation
    欧拉工程第65题:Convergents of e
    欧拉工程第64题:Odd period square roots
    欧拉工程第63题:Powerful digit counts
  • 原文地址:https://www.cnblogs.com/2YSP/p/16251500.html
Copyright © 2020-2023  润新知