深入理解Guava EventBus实现思想和实际用法

一、写在前面

随着企业业务趋向复杂,往往会碰到以下情况

  • 在一个类中需要和多个其他类进行关联,造成类与类之间的耦合严重。
  • 多处地方有相同的逻辑,造成代码重复性。

而有效的解耦方式就是使用事件机制,它主要的实现思想就是观察者模式。观察者模式主要涉及到三个概念:事件、订阅者和发布者。简单可以理解为发布者发布事件,订阅者消费对应的事件。

实现本地事件机制的有Guava的eventBus,Spring的Event。本文我主要想介绍一下Guava的eventBus。

二、传统的观察者模式

为啥这里需要先说一下传统的观察者模式呢?因为Guava的eventBus实现和传统的观察者模式略微有些不同,所以这里先介绍一下传统的订阅者模式是如何实现的。
深入理解Guava EventBus实现思想和实际用法
以微信公众号来举例,假设微信用户就是观察者,微信公众号是被观察者,有多个的微信用户关注了王者荣耀这个微信账号,当这个公众号更新时就会通知这些订阅的微信用户。那观察者是如何将自己注册到被观察者上,被观察者又是如何主动去通知观察者的呢?接下来我们就一起根据代码来进行分析:

抽象观察者(Observer)
里面定义了一个更新的方法:

public interface Observer {
    public void update(String message);
}

具体观察者(ConcrereObserver)
微信用户是观察者,里面实现了更新的方法:

public class WeixinUser implements Observer {
    // 微信用户名
    private String name;
    public WeixinUser(String name) {
        this.name = name;
    }
    @Override
    public void update(String message) {
        System.out.println(name + "-" + message);
    }


}

抽象被观察者(Subject)
抽象主题,提供了attach、detach、notify三个方法:

public interface Subject {
    /**
     * 增加订阅者
     * @param observer
     */
    public void attach(Observer observer);
    /**
     * 删除订阅者
     * @param observer
     */
    public void detach(Observer observer);
    /**
     * 通知订阅者更新消息
     */
    public void notify(String message);
}

具体被观察者(ConcreteSubject)
微信公众号是具体主题(具体被观察者),里面存储了订阅该公众号的微信用户,并实现了抽象主题中的方法:

public class SubscriptionSubject implements Subject {
    //储存订阅公众号的微信用户
    private List<Observer> weixinUserlist = new ArrayList<Observer>();

    @Override
    public void attach(Observer observer) {
        weixinUserlist.add(observer);
    }

    @Override
    public void detach(Observer observer) {
        weixinUserlist.remove(observer);
    }

    @Override
    public void notify(String message) {
        for (Observer observer : weixinUserlist) {
            observer.update(message);
        }
    }
}

总结一下,在被观察者中有一个list,用来存储注册进来的观察者,被观察者提供一个注册和解除注册的接口,供观察者进行注册和解除注册。同时被观察者还有同时方法,在被观察者感知到自己变化时,调用注册进来的观察者进行update,所以本质上也就是观察者将自己某项功能供被观察者调用,使被观察拥有观察者的某项能力。

那为什么这里有这么多的接口,例如Observer和Subject,主要是为了提高程序的可扩展性,例如在Observer上我们有一天需要支持qq用户,只需要从接口Observer上在加一个实现即可,这样也符合了程序的开闭原则

深入理解Guava EventBus实现思想和实际用法
 

但是普通的观察者模式还是有所弊端,就是无法按需将消息传递给观察者,每一次被观察者发生变化,所有的观察者都被通知到了,也就是所有的观察者都被执行了,但是可能观察者只想观察到之中的某一部分变化。

例如,我是一个服装设计师,我只关心模特在走秀时穿的什么衣服,不关心她是在笑还是在哭,所以我希望只有模特在换衣服时通知到我,其他的时候不希望来打扰我,这个普通模式是无法做到的,而Guava的eventBus则是很好的解决了这个问题。

三、Guava EventBus

