/* * Copyright (C) 2007 The Guava Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * The EventBus allows publish-subscribe-style communication between components * without requiring the components to explicitly register with one another * (and thus be aware of each other). It is designed exclusively to replace * traditional Java in-process event distribution using explicit registration. * It is not a general-purpose publish-subscribe system, nor is it * intended for interprocess communication. * *
See the Guava User Guide article on * {@code EventBus}. * *
Converting an existing EventListener-based system to use the EventBus is * easy. * *
To listen for a specific flavor of event (say, a CustomerChangeEvent)... *
To register your listener methods with the event producers... *
To listen for a common event supertype (such as EventObject or Object)... *
To listen for and detect events that were dispatched without listeners... *
To keep track of listeners to your events... *
To dispatch an event to listeners... *
The EventBus system and code use the following terms to discuss event * distribution: *
The Event Bus doesn't specify how you use it; there's nothing stopping your * application from having separate EventBus instances for each component, or * using separate instances to separate events by context or topic. This also * makes it trivial to set up and tear down EventBus objects in your tests. * *
Of course, if you'd like to have a process-wide EventBus singleton, * there's nothing stopping you from doing it that way. Simply have your * container (such as Guice) create the EventBus as a singleton at global scope * (or stash it in a static field, if you're into that sort of thing). * *
In short, the EventBus is not a singleton because we'd rather not make * that decision for you. Use it how you like. * *
We feel that the Event Bus's {@code @Subscribe} annotation conveys your * intentions just as explicitly as implementing an interface (or perhaps more * so), while leaving you free to place event subscriber methods wherever you wish * and give them intention-revealing names. * *
Traditional Java Events use a listener interface which typically sports * only a handful of methods -- typically one. This has a number of * disadvantages: *
The difficulties in implementing this cleanly has given rise to a pattern, * particularly common in Swing apps, of using tiny anonymous classes to * implement event listener interfaces. * *
Compare these two cases:
* class ChangeRecorder { * void setCustomer(Customer cust) { * cust.addChangeListener(new ChangeListener() { * void customerChanged(ChangeEvent e) { * recordChange(e.getChange()); * } * }; * } * } * * // Class is typically registered by the container. * class EventBusChangeRecorder { * @Subscribe void recordCustomerChange(ChangeEvent e) { * recordChange(e.getChange()); * } * }* *
The intent is actually clearer in the second case: there's less noise code, * and the event subscriber has a clear and meaningful name. * *
Some have proposed a generic {@code Subscriber Let's say the interface looked something like the following: Due to erasure, no single class can implement a generic interface more than
* once with different type parameters. This is a giant step backwards from
* traditional Java Events, where even if {@code actionPerformed} and {@code
* keyPressed} aren't very meaningful names, at least you can implement both
* methods!
*
* Some have freaked out about EventBus's {@code register(Object)} and {@code
* post(Object)} methods' use of the {@code Object} type.
*
* {@code Object} is used here for a good reason: the Event Bus library
* places no restrictions on the types of either your event listeners (as in
* {@code register(Object)}) or the events themselves (in {@code post(Object)}).
*
* Event subscriber methods, on the other hand, must explicitly declare their
* argument type -- the type of event desired (or one of its supertypes). Thus,
* searching for references to an event class will instantly find all subscriber
* methods for that event, and renaming the type will affect all subscriber methods
* within view of your IDE (and any code that creates the event).
*
* It's true that you can rename your {@code @Subscribed} event subscriber
* methods at will; Event Bus will not stop this or do anything to propagate the
* rename because, to Event Bus, the names of your subscriber methods are
* irrelevant. Test code that calls the methods directly, of course, will be
* affected by your renaming -- but that's what your refactoring tools are for.
*
* Nothing at all.
*
* The Event Bus was designed to integrate with containers and module
* systems, with Guice as the prototypical example. In these cases, it's
* convenient to have the container/factory/environment pass every
* created object to an EventBus's {@code register(Object)} method.
*
* This way, any object created by the container/factory/environment can
* hook into the system's event model simply by exposing subscriber methods.
*
* Any problem that can be unambiguously detected by Java's type system. For
* example, defining a subscriber method for a nonexistent event type.
*
* Immediately upon invoking {@code register(Object)} , the listener being
* registered is checked for the well-formedness of its subscriber methods.
* Specifically, any methods marked with {@code @Subscribe} must take only a
* single argument.
*
* Any violations of this rule will cause an {@code IllegalArgumentException}
* to be thrown.
*
* (This check could be moved to compile-time using APT, a solution we're
* researching.)
*
* If a component posts events with no registered listeners, it may
* indicate an error (typically an indication that you missed a
* {@code @Subscribe} annotation, or that the listening component is not loaded).
*
* (Note that this is not necessarily indicative of a problem. There
* are many cases where an application will deliberately ignore a posted event,
* particularly if the event is coming from code you don't control.)
*
* To handle such events, register a subscriber method for the {@code DeadEvent}
* class. Whenever EventBus receives an event with no registered subscribers, it
* will turn it into a {@code DeadEvent} and pass it your way -- allowing you to
* log it or otherwise recover.
*
* Because subscriber methods on your listener classes are normal methods, you can
* simply call them from your test code to simulate the EventBus.
*/
package com.google.common.eventbus;
{@code
* interface Subscriber
*
* Doesn't EventBus destroy static typing and eliminate automated
* refactoring support?
* What happens if I {@code register} a listener without any subscriber
* methods?
* What Event Bus problems can be detected at compile time?
* What Event Bus problems can be detected immediately at registration?
* What Event Bus problems may only be detected later, at runtime?
* How do I test event listeners and their subscriber methods?
*