06.EventBus

一、进程间(服务间)的消息发送/事件通知

 

二、进程内的消息发送/事件通知

不使用EventBus时,进程内程序,模块与模块之间调用,是直接调用的方式,这样的缺点是:

①模块之间耦合度高,不易扩展

②如果存在多个依赖关系,采用直接调用,代码难以维护

使用EventBus的目的是进行解耦(事件源与事件处理者解耦):

  • 模块A(进程A)向eventbus发送事件,模块B提前注册自己感兴趣的事件,当此类事件产生时,eventbus会push事件给对应的模块。
  • 模块B随后进行处理
  • 模块B处理完后,如果想要发送给模块A处理结果,可以将结果以一个新的事件发送到eventbus上,模块B这时作为监听者,接收模块B的处理结果,request与response是两个不同的、单独的event
  • 也就是同一个模块即可以是事件的发送者,也可以是其他事件的监听者

三、Google EventBus

1、使用特性

  • subscruber对象是一个普通类
  • subscribe方法必须是public的并且是void的(即使方法有返回值,google eventbus也不会处理返回值)
  • subscribe方法必须只有一个参数,多个参数无法识别,如果需要多个参数,可以将参数进行封装
  • subscribe方法必须被@Subscrive注解修饰

2、简单使用方式

1)编写向eventbus注册的listener

  • listener的回调方法通过@Subscribe注解进行声明:通过注解方式告诉eventbus,这是一个listener
  • 通过listener的调方法的参数列表,告诉eventbus,如果有方法参数类型的event,就push给这个listener
  • 方法参数只能有一个,多个参数无法识别,如果需要多个参数,可以将参数进行封装
package com.mzj.guava.eventbus.listeners;

import com.google.common.eventbus.Subscribe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SimpleListener {

    private final static Logger LOGGER = LoggerFactory.getLogger(SimpleListener.class);

    /**
     * 通过注解方式告诉eventbus,这是一个listener+参数是String类型的:告诉eventbus,如果有String类型的event,就push给这个listener
     *
     * google guava的eventbus的listener,方法参数只能有一个,多个参数无法识别,如果需要多个参数,可以将参数进行封装
     * @param event
     */
    @Subscribe
    public void doAction(final String event) {
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Received event [{}] and will take a action", event);
        }
    }
}

2)创建eventbus,并向其注册listener、发送事件

package com.mzj.guava.eventbus;

import com.google.common.eventbus.EventBus;
import com.mzj.guava.eventbus.listeners.SimpleListener;

public class SimpleEventBusExample {

    public static void main(String[] args) {
        final EventBus eventBus = new EventBus();
        eventBus.register(new SimpleListener());

        System.out.println("post the simple event...");
        eventBus.post("simple event");
    }
}

3)测试

4)unregister

说明:unregister后的listener不再会监听消息

eventbus.unregister(Object listener);

3、一个类中多个监听回调方法

package com.mzj.guava.eventbus.listeners;

import com.google.common.eventbus.Subscribe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 一个类中多个监听回调方法
 */
public class MultipleEventListener {
    private final static Logger LOGGER = LoggerFactory.getLogger(MultipleEventListener.class);

    @Subscribe
    public void task1(final String event) {
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Received event [{}] and will take a action by ==task1==", event);
        }
    }

    @Subscribe
    public void task2(final String event) {
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Received event [{}] and will take a action by ==task2==", event);
        }
    }

    @Subscribe
    public void intTask(final Integer event) {//这里的参数不能时int,而是integer
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Received event [{}] and will take a action by ==intTask==", event);
        }
    }
}

注意:基本类型不能作为listener方法的参数,需要使用对应的包装类型

测试类:

package com.mzj.guava.eventbus;

import com.google.common.eventbus.EventBus;
import com.mzj.guava.eventbus.listeners.MultipleEventListener;
import com.mzj.guava.eventbus.listeners.SimpleListener;

/**
 * 一个类中多个监听回调方法
 */
public class MultipleEventBusExample {

