zoukankan      html  css  js  c++  java
  • spring集成guava的event bus

    Guava的event bus

    guava, https://github.com/google/guava 是一个非常有名的Java类库,提供了很多在日常开发中常用的集合、函数接口等。此外,guava还提供了一个模块叫做event bus,生产者往event bus上投递消息,event bus负责回调订阅了此类消息的回调函数,实现了消息生产者和消费者之间的解耦和异步处理。以下是一个简单的例子:

    public class SimpleListener {
        @Subscribe
        public void task(String s) {
            System.out.println("do task(" + s + ")");
        }
    }
    public class SimpleEventBusExample {
        public static void main(String[] args) {
            EventBus eventBus = new EventBus();
            eventBus.register(new SimpleListener());
            System.out.println("Post Simple EventBus Example");
            eventBus.post("Simple EventBus Example");
        }
    }

    output

    Post Simple EventBus Example
    do task(Simple EventBus Example)

    event bus集成到spring中

    在之前的例子和guava的官方文档里面可以看到,guava的event bus使用方式如下

    1. 声明一个event bus对象(线程安全,所以可以做到全局唯一,而且订阅者和发布者必须共享这个event bus对象)

    2. 对于订阅者,支持 @Subscribe,定义处理消息的回调函数。

    3. 对每一个订阅者,需要调用event bus的register方法,才能收到消息订阅。

    对于1 和 2,都比较好。但是对于第3步来说,则有一点困难。因为

    1. 在spring中,通常的你的订阅者还会依赖其他的spring管理的bean,于是你的订阅者也会被纳入到spring的生命周期的管理中来,这样如果用new的方式来初始化一个订阅者,显得非常的"不spring"。

    2. 对于每个订阅者,都要显式的注册到event bus里面,这样并没有做到关注点分离。理想的情况下,订阅者是不应该去关注如何注册到event bus中。它只应该申明处理消息的回调函数,以及该回调函数是否能够并发调用。注册到event bus中这件事,对于订阅者应该是被动且自动的(只需要申明自己是否想注册到event bus,而不需要关心细节)。这一点在多人开发,并且项目人员水平参差不齐的时候,尤其重要。

    那么如何做到自动注册呢?其实答案很简单,在spring中,ApplicationContext这个类提供了一系列的方法去获取到当前spring context中的bean,只需要在event bus初始化之后,通过ApplicationContext来获取当前有哪些订阅者,并且主动的去注册就行。由于,guava的实现中,并没有要求订阅者实现某个接口,而是用注解的方式来声明回调函数的,则这篇文章中的实现,也不需要订阅者去实现某个接口,而是用注解的方式来申明自己是一个订阅者。代码如下

    先声明一个注解

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Service
    public @interface EventSubscriber {}

    对于一个订阅者,在类的接口上,加上这个注解,并且确定这个bean会在纳入spring的生命周期管理中。

    @EventSubscriber
    public class SimpleSubscriber implements HiDasSubscriber {
    @Autowired Somebean somebean; @Subscribe @AllowConcurrentEvents
    public Integer logEventToDbAndUpdateStatus(String event) { System.out.println("receive a event:"+event); } }

    声明一个event bus的服务,并且在初始化之后,通过ApplicationContext的 getBeansWithAnnotation 的方法把所有的订阅者获取,并且注册到event bus中。

    @Service
    public class EventBusService implements InitializingBean{
    
        private EventBus innerBus;
    
        @Inject
        private ApplicationContext appContext;
    
        public void unRegister(Object eventListener){
            innerBus.unregister(eventListener);
        }
    
        public void postEvent(String event){
            innerBus.post(event);
        }
    
        public void register(Object eventListener){
            innerBus.register(eventListener);
        }
    
        @Override
        public void afterPropertiesSet() throws Exception {
            innerBus = new AsyncEventBus("Hidas-event-bus", Executors.newCachedThreadPool());
            appContext.getBeansWithAnnotation(EventSubscriber.class).forEach((name, bean) -> {
                innerBus.register(bean);
            });
        }
    }

    完成了这两步之后。如果其他的消息生产者要往event bus上发消息,只需要注入这个event bus service,并且调用其post方法就好了。

    注:这个只是例子,在实际项目中,对所有的消息都会声明一个基类或者接口,每个订阅者对只会处理消息的某个具体实现。这样event bus会根据消息的具体类型,来调用真正关注此类消息的订阅者的回调函数。这样比起让所有消息订阅者去实现一个 onEvent(BaseEvent event)的方法, 实际上是避免了一个多路分配的问题。

    总结

    对于一些类库在spring中使用,这种方法实际上是一种通用的模式,实现某个接口或者编写某个注解,然后通过ApplicationContext来获取对应的bean,之后进行某些注册或者组装操作。这样的话,可以让业务的代码,和框架的代码做到一定程度的关注点分离。

  • 相关阅读:
    web.xml+spring mvc基本配置
    REST服务安全-双向认证
    thymeleaf 配置
    jenkins
    linux下ssh/scp无密钥登陆方法
    java编译 Error: Could not find or load main class java执行包main方法
    文本按列导入excel
    linux脚本-判断进程是否存在,从而可以做预警处理..
    Linux中顿号
    >/dev/null 2>&1
  • 原文地址:https://www.cnblogs.com/javanerd/p/6264820.html
Copyright © 2011-2022 走看看