ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2021-04-11 16:00:57  阅读:284  来源: 互联网

标签:observer Observer void 观察者 用法 EventBus Guava public


一、写在前面

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

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

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

实现本地事件机制的有Guava的eventBus,Spring的Event。本文我主要想介绍一下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

在这里插入图片描述
首先看一下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可以接收到消息的情况如下
 
在这里插入图片描述
 
那么每一个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);
    }
  }

通过这个函数我们就可以得到下面这种情况
在这里插入图片描述

最后我们当调用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);
	}
}

标签:observer,Observer,void,观察者,用法,EventBus,Guava,public
来源: https://blog.csdn.net/weixin_36488231/article/details/115596336

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有