    public static void main(String[] args) {
        final EventBus eventBus = new EventBus();
        eventBus.register(new MultipleEventListener());

        System.out.println("post the string event...");
        eventBus.post("string event");
        System.out.println("post the int event...");
        eventBus.post(123);
    }
}

执行结果:


4、Listener的继承

AbstractListener.java

package com.mzj.guava.eventbus.listeners;

import com.google.common.eventbus.Subscribe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * listener继承
 */
public abstract class AbstractListener {

    private final static Logger LOGGER = LoggerFactory.getLogger(MultipleEventListener.class);

    @Subscribe
    public void commonTask(final String event) {
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Received event [{}] and will take a handle by {}.{}", event,this.getClass().getSimpleName(),"commonTask");
        }
    }
}

BaseListener.java extends AbstractListener.java

package com.mzj.guava.eventbus.listeners;

import com.google.common.eventbus.Subscribe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class BaseListener extends AbstractListener {

    private final static Logger LOGGER = LoggerFactory.getLogger(BaseListener.class);

    @Subscribe
    public void baseTask(final String event) {
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Received event [{}] and will take a handle by {}.{}", event,this.getClass().getSimpleName(),"baseTask");
        }
    }
}

ConcreteListener.java extends BaseListener.java

package com.mzj.guava.eventbus.listeners;

import com.google.common.eventbus.Subscribe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ConcreteListener extends BaseListener {

    private final static Logger LOGGER = LoggerFactory.getLogger(ConcreteListener.class);

    @Subscribe
    public void conTask(final String event) {
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Received event [{}] and will take a handle by {}.{}", event,this.getClass().getSimpleName(),"conTask");
        }
    }
}

测试类:

package com.mzj.guava.eventbus;

import com.google.common.eventbus.EventBus;
import com.mzj.guava.eventbus.listeners.ConcreteListener;
import com.mzj.guava.eventbus.listeners.MultipleEventListener;

/**
 * Listener的继承
 */
public class InheritListenerEventBusExample {

    public static void main(String[] args) {
        final EventBus eventBus = new EventBus();
        eventBus.register(new ConcreteListener());

        System.out.println("post the string event...");
        eventBus.post("string event");
    }
}

运行结果:

可见:继承关系的类,中符合参数列表的方法也会被调用,这种特性需要根据实际业务进行衡量,是否需要被调用。

5、Event的继承

Fruit(水果)

package com.mzj.guava.eventbus.events;

import com.google.common.base.MoreObjects;

public class Fruit {

    private String name;

    public Fruit(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return MoreObjects.toStringHelper(this)
                .add("name", name)
                .toString();
    }
}

苹果 extends 水果

package com.mzj.guava.eventbus.events;

public class Apple extends Fruit {

    public Apple(String name) {
        super(name);
    }
}

listener:

package com.mzj.guava.eventbus.listeners;

import com.google.common.eventbus.Subscribe;
import com.mzj.guava.eventbus.events.Apple;
import com.mzj.guava.eventbus.events.Fruit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Event的继承
 */
public class FruitEateListener {

    private final static Logger LOGGER = LoggerFactory.getLogger(FruitEateListener.class);

    @Subscribe
    public void eat(Fruit event) {
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Fruit eat [{}]", event);
        }
    }

    @Subscribe
    public void eat(Apple event) {
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Apple eat [{}]", event);
        }
    }
}

测试类:

package com.mzj.guava.eventbus;

import com.google.common.eventbus.EventBus;
import com.mzj.guava.eventbus.events.Apple;
import com.mzj.guava.eventbus.events.Fruit;
import com.mzj.guava.eventbus.listeners.ConcreteListener;
import com.mzj.guava.eventbus.listeners.FruitEateListener;

/**
 * Event的继承
 */
public class InheritEventsEventBusExample {

    public static void main(String[] args) {
        final EventBus eventBus = new EventBus();
        eventBus.register(new FruitEateListener());

        eventBus.post(new Apple("apple"));
        System.out.println("--------");
        eventBus.post(new Fruit("fruit"));
    }
}

