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; } } }
诸葛