guava learning (a): Observer Pattern

The observer pattern is very common behavioral design patterns. In the implementation of Java primitive form, the viewer realize the Observer interface, inherited Observable observer.

Here the use of Java api to write a simple implementation.

Observer Code:

public class MyObserver implements Observer {

    public void update(Observable o, Object arg) {
        if (o instanceof MyObservable){
            System.out.println(arg);
        }

    }
}

The observed:

public class MyObservable extends Observable {

    @Override
    public void notifyObservers(Object message){
        super.setChanged();
        super.notifyObservers(message);
    }

}

 

Bound subject categories:

public class Subject {

    private Observable observable = new MyObservable();

    public void registerObserver(MyObserver observer) {
        observable.addObserver(observer);
    }

    public void removeObserver(MyObserver observer) {
        observable.deleteObserver(observer);
    }

    public void notifyObservers(String message) {
        observable.notifyObservers(message);
    }

}

Test code

public static void main(String[] args) {
        Subject subject = new Subject();
        MyObserver observer = new MyObserver();
        subject.registerObserver(observer);
        subject.notifyObservers("hi, I am subject Observable");
}

java implementation, if the observer is achieved using an asynchronous message processing, and the service code will be coupled together non-business code.

guava observer encapsulates Java mode, and convenient support asynchronous. talk is cheap, look at the code:

Definition 2 viewers:

public class AObserver {

    Logger logger = LoggerFactory.getLogger(getClass());

    @Subscribe
    public void handleMessage(String msg){
        logger.info("a obsesrver receive message:{}", msg);
    }
}
public class BObserver {

    Logger logger = LoggerFactory.getLogger(getClass());

    @Subscribe
    public void handleMessage(String msg){
        logger.info("b obsesrver receive message:{}", msg);
    }
}

 

EventBusUtil class

public class EventBusUtil {

    public static EventBus getEventBus(){
        return EventBusFactory.getAsyncInstance();
    }

    public static class EventBusFactory{
        private static EventBus asyncEventBus = new AsyncEventBus(LocalThreadPoolExecutor.getExecutor());
        private static EventBus syncEventBus = new AsyncEventBus(MoreExecutors.directExecutor());

        public static EventBus getAsyncInstance(){
            return asyncEventBus;
        }

        public static EventBus getyncInstance(){
            return syncEventBus;
        }

    }
}

Note: MoreExecutors.directExecutor () thread pool appears to be, in fact, is single-threaded, look at the source code annotation:

Test code:

public class TestEventBus{

    public static void main(String[] args){
        EventBus eventBus = EventBusUtil.getEventBus();
        eventBus.register(new AObserver());
        eventBus.register(new BObserver());

        for (int j = 0; j < 2; j ++){
            eventBus.post("hi, observer" + j);
        }
    }
}

The following look at the implementation of guava:

1) registration of EventBus, any object can be registered as an observer

  /**
   * 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);
  }

 

All observers class approach to monitor events add a comment @Subscribe, registration time, Canada will look like this annotated method then register, see the following code

findAllSubscribers method

/** Registers all subscriber methods on the given listener object. */
  void register(Object listener) {
//查找所有包含@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);

      if (eventSubscribers == null) {/还没有注册观察者
        CopyOnWriteArraySet<Subscriber> newSet = new CopyOnWriteArraySet<>();
        eventSubscribers =
            MoreObjects.firstNonNull(subscribers.putIfAbsent(eventType, newSet), newSet);
      }

      eventSubscribers.addAll(eventMethodsInListener);
    }
  }

2) EventBus of notification

/**
   * 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));
    }
  }

As can be seen from the above code notified by dispatcher.dispatch method, the code of this method see the following codes:

@Override
    void dispatch(Object event, Iterator<Subscriber> subscribers) {
      checkNotNull(event);
      while (subscribers.hasNext()) {
        queue.add(new EventWithSubscriber(event, subscribers.next()));
      }

      EventWithSubscriber e;
      while ((e = queue.poll()) != null) {
        e.subscriber.dispatchEvent(e.event);
      }
    }

The above codes can be seen, the event message event viewer subscriber and packaged into a complicated object into the queue, and then let the observer trigger dequeue message processing.

/** Dispatches {@code event} to this subscriber using the proper executor. */
  final void dispatchEvent(final Object event) {
    executor.execute(
        new Runnable() {
          @Override
          public void run() {
            try {
              invokeSubscriberMethod(event);
            } catch (InvocationTargetException e) {
              bus.handleSubscriberException(e.getCause(), context(event));
            }
          }
        });
  }

Here's what we passed in the thread pool when declaring variables EventBus thread pool. The last event triggers using the reflection of java.

/**
   * Invokes the subscriber method. This method can be overridden to make the invocation
   * synchronized.
   */
  @VisibleForTesting
  void invokeSubscriberMethod(Object event) throws InvocationTargetException {
    try {
      method.invoke(target, checkNotNull(event));
    } catch (IllegalArgumentException e) {
      throw new Error("Method rejected target/argument: " + event, e);
    } catch (IllegalAccessException e) {
      throw new Error("Method became inaccessible: " + event, e);
    } catch (InvocationTargetException e) {
      if (e.getCause() instanceof Error) {
        throw (Error) e.getCause();
      }
      throw e;
    }
  }

Code analysis on here, guava detailed look at the code here:

https://github.com/google/guava

Sample code text see here

https://github.com/jinjunzhu/myguava.git

 

No personal welcome attention to the public, learn together, grow together

Published 33 original articles · won praise 2 · views 40000 +

Guess you like

Origin blog.csdn.net/zjj2006/article/details/104914398