January 26, 2021
Hot Topics:

Implement Automatic Discovery in Your Java Code with Annotations

  • By Jason Morris
  • Send Email »
  • More Articles »

If you need to start and stop various parts of code in a Java-based web system, you can implement a solution that uses a single ServletContentListener to listen for start and stop events from the container. This Listener can use the java.util.ServiceLoader to find registered classes that want to listen for these events.

That would work well enough, but what about adding a compile-time annotation processor? When you annotate a static method with @Lifecycle(LifecycleEvent.STARTUP), it'll be invoked at startup (and vice versa for SHUTDOWN). The processor generates classes and registers them for the ServiceLoader. You could leverage this same mechanism for any event-bus model: listeners are registered at compile time with annotations and then called automatically by the bus when the event is triggered. In essence, you can use annotations to automatically discover code at runtime with a ServiceLoader.

In action, the concept is as follows:

  1. You annotate methods with @EventListener (which may contain some meta-info).
  2. An annotation processor writes an EventDispatcher for each @EventListener method, including the filters required by the meta-info in the annotation.
  3. The event bus uses java.util.ServiceLoader to find EventDispatcher implementations.
  4. When EventBus.dispatch is called, any interested methods that were annotated with @EventListener are invoked.

This article demonstrates this concept by walking through the necessary steps for building an event bus that invokes annotated listener methods without any manual registration. The discussion starts with the EventBus, moves on to the annotation processor, and ends with a usage example.

Organizing Your Code

The code download for the example contains two separate IDE projects:
  • EventBus – contains the event bus and the annotation processor
  • EventBusExample – contains an example usage of the event bus

When dealing with annotation processors, you generally should turn off the "Compile on Save" (or equivalent) option in your IDE. These options have a nasty habit of deleting the classes generated by the annotation processor, leaving you scratching your head.

The following sections will explain how the code in these projects works, with snippets provided for illustration.

The Annotation and the Event

The first thing you need is an @EventListener annotation to mark methods that will listen for events. Here is an example of the EventListener annotation, which is allowed to annotate only methods. It will be discarded after the code compiles, because all the processing is done against the source code.
public @interface EventListener {
    String name() default ".*";
    Class<?> source() default Object.class;

Because this example is an event-bus model, it is best that the listener methods receive only the events they are interested in. To help enforce that rule, a BusEventObject class contains a name that you can filter (based on the name in the @EventListener annotation). To make it easier to filter events, the normal EventObject class has an added name field. The BusEventObject also serves as a marker for events that can be dispatched through the EventBus.

public abstract class BusEventObject extends EventObject {
    private final String name;
    public BusEventObject(
            final Object source,
            final String name) {
        if(name == null || name.isEmpty()) {
            throw new IllegalArgumentException("empty or null name");
        this.name = name;
    public String getName() {
        return name;

Page 1 of 3

This article was originally published on December 16, 2009

Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Thanks for your registration, follow us on our social networks to keep up-to-date