Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

The Why and How Behind Listener-Based Dependency Injection


advertisement
he Spring Framework introduced many people to the idea of dependency injection, the notion that instead of looking up objects you need to collaborate with, you simply have them injected into your class via JavaBean-style getters and setters. This article introduces a new, complimentary form of dependency injection that is based not on properties but on the idea of channels and callback listeners. Much like classic dependency injection (which I consider property-based), this listener-based injection has the ability to reduce boilerplate code, promote looser coupling, and help make code more testable and reusable.


In the same way that the JavaBean introspection used in dependency injection can infer properties by the presence of methods such as setPropertyName/getPropertyName, the listener introspection used in listener injection can infer communication channels on an object from methods such as addChannelName/removeChannelName, as the method signature defines the listener or callback interface. This article demonstrates how to use listener injection to clearly communicate relationships between POJOs using the Spring framework.

Spring-Based Injection Example
Suppose you have an application to which you want to introduce file-based input. So you need something that will poll a directory for files being added. Enter JPoller, an open source project that has all the capabilities you need. You decide to add it to your application using Spring, which makes it easy to define an instance of your poller as well as your listener class.

JPoller defines a PollManager callback interface. You provide an instance of this interface to receive the "file found" events. The basic configuration in Spring might look as follows:

<code> <span style="color:#0000ff"><?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"></span> <span style="color:teal"><</span><span style="color:#3F7F7F">bean</span> <span style="color:#7F007F">id</span>=<span style="color: #0000ff">"poller"</span> <span style="color:#7F007F">class</span>=<span style="color: #0000ff">"org.sadun.util.polling.ManagedDirectoryPoller"</span> <span style="color:#7F007F">init-method</span>=<span style="color: #0000ff">"startUp"</span> <span style="color:#7F007F">destroy-method</span>=<span style="color: #0000ff">"shutDown"</span><span style="color:teal">> <</span><span style="color:#3F7F7F">property</span> <span style="color:#7F007F">name</span>=<span style="color: #0000ff">"autoMove"</span> <span style="color:#7F007F">value</span>=<span style="color: #0000ff">"true"</span> <span style="color:teal">/> <</span><span style="color:#3F7F7F">property</span> <span style="color:#7F007F">name</span>=<span style="color: #0000ff">"pollInterval"</span> <span style="color:#7F007F">value</span>=<span style="color: #0000ff">"2000"</span> <span style="color:teal">/> <</span><span style="color:#3F7F7F">property</span> <span style="color:#7F007F">name</span>=<span style="color: #0000ff">"directories"</span><span style="color:teal">> <</span><span style="color:#3F7F7F">list</span><span style="color:teal">> <</span><span style="color:#3F7F7F">value</span><span style="color:teal">></span>/polldir<span style="color:teal"></ </span><span style="color:#3F7F7F">value</span><span style="color:#3F7F7F">> </</span><span style="color:#3F7F7F">list</span><span style="color:#3F7F7F">> </</span><span style="color:#3F7F7F">property</span><span style="color:#3F7F7F">> </</span><span style="color:#3F7F7F">bean</span><span style="color:#3F7F7F">> <</span><span style="color:#3F7F7F">bean</span> <span style="color:#7F007F">id</span>=<span style="color: #0000ff">"listener"</span> <span style="color:#7F007F">class</span>=<span style="color: #0000ff">"example1.Callback"</span> <span style="color:#3F7F7F">/> </</span><span style="color:#3F7F7F">beans</span><span style="color:#3F7F7F">></span> </code>

With an instance of your poller and your listener class defined, the only remaining issue is how you register your listener bean with the poller. Somewhere, your code has to call the ManagedDirectoryPoller.addPollManager() method to associate your listener with the polling object. Standard Spring effectively offers only three options for where to place this code:

  1. You could subclass ManagedDirectoryPoller and add an init or setter method that can be called from Spring for registration.
  2. You could add the code to your listener class and pass it a reference to the poller bean so that it could add itself.
  3. You could define a third class that knows about both the poller and the listener and adds them.

All three of these approaches requires writing code, and none of them would convey exactly where the addPollManager() method is invoked just from looking at the configuration.

Enter Listener-Based dependency injection
With listener-based dependency injection you can state this relationship explicitly. Listener injection introspects the communication channels on ManagedDirectoryPoller and identifies the addPollManager(PollManager) method as a defining messaging channel (named PollManager). Just as with JavaBeans introspection, you can then register your bean with this channel by providing an appropriate listener.

I've used Spring's namespace extensions to extend the Spring XML to support syntax for injecting listeners by channel name. The example application can now fully define the relationship between poller and callback listener (see Listing 1).

Notice the line in Listing 1 containing the tag. This injects the listener bean into the poller bean's PollManager channel. The process is analogous to injecting a property. The beauty of this is that it communicates the relationship between the objects clearly (you're also not forced to find a class off of which to hang the call to addPollManager()).

This is the basic usage of listener injection, which is enough for most people to immediately see the benefit, but there really is much, much more.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.