运行结果:

可见,eventbus在继承事件的通知上与面向对象特性一致

6、Exception

说明:当eventbus收到某个事件时会以push的方式将消息推送给subscribers(消息订阅者s),某个subscriber处理消息时,如果出现异常(异常向上抛到eventbus调用线程),不会影响其他subscriber

listener:

package com.mzj.guava.eventbus.listeners;

import com.google.common.eventbus.Subscribe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ExceptionListener {

    private final static Logger LOGGER = LoggerFactory.getLogger(ExceptionListener.class);

    @Subscribe
    public void m1(String event){
        LOGGER.info("===========m1==========",event);
    }

    @Subscribe
    public void mException(String event){
        throw new RuntimeException("测试subscribe处理过程抛出异常");
    }

    @Subscribe
    public void m2(String event){
        LOGGER.info("===========m2==========",event);
    }

    @Subscribe
    public void m3(String event){
        LOGGER.info("===========m3==========",event);
    }
}

测试代码:

package com.mzj.guava.eventbus;

import com.google.common.eventbus.EventBus;
import com.mzj.guava.eventbus.listeners.ExceptionListener;

public class ExceptionEventBusExample {
    public static void main(String[] args) {

        final EventBus eventBus = new EventBus();
        eventBus.register(new ExceptionListener());
        eventBus.post("exception post");
    }
}

运行结果:

 

但是~~!!,我们发现,抛出的异常目前的处理行为仅仅是在eventbus内部进行了堆栈信息打印,那么如何能使我们可以获取到异常对象呢?

答案:①实现SubscriberExceptionHandler接口,在接口的handleException方法中可以获得异常对象并且通过方法的第二个参数context可以获取异常对应产生的事件、eventbus、Subscriber等信息②创建接口实现类并注册到eventbus上

注:SubscriberExceptionHandler接口是一个Functioninterface,如果是java8,可以直接传递lambda

代码示例:

package com.mzj.guava.eventbus;

import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.SubscriberExceptionContext;
import com.google.common.eventbus.SubscriberExceptionHandler;
import com.mzj.guava.eventbus.listeners.ExceptionListener;

public class ExceptionEventBusExample {
    public static void main(String[] args) {

        final EventBus eventBus = new EventBus(new ExceptionHandler());
        eventBus.register(new ExceptionListener());
        eventBus.post("exception post");
    }

    static class ExceptionHandler implements SubscriberExceptionHandler{

        @Override
        public void handleException(Throwable exception, SubscriberExceptionContext context) {
            System.out.println(context.getEvent());
            System.out.println(context.getEventBus());
            System.out.println(context.getSubscriber());
            System.out.println(context.getSubscriberMethod());
        }
    }
}

7、dead event

说明:当发送的事件没有被消费时(没有任何subscriber对其感兴趣)则此event被称为dead event,如何获取这类事件呢?

答:通过一个listener,方法声明的参数类型为DeadEvent

listener:

package com.mzj.guava.eventbus.listeners;

import com.google.common.eventbus.DeadEvent;
import com.google.common.eventbus.Subscribe;

public class DeadEventListener {

    @Subscribe
    public void handle1(DeadEvent event){
        System.out.println("[Dead listener]"+event.getSource());
        System.out.println("[Dead listener]"+event.getEvent());
    }

    @Subscribe
    public void handle2(String event){
        System.out.println("[String listener]" + event);
    }
}

测试代码:

package com.mzj.guava.eventbus;

import com.google.common.eventbus.EventBus;
import com.mzj.guava.eventbus.listeners.DeadEventListener;

public class DeadEventBusExample {
    public static void main(String[] args) {
        final EventBus eventBus = new EventBus();
        eventBus.register(new DeadEventListener());
        eventBus.post("Hello");
        eventBus.post(123);
    }
}

运行结果:

 

四、完整代码示例

https://github.com/mazhongjia/googleguava/tree/master/src/main/java/com/mzj/guava/eventbus

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值