zoukankan      html  css  js  c++  java
  • EventBus VS Spring Event

    EventBus VS Spring Event

    本地异步处理,采用事件机制 可以使 代码解耦,更易读。事件机制实现模式是 观察者模式(或发布订阅模式),主要分为三部分:发布者、监听者、事件。

    Guava EventBus

    Guava EventBus实现是观察者模式,用法很简单,先上代码。

    不止是代码
    /**
     * Desc: 事件对象
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class HelloEvent {
        private String eventName;
    }
      
      
    @Data
    @NoArgsConstructor
    public class WorldEvent extends HelloEvent {
     
        private int eventNo;
     
        public WorldEvent(String name, int no) {
            setEventName(name);
            setEventNo(no);
        }
    }
      
    /**
     * Desc: 事件监听器,可以监听多个事件。处理方法添加 @Subscribe 注解即可。
     */
    public class GeventListener {
     
        /**
         * 监听 HelloEvent 类型及其父类型(Object)的事件
         */
        @Subscribe
        public void processEvent(HelloEvent event){
            System.out.println("process hello event, name:" + event.getEventName());
        }
     
        /**
         * 监听 WorldEvent 类型及其父类型(HelloEvent 和 Object)的事件
         */
        @Subscribe
        public void processWorldEvent(WorldEvent event) {
            System.out.println("process world eventV1, no:" + event.getEventNo() + ", name:" + event.getEventName());
        }
    
        /**
         * 注册多个监听器 监听同一事件
         * @param event
         */
        @Subscribe
        public void processWorldEventV2(WorldEvent event) {
            System.out.println("process world eventV2, no:" + event.getEventNo() + ", name:" + event.getEventName());
        }
     
        @Subscribe
        public void processObject(Object object) {
            System.out.println("process common event, class:" + object.getClass().getSimpleName());
        }
    }
      
    public class GuavaTest {
     
        public static void main(String[] args) {
            EventBus eventBus = new EventBus();
            GeventListener listener = new GeventListener();
            eventBus.register(listener);
     
            eventBus.post(new HelloEvent("hello"));
            eventBus.post(new WorldEvent("world", 23333));
        }
    }
    

    结果如下:

    //HelloEvent被两个监听器处理(HelloEvent类及Object类的监听器)
    process hello event, name:hello
    process common event, class:HelloEvent
    //WorldEvent被四个监听器处理(两个自己的,两个父类的)
    process world eventV1, no:23333, name:world
    process world eventV2, no:23333, name:world
    process hello event, name:world
    process common event, class:WorldEvent
    

    由上可知:Guava EventBus把类当做事件,是以class为key注册和管理事件的,value是事件监听器的method;事件监听器只处理某一类(及其父类)事件。

    事件注册与发布
    //com.google.common.eventbus.EventBus#register
      public void register(Object object) {
      //key为Class, value为EventSubscriber(Object target, Method method)【集合】。注意这里Multimap 为HashMultimap, 即HashMap<K, Collection<V>>
        Multimap<Class<?>, EventSubscriber> methodsInListener =
            finder.findAllSubscribers(object);
        subscribersByTypeLock.writeLock().lock();
        try {
          subscribersByType.putAll(methodsInListener);
        } finally {
          subscribersByTypeLock.writeLock().unlock();
        }
      }
    
    
    //com.google.common.eventbus.EventBus#post
      public void post(Object event) {
    	 //找到event类及其所有父类
        Set<Class<?>> dispatchTypes = flattenHierarchy(event.getClass());
    
        boolean dispatched = false;
        for (Class<?> eventType : dispatchTypes) {
          subscribersByTypeLock.readLock().lock();
          try {
          //找到所有事件订阅者(事件监听器)
            Set<EventSubscriber> wrappers = subscribersByType.get(eventType);
    
            if (!wrappers.isEmpty()) {
              dispatched = true;
              for (EventSubscriber wrapper : wrappers) {
              //事件入队列
                enqueueEvent(event, wrapper);
              }
            }
          } finally {
            subscribersByTypeLock.readLock().unlock();
          }
        }
    
    //如果没有订阅者订阅此类消息,则为 DeadEvent
        if (!dispatched && !(event instanceof DeadEvent)) {
          post(new DeadEvent(this, event));
        }
    
        dispatchQueuedEvents();
      }
    
    事件隔离

    多个EventBus可以隔离事件。

    public class AnotherListener {
        /**
         * 监听 WorldEvent 类型及其父类型(HelloEvent 和 Object)的事件
         */
        @Subscribe
        public void processAnotherWorldEvent(WorldEvent event) {
            System.out.println("process another world event, no:" + event.getEventNo() + ", name:" + event.getEventName());
        }
    }
    
    public class GuavaTest {
    
        public static void main(String[] args) {
            EventBus eventBus = new EventBus();
            GeventListener listener = new GeventListener();
            eventBus.register(listener);
            eventBus.post(new HelloEvent("hello"));
    
            EventBus anotherEventBus = new EventBus();
            AnotherListener anotherListener = new AnotherListener();
            anotherEventBus.register(anotherListener);
            anotherEventBus.post(new WorldEvent("AnotherWorld", 666));
        }
    }
    

    结果是

    //eventBus结果与之前相同
    process hello event, name:hello
    //anotherEventBus 发布的事件,只被其注册的监听器处理
    process common event, class:HelloEvent
    process another world event, no:666, name:AnotherWorld
    

    适用场景:

    • 按照类区分事件
    • 订阅 事件簇
    • 支持自定义event,可以根据event自己写分发器
    • 事件隔离

    spring event

    spring 新版事件机制也比较简单,看代码。

    不止是代码
    /**
     * 继承 ApplicationEvent 的事件
     */
    @Data
    public class HelloEvent extends ApplicationEvent {
    
        private String eventName;
    
        public HelloEvent(String eventName) {
            super(eventName);
            setEventName(eventName);
        }
    }
    
    
    /**
     * 自定义事件
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class CustomerEvent {
        private String name;
        private Boolean isCustomer;
    }
    
    /**
     * 监听器类,spring也支持一个类中监听多个事件
     */
    @Component("springListener")
    public class SpringListener {
    
        /**
         * 监听所有ApplicationEvent类型 及其子类型 的事件
         */
        @EventListener
        public void processApplicationEvent(ApplicationEvent event) {
            System.out.println("process common event, class:" + event.getClass().getSimpleName());
        }
    
        /**
         * 监听 HelloEvent类型 事件
         */
        @EventListener
        public void processHelloEvent(HelloEvent event) {
            System.out.println("process helloEvent, name:" + event.getEventName());
        }
    
        /**
         * 监听 CustomerEvent 类型事件,但是需要满足condition条件,即isCustomer=true
         */
        @EventListener(condition = "#event.isCustomer")
        public void processCustomerEvent(CustomerEvent event) {
            System.out.println("process customer CustomerEvent, name:" + event.getName());
        }
    
        /**
         * 监听 CustomerEvent 类型事件,但是需要满足condition条件,即name="miaomiao"
         */
        @EventListener(condition = "#event.getName().equals('miaomiao')")
        public void processMiaoMiaoEvent(CustomerEvent event) {
            System.out.println("process miaomiao's CustomerEvent, name:" + event.getName());
        }
    
        /**
         * 支持异步处理事件
         */
        @Async
        @EventListener
        public void processAsyncCustomerEvent(CustomerEvent event) {
            System.out.println("Async process CustomerEvent, name:" + event.getName());
        }
    }
    
    
    //执行类,测试入口
    @SpringBootApplication
    @ComponentScan(basePackages = {"com.example.manyao.async"})
    public class DemoApplication {
    	public static void main(String[] args) throws TException {
    		SpringApplication.run(DemoApplication.class, args);
    
    		ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    		String[] names = context.getBeanDefinitionNames();
    		for(int i=0; i<names.length; i++) {
    			System.out.println(names[i]);
    		}
    		System.out.println("++++++++++");
    		context.publishEvent(new HelloEvent("helloEvent"));
    		context.publishEvent(new CustomerEvent("customer", true));
    		context.publishEvent(new CustomerEvent("miaomiao", false));
    	}
    }
    

    结果是

    //以下是spring上下文event,继承自 ApplicationContextEvent。 用于用户参与上下文生命周期的入口。因为是ApplicationEvent子类型,所以,由processApplicationEvent处理。
    process common event, class:ContextRefreshedEvent
    process common event, class:EmbeddedServletContainerInitializedEvent
    process common event, class:ApplicationReadyEvent
    process common event, class:ContextRefreshedEvent
    //以下是上下文中的bean
    springListener
    org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    org.springframework.context.annotation.internalRequiredAnnotationProcessor
    org.springframework.context.annotation.internalCommonAnnotationProcessor
    org.springframework.context.event.internalEventListenerProcessor
    org.springframework.context.event.internalEventListenerFactory
    ++++++++++
    //HelloEvent 继承 ApplicationEvent,会被processApplicationEvent处理
    process common event, class:HelloEvent
    //监听 HelloEvent类型 的 processHelloEvent 处理
    process helloEvent, name:helloEvent
    //非 ApplicationEvent 的事件,则为 PayloadApplicationEvent
    process common event, class:PayloadApplicationEvent
    //isCustomer=true,符合processCustomerEvent处理条件
    process customer CustomerEvent, name:customer
    //监听CustomerEvent类型,处理结果
    Async process CustomerEvent, name:customer
    process common event, class:PayloadApplicationEvent
    //符合processMiaoMiaoEvent条件
    process miaomiao's CustomerEvent, name:miaomiao
    Async process CustomerEvent, name:miaomiao
    //spring 上下文事件
    process common event, class:ContextClosedEvent
    
    spring 上下文事件

    上述例子中的
    ContextRefreshedEvent,EmbeddedServletContainerInitializedEvent,ApplicationReadyEvent,ContextRefreshedEvent,ContextClosedEvent 等事件,都是spring上下文事件。可以通过监听这些事件,参与到spring生命周期中去。这种无侵入性交互方式,在做平台服务时,是一种很好的方式。

    注册监听器

    org.springframework.context.event.EventListenerMethodProcessor#processBean 将所有注解EventListener的方法,存入上下文的applicationListeners中。Listener的封装类为ApplicationListenerMethodAdapter(String beanName, Class<?> targetClass, Method method)。
    org.springframework.context.support.AbstractApplicationContext#refresh 中调用 initApplicationEventMulticaster 初始化事件发布管理器applicationEventMulticaster,然后调用registerListeners() 注册监听器。

    发布事件

    spring 起初只支持 ApplicationEvent类型事件,后来优化之后,支持自定义事件。自定义事件的处理,默认为PayloadApplicationEvent,相当于EventBus的DeadEvent。

    //org.springframework.context.support.AbstractApplicationContext#publishEvent(java.lang.Object, org.springframework.core.ResolvableType)
    	protected void publishEvent(Object event, ResolvableType eventType) {
    		Assert.notNull(event, "Event must not be null");
    		if (logger.isTraceEnabled()) {
    			logger.trace("Publishing event in " + getDisplayName() + ": " + event);
    		}
    
    		// Decorate event as an ApplicationEvent if necessary
    		ApplicationEvent applicationEvent;
    		if (event instanceof ApplicationEvent) {
    			applicationEvent = (ApplicationEvent) event;
    		}
    		else {
    		//若不是ApplicationEvent类型,则使用PayloadApplicationEvent封装
    			applicationEvent = new PayloadApplicationEvent<Object>(this, event);
    			if (eventType == null) {
    				eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();
    			}
    		}
    
    		// Multicast right now if possible - or lazily once the multicaster is initialized
    		if (this.earlyApplicationEvents != null) {
    			this.earlyApplicationEvents.add(applicationEvent);
    		}
    		else {
    //核心操作,初始化 event		
    	getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
    		}
    
    		//调用父类,发布事件
    		// Publish event via parent context as well...
    		if (this.parent != null) {
    			if (this.parent instanceof AbstractApplicationContext) {
    				((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
    			}
    			else {
    				this.parent.publishEvent(event);
    			}
    		}
    	}
    
    执行事件
    	@Override
    	public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
    		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    		//获取事件的监听器集合,并逐个触发执行监听器
    		for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
    		//异步的话,就放在线程池中执行
    			Executor executor = getTaskExecutor();
    			if (executor != null) {
    				executor.execute(new Runnable() {
    					@Override
    					public void run() {
    						invokeListener(listener, event);
    					}
    				});
    			}
    			else {
    			//本线程调用
    				invokeListener(listener, event);
    			}
    		}
    	}
    

    可以看到,spring的事件机制更复杂,但是功能同样强大。
    适用场景:

    • 按照类区分事件
    • 订阅 事件簇
    • 支持自定义event
    • 按照condition过滤同类型事件

    比较EventBus与Spring Event

    • 使用方式比较
    项目 事件 发布者 发布方法 是否异步 监听者 注册方式
    EventBus 任意对象 EventBus EventBus#post 注解Subscribe方法 手动注册EventBus#register
    Spring Event 任意对象 ApplicationEventPublisher ApplicationEventPublisher#publishEvent 支持同步异步 注解EventListener方法 系统注册
    • 使用场景比较
    项目 事件区分 是否支持事件簇 是否支持自定义event 是否支持过滤 是否支持事件隔离 复杂程度
    EventBus Class 简单
    Spring Event Class 复杂
  • 相关阅读:
    项目管理【38】 | 项目人力资源管理-管理项目团队
    转:模型蒸馏,教师学生模型
    转:pytorch 中forward 的用法与解释说明
    KNN, sklearn
    转:matplotlib, 去除plt.savefig()的白边
    转:Latex 表格 合并行/列
    转:LaTeX xcolor颜色介绍
    余弦相似性,cos距离函数
    python confusion matrix 混淆矩阵
    转:Tmux 使用教程
  • 原文地址:https://www.cnblogs.com/shoren/p/eventBus_springEvent.html
Copyright © 2011-2022 走看看