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();
  }

适用场景:

  • 按照类区分事件
  • 订阅 事件簇
  • 支持自定义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|是|是|是|否|复杂|

版权声明:本文为shoren原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/shoren/p/eventBus_springEvent.html