`

观察者模式及Guava EventBus

阅读更多

编写不易,转载请注明(http://shihlei.iteye.com/blog/2426888)!

 

一 概述

最近看RxJava,其骨架就是使用的观察者模式,所以对观察者模式做个总结,顺便记录一下Guava EventBus的实现(事件监听,其实也相当于观察者模式)

 

二 观察者模式

1)概述

 

观察者模式:行为模式,提供一种一对多关系绑定对象的方法,一个对象状态需发生改变,绑定对象能收到通知完成自己的业务更新。

 

主要成员:

被观察者(Observerable):状态变化,通知所有的观察者。

观察者(observer):接收到“被观察者”的状态变化通知,执行自己的业务。

 

使用场景:

“被观察者”的状态变化需要通知多个“观察者”,但“被观察者”不需要知道观察者的个数具体细节,他们之间互相独立。一般“观察者”之间相互独立,不会彼此影响。

 

2)Demo

 

package x.rx.observer;

import java.util.LinkedList;
import java.util.List;

/**
 * 观察者模式:
 * 1)被观察者(Observerable):维护一个观察者队列,事件产生时,回调所以的观察者
 * 2)观察者(Observer):注册到"被观察者"中,接收通知,执行自己的业务
 *
 * @author shilei
 */
public class ObserverPatternDemo {

    public static void main(String[] args) {
        Observerable observerable = new Observerable();
        observerable.register(() -> {
            System.out.println("observer1 handle finish!");
        }).register(() -> {
            System.out.println("observer2 handle finish!");
        });

        observerable.generateEvent();
    }


    /**
     * 观察者
     */
    interface Observer {
        void doEvent();
    }

    /**
     * 被观察者
     */
    static class Observerable {

        private List<Observer> observerList = new LinkedList<>();

        /**
         * 注册观察者
         *
         * @param observer 观察者
         */
        Observerable register(Observer observer) {
            observerList.add(observer);
            return this;
        }

        /**
         * 取消注册
         */
        Observerable unRegister(Observer observer) {
            observerList.remove(observer);
            return this;
        }

        /**
         * 产生事件
         */
        void generateEvent() {
            for (Observer observer : observerList) {
                observer.doEvent();
            }
        }
    }
}

 

三 Guava EventBus

1) 概述

 

Guava EventBus 实现了事件监听器模式,主要提供一套基于注解的事件总线,可以灵活的使用。

 

使用方式:

1)定义一个关注的Event

2)定义一个监听处理方法,@Subscribe 标记,里面实现业务逻辑

3)注册到EventBus即可

 

2)Demo

(1)依赖

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>23.0</version>
        </dependency>

 

 (2)代码

package x.rx.eventbus;


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

/**
 * guava eventbus
 *
 * @author shilei
 */
public class EventBusDemo {

    public static void main(String[] args) {
        EventBus eventBus = new EventBus("demo");
        // 注册
        eventBus.register(new EventListener());
        // 产生事件
        eventBus.post(new Event());
    }

    /**
     * 事件
     */
    static class Event {

    }

    /**
     * 事件监听器
     */
    static class EventListener {

        @Subscribe //这里标记这个方法是事件处理方法
        public void handle1(Event event) {
            System.out.println("handle1 finish! ");
        }

        @Subscribe
        public void handle2(Event event) {
            System.out.println("handle2 finish! ");
        }

    }
}

 

3)源码分析

 

(1)事件注册到总线:EventBus的register()

 

private final SubscriberRegistry subscribers = new SubscriberRegistry(this);
 
  /**
   * Registers all subscriber methods on {@code object} to receive events.
   *
   * @param object object whose subscriber methods should be registered.
   */
public void register(Object object) {
    subscribers.register(object);
}
 

 “被观察者”的核心,提供一个队列,维护所有需要通知的观察者,Guava EventBus 这个工作代理给了 SubscriberRegistry , SubscriberRegistry 提供任何类型的事件到事件处理类的绑定关系。

 

看看注册的细节:

 

 /**
   * All registered subscribers, indexed by event type.
   *
   * <p>The {@link CopyOnWriteArraySet} values make it easy and relatively lightweight to get an
   * immutable snapshot of all current subscribers to an event without any locking.
   */
  private final ConcurrentMap<Class<?>, CopyOnWriteArraySet<Subscriber>> subscribers =
      Maps.newConcurrentMap();

  /**
   * Registers all subscriber methods on the given listener object.
   */
  void register(Object listener) {
    Multimap<Class<?>, Subscriber> listenerMethods = findAllSubscribers(listener);

    for (Map.Entry<Class<?>, Collection<Subscriber>> entry : listenerMethods.asMap().entrySet()) {
      Class<?> eventType = entry.getKey();
      Collection<Subscriber> eventMethodsInListener = entry.getValue();

      CopyOnWriteArraySet<Subscriber> eventSubscribers = subscribers.get(eventType);

      if (eventSubscribers == null) {
        CopyOnWriteArraySet<Subscriber> newSet = new CopyOnWriteArraySet<Subscriber>();
        eventSubscribers =
            MoreObjects.firstNonNull(subscribers.putIfAbsent(eventType, newSet), newSet);
      }

      eventSubscribers.addAll(eventMethodsInListener);
    }
  }
 

 

    内部是一个ConcrurentMap,key 是事件class,value是个 CopyOnWriteArraySet<Subscriber> 用于通知,最重要的方法  findAllSubscribers(listener)  ,他会通过反射,找到 @Subscribe 注解的方法,并关联他绑定的事件。获取到就添加到 subscribers(类型ConcrurentMap)中,这个subscribers 维护了所有的事件和事件处理器的绑定关系。

 

    看看findAllSubscribers(listener) 的细节:

 

  /**
   * Returns all subscribers for the given listener grouped by the type of event they subscribe to.
   */
  private Multimap<Class<?>, Subscriber> findAllSubscribers(Object listener) {
    Multimap<Class<?>, Subscriber> methodsInListener = HashMultimap.create();
    Class<?> clazz = listener.getClass();
    for (Method method : getAnnotatedMethods(clazz)) {
      Class<?>[] parameterTypes = method.getParameterTypes();
      Class<?> eventType = parameterTypes[0];
      methodsInListener.put(eventType, Subscriber.create(bus, listener, method));
    }
    return methodsInListener;
  }
   
    很常规的反射和Annotation处理,不过事件一定是处理方法的第一个参数。

    

    就此,EventBus就有了要通知的订阅者列表了

 

(2)提交事件,调用订阅者方法

 

这里就很简单了,根据事件类型,从 subscribers(类型ConcrurentMap)获得相应的订阅者集合,通过反射调用下方法就ok

  

  /**
   * Posts an event to all registered subscribers. This method will return successfully after the
   * event has been posted to all subscribers, and regardless of any exceptions thrown by
   * subscribers.
   *
   * <p>If no subscribers have been subscribed for {@code event}'s class, and {@code event} is not
   * already a {@link DeadEvent}, it will be wrapped in a DeadEvent and reposted.
   *
   * @param event event to post.
   */
  public void post(Object event) {
    Iterator<Subscriber> eventSubscribers = subscribers.getSubscribers(event);
    if (eventSubscribers.hasNext()) {
      dispatcher.dispatch(event, eventSubscribers);
    } else if (!(event instanceof DeadEvent)) {
      // the event had no subscribers and was not itself a DeadEvent
      post(new DeadEvent(this, event));
    }
  }
  

(3)总结:

 

EventBus 在观察者模式上做了通用性的抽象,可以定义任何事件,和基于注解的事件处理器,还是非常有用的。

分享到:
评论

相关推荐

    跟着 Guava、Spring 学习如何设计观察者模式.doc

    Guava的EventBus是观察者模式的一个实例,它简化了事件发布和订阅的过程。EventBus允许你定义事件的发布者(即被观察者)和事件的订阅者(即观察者)。发布者通过调用EventBus的post方法发布事件,而订阅者通过在...

    观察者模式

    9. **扩展**:除了Java内置的`Observer`和`Observable`,开发者还可以使用第三方库如Guava的`EventBus`,或者Spring框架的`ApplicationEvent`和`ApplicationListener`,这些都提供了更高级的观察者模式实现。...

    EventBus与Spring Event区别详解(EventBus 事件机制,Spring Event事件机制)

    它基于观察者模式(或发布订阅模式),主要分为三部分:发布者、监听者、事件。EventBus的使用非常简单,只需要定义事件对象、事件监听器和事件发布者。 在上面的代码示例中,我们定义了一个HelloEvent事件对象和一...

    JAVA 发布-订阅(观察者)模式

    发布-订阅(观察者)模式是软件设计模式中的一种行为模式,它在Java中得到了广泛的应用。这种模式的核心思想是定义对象之间的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。在...

    guava_programming.zip

    EventBus是一种观察者模式的实现,它允许组件之间通过发布和订阅事件进行通信,而不必显式地相互依赖。通过注册到EventBus的订阅者可以接收特定类型的事件,而发布者只需发送事件,无需关心哪个或哪些订阅者会处理...

    EventBus消息总线

    EventBus是Guava的事件处理机制,是设计模式中的观察者模式(生产/消费者编程模型)的优雅实现。对于事件监听和发布订阅模式,EventBus是一个非常优雅和简单解决方案,我们不用创建复杂的类和接口层次结构。 ...

    SpringBoot+EventBus使用教程示例代码

    EventBus是Google Guava库的一部分,它提供了一种发布/订阅模式的事件传递机制,允许应用程序的不同组件之间进行异步通信,而无需直接引用彼此。 **二、EventBus的使用** 1. **依赖添加**:首先,在SpringBoot项目...

    设计模式 java 参考模式

    在异步非阻塞的EventBus框架中,观察者模式是核心机制,它允许订阅者注册事件监听,并在事件触发时进行相应处理。 6. 访问者模式:访问者模式是一种行为设计模式,它允许在不修改类结构的情况下,向已存在的对象...

    Observer设计模式

    在Java中,除了标准库提供的Observable和Observer之外,还可以使用第三方库如Guava的EventBus或Spring框架的ApplicationEventPublisher接口来实现观察者模式,这些库提供了更高级的功能和更好的扩展性。 总之,...

    Node.js-EventBusOttoRxBus事件总线使用

    RxJava是一个强大的异步编程库,它将观察者模式和函数响应式编程结合在一起。RxBus使用`Observable`来发布事件,订阅者通过`subscribe()`方法订阅这些事件。RxBus的优势在于它可以提供更复杂的事件传播规则,如延迟...

    Prism:Prism 代码库

    10. **设计模式**:Java开发者应该熟悉GOF(Gang of Four)设计模式,包括工厂模式、观察者模式、装饰器模式等,它们是解决常见软件设计问题的有效工具。 在构建类似Prism的Java框架时,上述知识点将构成核心组件,...

    stb-java

    观察者模式和事件总线(如Guava的EventBus或Kotlin的Coroutines Channel)是实现事件驱动编程的常见工具。 9. **测试与调试**:单元测试、集成测试和端到端测试是确保代码质量的关键步骤。JUnit、Mockito等库可以...

Global site tag (gtag.js) - Google Analytics