深入理解Guava EventBus实现思想和实际用法
首先看一下EventBus官方的画图,应该和普通的观察者模型相差不大,都是发布者进行发布,订阅者进行消费。但是又有一些区别

  • 基于EventBus,我们不需要定义Observer接口,也就是观察者接口,任意类型的对象都可以注册到EnventBus中。
  • EventBus可以将信息发布给想查看这类信息的订阅者,而不是发布给所有的订阅者

 
 
首先需要先介绍一下Guava EventBus的几个重要的类和函数。Guava EventBus对外暴露的所有可调用的接口,都是封装在EventBus类中,而AsyncEventBus继承自EventBus,提供了异步非堵塞的观察者模式。

在EventBus类中,我们需要注意register,unregister和post函数,这三个函数相信读者自己看函数名都可以联想到他的功能。
而首先需要关注register,这个函数到底和普通的观察者模式有什么区别才会使得eventBus支持观察者可选择性发送,所谓的可选择性具体指的是能接收的消息类型是发送消息(post函数中定义的event)类型的父类。

 
例如,当被观察者发送XMsg时,AObserver能够接收到消息,当发送YMsg时,BObserver能够接收到消息,当发送ZMsg时,BObserver能够接收到消息,其中XMsg是YMsg的父类,,name可以接收到消息的情况如下
 
深入理解Guava EventBus实现思想和实际用法
 
那么每一个Observer能够接收哪一种类型的event是在哪里定义的呢?
那就是@Subscribe注解,例如现在有一个Observer,

public DObserver{
	@Subscribe
	public void f1(XMsg event){...}
	
	@Subscrive
	public void f2(YMsg event){...}
}

当通过register函数将DObserver类对象注册到EventBus的时候,EventBus会根据@Subscribe注解找到f1和f2函数,并且将两个函数能接收的消息类型记录下来(XMsg->f1, YMsg->f2)。当我们通过post函数发送消息的时候,EventBus会通过之前的记录调用相应的函数。

 
 
当然理论总是需要结合代码实际的,那么上面的实现是在哪里呢?正是我们在上面提到的regist函数中

 void register(Object listener) {
 	//用来找到对应register类中有注解的函数,其中key就是函数第一个参数的参数类型,value就是对应的方法包装出来的subscribe对象
 	//这个找对应的方法时就是找注解了@Subscribe的方法
    Multimap<Class<?>, Subscriber> listenerMethods = findAllSubscribers(listener);
	//遍历所有的方法
    for (Entry<Class<?>, Collection<Subscriber>> entry : listenerMethods.asMap().entrySet()) {
      Class<?> eventType = entry.getKey();
      Collection<Subscriber> eventMethodsInListener = entry.getValue();

      CopyOnWriteArraySet<Subscriber> eventSubscribers = subscribers.get(eventType);
	   //第一次没有,则需要将这个subscribe添加上去,这边选择CopyOnWriteArraySet可以保证并发的安全性
      if (eventSubscribers == null) {
        CopyOnWriteArraySet<Subscriber> newSet = new CopyOnWriteArraySet<>();
        eventSubscribers =
            MoreObjects.firstNonNull(subscribers.putIfAbsent(eventType, newSet), newSet);
      }

      eventSubscribers.addAll(eventMethodsInListener);
    }
  }

通过这个函数我们就可以得到下面这种情况
深入理解Guava EventBus实现思想和实际用法

最后我们当调用post函数发送消息的时候,EventBus就通过注册表找到对应的可接受消息的函数,然后通过java的反射语法来动态的创建对象、执行函数。

四、Guava EventBus的使用

先定义一个观察者方法

public DObserver{
	@Subscribe
	public void f1(XMsg event){...}
	
	@Subscrive
	public void f2(YMsg event){...}
}

然后进行注册和发送

public TestEventBus {
	private EventBus syncEventBus = new EventBus();
	
	@Autowired
	private DObserver ovserver;
	
	@PostConstruct
	public void init() {
		syncEventBus.register(observer);
	}
	
	public void post(Object object) {
		syncEventBus.post(object);
	}
}
上一篇:06、Android进阶--Rxjava源码解析


下一篇:17-观察者模式