Singling Out to Spring

Last time, I wrote about why Singletons get a bad rap, and I wrote some high-level advice for moving away from them sanely. I want to cover a practical (if somewhat idealistic) example using Java and Spring to move a Singleton creation dependency out of a class.

First, a quick terminology aside. The term “Singleton” is actually drawn from set theory, where it means “a set with only one element”. The set of integers that equal 5 is a singleton. The analogy is that types (which are sets of values) that are Singletons also only have one value (the Singleton instance). This has caused a bit of confusion.

The Singleton pattern specifically refers to the object creational pattern where a class manages a single instance of itself and, usually, forbids other instances from existing. There’s also the “singleton” scope in Spring, which means something more limited: Spring will only create one instance of the bean. Don’t confuse the arguments that Singleton classes are a bad idea for arguments that objects you only have one of are a bad idea.

I mention this because Spring scopes come up in the following examples, and the default Spring scope is also called singleton. Now, with that out of the way, on with the show.

Moving a Singleton creation dependency to Spring

Injecting a singleton into its clients involves replacing code like this:

public class Mailer {
  private static final Mailer instance = new Mailer ();
 
  public static Mailer getInstance () { return instance; }
 
  private Mailer () { }
 
  public void sendMail (String recipient, String message) {
    System.out.printf ("I am sending %s to %s!\n",
      message, recipient);
  }
}
 
/* Using a Struts2 Action as an example "client" */
public class SendMailAction {
  public void setRecipient (String recipient) {
    this.recipient = recipient;
  }
 
  public void setMessage (String message) {
    this.message = message;
  }
 
  public void execute () {
    Mailer.getInstance ().sendMail (recipient, message);
  }
 
  private String message;
  private String recipient;
}

with code like this:

public class SendMailAction {
  public SendMailAction (Mailer mailer) {
    this.mailer = mailer;
  }
 
  public void setRecipient (String recipient) {
    this.recipient = recipient;
  }
 
  public void setMessage (String message) {
    this.message = message;
  }
 
  public void execute () {
    mailer.sendMail (recipient, message);
  }
 
  private final Mailer mailer;
  private String message;
  private String recipient;
}

The corresponding Spring configuration file looks like

<beans xmlns="http://www.springframework.org/schema/beans">
  <bean id="mailer"
        class="ca.grimoire.examples.Mailer"
        factory-method="getInstance" />
 
  <bean id="action"
        scope="request"
        class="ca.grimoire.examples.SendMailAction">
    <constructor-arg ref="mailer" />
  </bean>
 
  <!-- more definitions -->
</beans>

If getInstance, for some reason, needs parameters, they can be passed as elements nested inside its bean definition.

You can now write isolated tests for SendMailAction:

// Using JMock rather than a hand-crafted alternate implementation.
@RunWith(JMock.class)
public class SendMailTest {
  // This requires some extension to work with class mocks
  // See http://www.jmock.org/mocking-classes.html
  private final Mockery mockery = new JUnit4Mockery() {{
    setImposteriser(ClassImposteriser.INSTANCE);
  }};
 
  @Test
  public void sendsExpectedMessage () {
    final Mailer mailer = mockery.mock (Mailer.class);
 
    mockery.checking (new Expectations () {{
      oneOf (mailer).sendMail ("Recipient", "Hello World");
    }});
 
    SendMailAction action = new SendMailAction (mailer);
    action.setRecipient ("Recipient");
    action.setMessage ("Hello World");
 
    action.execute ();
  }
}

Prior to this change, writing a test for SendMailAction that did not also involve Mailer would’ve been difficult and error-prone.

Injecting dependencies into Singletons

You can also inject dependencies, including other Singleton instances, into a Singleton instance, which is helpful for breaking Singleton graphs. Let’s give Mailer another Singleton it depends on:

public class SmtpConnection {
  private static final SmtpConnection instance = new SmtpConnection ();
 
  public static SmtpConnection getInstance () { return instance; }
 
  public PrintStream getSmtpStream () {
    return System.out;
  }
}
 
public class Mailer {
  private static final Mailer instance = new Mailer ();
 
  public static Mailer getInstance () { return instance; }
 
  private Mailer () { }
 
  public void sendMail (String recipient, String message) {
    SmtpConnection connection = SmtpConnection.getInstance ();
    connection.getSmtpStream ().printf (
      "I am sending %s to %s!\n", message, recipient);
  }
}

You can move this dependency into Spring if you change Mailer:

public class Mailer {
  private static final Mailer instance = new Mailer ();
 
  public static Mailer getInstance () { return instance; }
 
  private Mailer () { }
 
  public void setConnection (SmtpConnection connection) {
    this.connection = connection;
  }
 
  public void sendMail (String recipient, String message) {
    connection.getSmtpStream ().printf (
      "I am sending %s to %s!\n", message, recipient);
  }
 
  private SmtpConnection connection;
}

The corresponding Spring configuration looks like:

<beans xmlns="http://www.springframework.org/schema/beans">
 
  <bean id="connection"
        class="ca.grimoire.examples.SmtpConnection"
        factory-method="getInstance" />
 
  <bean id="mailer"
        class="ca.grimoire.examples.Mailer"
        factory-method="getInstance">
    <property name="connection" ref="connection" />
  </bean>
 
  <!-- more definitions -->
</beans>

Even clients who aren’t using Spring to inject the Mailer Singleton can be used with this, so long as the Spring injection happens before anything tries to use the Mailer.

Without an interface limiting the operations, though, clients of Mailer may decide to call setConnectiondon’t do this because it becomes a form of mutable global state. If you’re going to follow this route, either force clients to use a narrower interface without the setters or be very careful about code review.

Edited at Mar 11, 2009 @ 10:43: Taavi pointed out that my SmtpConnection Spring example was still calling getInstance. Whoops!

No Comments

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a comment

Image | WordPress Themes