Easy Event-Driven Application With Spring!

Suppose you are making an event-driven application. You have your listener interfaces and your event-generating objects. What is the most annoying part of getting this all to work?

Connecting your listeners to the event-generating objects. Every time you want some object to receive certain events, you have to register your listener with the correct producer object. This has some nasty effects on your code:

  • Either your listeners know to which object they are subscribing, or your event generators know who should be listening to their events
  • Due to this coupling, listeners and producers are difficult to test
  • Adding a new listener to your project requires some boilerplate code to get it working

Spring has a feature that can take care of all of this hassle: autowiring. For normal dependency injection, autowiring feels icky. It’s just too magical and leaves me with a feeling I am not in control. The great thing about autowiring is that it can be used on a per-method base.

Suppose you have a listener interface:

public interface MyEventListener
{
  public void onMyEvent(MyEvent me);
}

And suppose you have some class that implements this interface:

public class MyListener implements MyEventListener
{
  public void onMyEvent(MyEvent me)
  {
    System.out.println("My Event was raised!");
  }
}

Cool, so now we still need some event-generating class:

public class MyGenerator
{
  private MyEventListener[] listeners;

  public void generateEvent()
  {
    MyEvent me = new MyEvent();
    
    for (MyEventListener mel: listeners)
    {
      mel.onMyEvent(me);
    }
  }

  public void setMyEventListeners(MyEventListener[] listeners)
  {
    this.listeners = listeners;
  }
}

If you looked at the above class and wondered why I’ve made a setter for an array of listeners, instead of a method that registers a single listener, then prepare to be amazed. If you are familiar with Spring and autowiring, you probably know where I am headed :).

Neither the event listener, nor the event generator have any knowledge of each other. This is perfect, we can write unittests for each class without having to mock the other. However, the event generator still needs a reference to the listener for this thing to work. Let’s put these two objects in a Spring context together:

<beans ...>

  <bean name="myGenerator" class="MyEventGenerator"/>

  <bean name="myListener" class="MyListener"/>

</beans>

Great! Almost there, all we need now is some magic. The magic requires two components, one in the Spring config and one in the event generator class. The Spring config requires the <context:annotation-config/> tag, to enable the annotations:

<beans ...>

  <context:annotation-config/>

  ...

</beans>

And finally, the generator class will get its listeners autowired:

  @Autowire(required=false)
  public void setMyEventListeners(MyEventListener[] listeners)
  {
    this.listeners = listeners;
  }

Tada! The generator class will now automatically have an array of listeners injected into its setter. The Spring container selects all beans that are assignable to the array value type and passes them to the event generator. The required = false bit ensures Spring won’t throw an exception when you have no listeners configured.

If you now want more than one bean to listen to these events, all you have to do is add a bean to the Spring config that implements the MyEventListener interface and it will automatically get added to the array. Yes, it is really that simple.

Have fun!

–JH