Guice Listeners

Categories: Java

Guice Injection and the Listener Pattern

Introduction

Google Guice (pronounced “juice”) is a fantastic library for applying the Dependency Injection technique to Java code.

The Observer pattern is a common pattern for decoupling a source of “events” from the objects that respond to those events. This is also known as the listener pattern, or the publish-subscribe pattern.

This short article shows a convenient way to use Guice to wire up subscribers to their appropriate publisher objects.

Note that this works only when the “subscriber” interface is unique to a particular publisher class; it doesn’t work for something like “MouseListener” for example, where multiple classes might generate events of that type. However variants of this can work by requiring relevant subscribers to have a specific annotation or similar marker.

The standard Guice “multi-bindings” extension does something similar. However this approach:

  • does not require an extra jarfile, and

  • allows control over the order in which subscribers are registered; this is the same order in which bindings are registered with Guice.

===============

To implement this functionality in your own app, you will need utility class GuiceSubclassMatcher.

In addition, libraries guice-2.0.jar and aopalliance-1.0.jar are needed.

A full example is available for download here.

The core logic is in the PublisherModule class of this example:


  @Override
  protected void configure()
  {
    Matcher<? super TypeLiteral<?>> matcher = new GuiceSubclassMatcher<TypeLiteral<?>>(MyPublisher.Subscriber.class);
    bindListener(matcher, subscriberRegistry);
  }

The bindListener call ensures that the subscriberRegistry object gets a callback to its “hear” method for each binding that Guice knows of which satisfies the specified matcher object. The callbacks occur both for bindings known before the bindListener call is made, and for bindings that are discovered after the bindListener call.

The GuiceSubclassMatcher class is a custom class that returns true for every class which is a subtype of the specified class. In this case, our desired subscriber interface is specified. The source code for GuiceSubclassMatcher can be found in the example jarfile.

The subscriberRegistry simply creates a Provider object for each Subscriber subclass encountered, for later use when MyPublisher instances are created:


  static class SubscriberRegistry implements TypeListener
  {
    /** List of providers for all Listener subclasses known to Guice. */
    private ArrayList<Provider<? extends MyPublisher.Subscriber>> providers = 
      new ArrayList<Provider<? extends MyPublisher.Subscriber>>(10);

    /**
     * As each Subscriber subclass is found by Guice, store a provider for that type in the providers list.
     */
    public <I> void hear(TypeLiteral<I> paramTypeLiteral, TypeEncounter<I> paramTypeEncounter)
    {
      // This cast is ugly; there is probably a way to avoid it using correct generics signatures....
      @SuppressWarnings("unchecked")
      Class<? extends MyPublisher.Subscriber> clazz = (Class<? extends MyPublisher.Subscriber>) paramTypeLiteral.getType();
      Provider<? extends MyPublisher.Subscriber> prov = paramTypeEncounter.getProvider(clazz);
      providers.add(prov);
    }
  }

A simple Provider method for the MyPublisher class then creates the instance and populates it with all known subscribers:


  @Provides
  @Singleton
  MyPublisher getMyPublisher()
  {
    MyPublisher p = new MyPublisher();
    for (Provider<? extends MyPublisher.Subscriber> provider : subscriberRegistry.providers)
    {
      MyPublisher.Subscriber subscriber = provider.get();
      p.subscribe(subscriber);
    }
    return p;
  }

References

  • http://code.google.com/p/google-guice/

  • http://code.google.com/p/google-guice/wiki/Multibindings