zoukankan      html  css  js  c++  java
  • 设计模式课程 设计模式精讲 21-3 观察者模式源码解析

    1    源码解析

    1.1    源码解析1(jdk中的应用:监听器是如何实现的)

    1.2    源码解析2(guava中的应用)

     

     

    1    源码解析
    1.1    源码解析1(jdk中的应用:监听器是如何实现的)

    a  监听器的实现方案是观察者模式(发布订阅模式)实现的一种

    b  前端给按钮添加事件也可以看作观察者模式的一种实现

    web.xml 调用listener

    (因为没有原教程的相关类)这里我随便拿出一个awt包下的Listener

    java.awt.event.MouseMotionListener

    package java.awt.event;
    
    import java.util.EventListener;
    
    
    public interface MouseMotionListener extends EventListener {
    
       
        public void mouseDragged(MouseEvent e);
    
        public void mouseMoved(MouseEvent e);
    
    }

     java.util.EventListener

    package java.util;
    
    
    public interface EventListener {
    }
    1.2    源码解析2(guava中的应用)

    为什么Set要重写equals方法和hashSet方法?

    https://www.cnblogs.com/1446358788-qq/articles/12322996.html

    测试类:

    package com.geely.design.pattern.behavioral.observer.guavatest;
    
    import com.google.common.eventbus.EventBus;
    
    /**
     * Created by geely
     */
    public class GuavaEventTest {
        public static void main(String[] args) {
            EventBus eventbus = new EventBus();
            GuavaEvent guavaEvent = new GuavaEvent();
            eventbus.register(guavaEvent);
            eventbus.post("post的内容");
        }
    
    }

    被监听类:

    package com.geely.design.pattern.behavioral.observer.guavatest;
    
    import com.google.common.eventbus.Subscribe;
    
    /**
     * Created by geely
     */
    public class GuavaEvent {
        @Subscribe
        /**
         * 通过注解:Subscribe 来监听该类,从而实现观察者。
         * 方法名不取subscribe 也可以,可以随意取名字
         */
        public void subscribe(String str){
            //业务逻辑
            System.out.println("执行subscribe方法,传入的参数是:" + str);
        }
    
    }

    EventBus(guava包自带类)(重点看注册方法:register,解除注册方法:unregister 和post方法subscribers:订阅者

    package com.google.common.eventbus;
    
    import static com.google.common.base.Preconditions.checkNotNull;
    
    import com.google.common.annotations.Beta;
    import com.google.common.base.MoreObjects;
    import com.google.common.util.concurrent.MoreExecutors;
    import java.lang.reflect.Method;
    import java.util.Iterator;
    import java.util.Locale;
    import java.util.concurrent.Executor;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    
    @Beta
    public class EventBus {
    
      private static final Logger logger = Logger.getLogger(EventBus.class.getName());
    
      private final String identifier;
      private final Executor executor;
      private final SubscriberExceptionHandler exceptionHandler;
    
      private final SubscriberRegistry subscribers = new SubscriberRegistry(this);
      private final Dispatcher dispatcher;
    
      /**
       * Creates a new EventBus named "default".
       */
      public EventBus() {
        this("default");
      }
    
      /**
       * Creates a new EventBus with the given {@code identifier}.
       *
       * @param identifier a brief name for this bus, for logging purposes. Should be a valid Java
       *     identifier.
       */
      public EventBus(String identifier) {
        this(
            identifier,
            MoreExecutors.directExecutor(),
            Dispatcher.perThreadDispatchQueue(),
            LoggingHandler.INSTANCE);
      }
    
      /**
       * Creates a new EventBus with the given {@link SubscriberExceptionHandler}.
       *
       * @param exceptionHandler Handler for subscriber exceptions.
       * @since 16.0
       */
      public EventBus(SubscriberExceptionHandler exceptionHandler) {
        this(
            "default",
            MoreExecutors.directExecutor(),
            Dispatcher.perThreadDispatchQueue(),
            exceptionHandler);
      }
    
      EventBus(
          String identifier,
          Executor executor,
          Dispatcher dispatcher,
          SubscriberExceptionHandler exceptionHandler) {
        this.identifier = checkNotNull(identifier);
        this.executor = checkNotNull(executor);
        this.dispatcher = checkNotNull(dispatcher);
        this.exceptionHandler = checkNotNull(exceptionHandler);
      }
    
      /**
       * Returns the identifier for this event bus.
       *
       * @since 19.0
       */
      public final String identifier() {
        return identifier;
      }
    
      /**
       * Returns the default executor this event bus uses for dispatching events to subscribers.
       */
      final Executor executor() {
        return executor;
      }
    
      /**
       * Handles the given exception thrown by a subscriber with the given context.
       */
      void handleSubscriberException(Throwable e, SubscriberExceptionContext context) {
        checkNotNull(e);
        checkNotNull(context);
        try {
          exceptionHandler.handleException(e, context);
        } catch (Throwable e2) {
          // if the handler threw an exception... well, just log it
          logger.log(
              Level.SEVERE,
              String.format(Locale.ROOT, "Exception %s thrown while handling exception: %s", e2, e),
              e2);
        }
      }
    
      /**
       * Registers all subscriber methods on {@code object} to receive events.
       * 执行此方法的时候,subscribers会 把对象注册进去,根据eventType 进行注册,
       * @param object object whose subscriber methods should be registered.
       */
      public void register(Object object) {
        subscribers.register(object);
      }
    
      /**
       * Unregisters all subscriber methods on a registered {@code object}.
       *
       * @param object object whose subscriber methods should be unregistered.
       * @throws IllegalArgumentException if the object was not previously registered.
       */
      public void unregister(Object object) {
        subscribers.unregister(object);
      }
    
      /**
       * 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.
       * 执行此方法的时候,会把参数传进来,subscribers根据eventType进行判断,如果有值的话,进行分发,循环调用。从而执行到观察者的方法。
       * @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));
        }
      }
    
      @Override
      public String toString() {
        return MoreObjects.toStringHelper(this).addValue(identifier).toString();
      }
    
      /**
       * Simple logging handler for subscriber exceptions.
       */
      static final class LoggingHandler implements SubscriberExceptionHandler {
        static final LoggingHandler INSTANCE = new LoggingHandler();
    
        @Override
        public void handleException(Throwable exception, SubscriberExceptionContext context) {
          Logger logger = logger(context);
          if (logger.isLoggable(Level.SEVERE)) {
            logger.log(Level.SEVERE, message(context), exception);
          }
        }
    
        private static Logger logger(SubscriberExceptionContext context) {
          return Logger.getLogger(EventBus.class.getName() + "." + context.getEventBus().identifier());
        }
    
        private static String message(SubscriberExceptionContext context) {
          Method method = context.getSubscriberMethod();
          return "Exception thrown by subscriber method "
              + method.getName()
              + '('
              + method.getParameterTypes()[0].getName()
              + ')'
              + " on subscriber "
              + context.getSubscriber()
              + " when dispatching event: "
              + context.getEvent();
        }
      }
    }

    SubscriberRegistery(EventBus所调的类):

    package com.google.common.eventbus;
    
    import static com.google.common.base.Preconditions.checkArgument;
    import static com.google.common.base.Preconditions.checkNotNull;
    
    import com.google.common.annotations.VisibleForTesting;
    import com.google.common.base.MoreObjects;
    import com.google.common.base.Objects;
    import com.google.common.base.Throwables;
    import com.google.common.cache.CacheBuilder;
    import com.google.common.cache.CacheLoader;
    import com.google.common.cache.LoadingCache;
    import com.google.common.collect.HashMultimap;
    import com.google.common.collect.ImmutableList;
    import com.google.common.collect.ImmutableSet;
    import com.google.common.collect.Iterators;
    import com.google.common.collect.Lists;
    import com.google.common.collect.Maps;
    import com.google.common.collect.Multimap;
    import com.google.common.reflect.TypeToken;
    import com.google.common.util.concurrent.UncheckedExecutionException;
    import com.google.j2objc.annotations.Weak;
    import java.lang.reflect.Method;
    import java.util.Arrays;
    import java.util.Collection;
    import java.util.Iterator;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    import java.util.concurrent.ConcurrentMap;
    import java.util.concurrent.CopyOnWriteArraySet;
    import javax.annotation.Nullable;
    
    /**
     * Registry of subscribers to a single event bus.
     *
     * @author Colin Decker
     */
    final class 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();
    
      /**
       * The event bus this registry belongs to.
       */
      @Weak private final EventBus bus;
    
      SubscriberRegistry(EventBus bus) {
        this.bus = checkNotNull(bus);
      }
    
      /**
       * 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);
        }
      }
    
      /**
       * Unregisters all subscribers on the given listener object.
       */
      void unregister(Object listener) {
        Multimap<Class<?>, Subscriber> listenerMethods = findAllSubscribers(listener);
    
        for (Map.Entry<Class<?>, Collection<Subscriber>> entry : listenerMethods.asMap().entrySet()) {
          Class<?> eventType = entry.getKey();
          Collection<Subscriber> listenerMethodsForType = entry.getValue();
    
          CopyOnWriteArraySet<Subscriber> currentSubscribers = subscribers.get(eventType);
          if (currentSubscribers == null || !currentSubscribers.removeAll(listenerMethodsForType)) {
            // if removeAll returns true, all we really know is that at least one subscriber was
            // removed... however, barring something very strange we can assume that if at least one
            // subscriber was removed, all subscribers on listener for that event type were... after
            // all, the definition of subscribers on a particular class is totally static
            throw new IllegalArgumentException(
                "missing event subscriber for an annotated method. Is " + listener + " registered?");
          }
    
          // don't try to remove the set if it's empty; that can't be done safely without a lock
          // anyway, if the set is empty it'll just be wrapping an array of length 0
        }
      }
    
      @VisibleForTesting
      Set<Subscriber> getSubscribersForTesting(Class<?> eventType) {
        return MoreObjects.firstNonNull(subscribers.get(eventType), ImmutableSet.<Subscriber>of());
      }
    
      /**
       * Gets an iterator representing an immutable snapshot of all subscribers to the given event at
       * the time this method is called.
       */
      Iterator<Subscriber> getSubscribers(Object event) {
        ImmutableSet<Class<?>> eventTypes = flattenHierarchy(event.getClass());
    
        List<Iterator<Subscriber>> subscriberIterators =
            Lists.newArrayListWithCapacity(eventTypes.size());
    
        for (Class<?> eventType : eventTypes) {
          CopyOnWriteArraySet<Subscriber> eventSubscribers = subscribers.get(eventType);
          if (eventSubscribers != null) {
            // eager no-copy snapshot
            subscriberIterators.add(eventSubscribers.iterator());
          }
        }
    
        return Iterators.concat(subscriberIterators.iterator());
      }
    
      /**
       * A thread-safe cache that contains the mapping from each class to all methods in that class and
       * all super-classes, that are annotated with {@code @Subscribe}. The cache is shared across all
       * instances of this class; this greatly improves performance if multiple EventBus instances are
       * created and objects of the same class are registered on all of them.
       */
      private static final LoadingCache<Class<?>, ImmutableList<Method>> subscriberMethodsCache =
          CacheBuilder.newBuilder()
              .weakKeys()
              .build(
                  new CacheLoader<Class<?>, ImmutableList<Method>>() {
                    @Override
                    public ImmutableList<Method> load(Class<?> concreteClass) throws Exception {
                      return getAnnotatedMethodsNotCached(concreteClass);
                    }
                  });
    
      /**
       * 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;
      }
    
      private static ImmutableList<Method> getAnnotatedMethods(Class<?> clazz) {
        return subscriberMethodsCache.getUnchecked(clazz);
      }
    
      private static ImmutableList<Method> getAnnotatedMethodsNotCached(Class<?> clazz) {
        Set<? extends Class<?>> supertypes = TypeToken.of(clazz).getTypes().rawTypes();
        Map<MethodIdentifier, Method> identifiers = Maps.newHashMap();
        for (Class<?> supertype : supertypes) {
          for (Method method : supertype.getDeclaredMethods()) {
            if (method.isAnnotationPresent(Subscribe.class) && !method.isSynthetic()) {
              // TODO(cgdecker): Should check for a generic parameter type and error out
              Class<?>[] parameterTypes = method.getParameterTypes();
              checkArgument(
                  parameterTypes.length == 1,
                  "Method %s has @Subscribe annotation but has %s parameters."
                      + "Subscriber methods must have exactly 1 parameter.",
                  method,
                  parameterTypes.length);
    
              MethodIdentifier ident = new MethodIdentifier(method);
              if (!identifiers.containsKey(ident)) {
                identifiers.put(ident, method);
              }
            }
          }
        }
        return ImmutableList.copyOf(identifiers.values());
      }
    
      /**
       * Global cache of classes to their flattened hierarchy of supertypes.
       */
      private static final LoadingCache<Class<?>, ImmutableSet<Class<?>>> flattenHierarchyCache =
          CacheBuilder.newBuilder()
              .weakKeys()
              .build(
                  new CacheLoader<Class<?>, ImmutableSet<Class<?>>>() {
                    // <Class<?>> is actually needed to compile
                    @SuppressWarnings("RedundantTypeArguments")
                    @Override
                    public ImmutableSet<Class<?>> load(Class<?> concreteClass) {
                      return ImmutableSet.<Class<?>>copyOf(
                          TypeToken.of(concreteClass).getTypes().rawTypes());
                    }
                  });
    
      /**
       * Flattens a class's type hierarchy into a set of {@code Class} objects including all
       * superclasses (transitively) and all interfaces implemented by these superclasses.
       */
      @VisibleForTesting
      static ImmutableSet<Class<?>> flattenHierarchy(Class<?> concreteClass) {
        try {
          return flattenHierarchyCache.getUnchecked(concreteClass);
        } catch (UncheckedExecutionException e) {
          throw Throwables.propagate(e.getCause());
        }
      }
    
      private static final class MethodIdentifier {
    
        private final String name;
        private final List<Class<?>> parameterTypes;
    
        MethodIdentifier(Method method) {
          this.name = method.getName();
          this.parameterTypes = Arrays.asList(method.getParameterTypes());
        }
    
        @Override
        public int hashCode() {
          return Objects.hashCode(name, parameterTypes);
        }
    
        @Override
        public boolean equals(@Nullable Object o) {
          if (o instanceof MethodIdentifier) {
            MethodIdentifier ident = (MethodIdentifier) o;
            return name.equals(ident.name) && parameterTypes.equals(ident.parameterTypes);
          }
          return false;
        }
      }
    }
  • 相关阅读:
    继承与多态,Instanceof关键字
    面向对象,单例模式
    方法
    数组
    流程控制
    基础语法
    连接linux四大远程工具
    MYSQL-索引的设计
    银行一类(Ⅰ类)、二类(Ⅱ类)、三类(Ⅲ类)账户区别是什么?
    真正有效的学习
  • 原文地址:https://www.cnblogs.com/1446358788-qq/p/12320973.html
Copyright © 2011-2022 走看看