Enterprise Java

Spring Framework: Three Spring Bean Lifecycle Techniques

When using the term ‘lifecycle’ the Guys at Spring are referring to the construction and destruction of your beans and usually this is in relation to the construction and destruction the Spring Context. There are those occassions when the management of your bean’s lifecycle is not a trivial task as there’s the need for it to perform its own internal set up. This is usually true when your bean has to interact with an external system including: loading a file, opening a socket or reading some data from the database. It doesn’t really matter what it is, to solve this problem all you need is for Spring to call your bean when it’s both loading the Spring Context and closing it down.
 
 
 
 
To that end Spring has three ways of calling your code during initialisation and shut down. These are:

  • Programmatically, usually called ‘interface callbacks’.
  • Declaratively on a per bean basis, called ‘method callbacks’.
  • Declaratively by applying the same default method callback to all beans.

Interface callbacks are something I’ve described before; however to summarise the technique and to ensure that Spring calls your bean during the setup and tear down of the Spring Context your bean has to implement a particular interface. In the initialisation case it’s InitializingBean and in the shutdown case it’s DisposableBean. If you need to know more about these techniques, then here is a blog on InitializingBean and another on DisposableBean.

I actually think that the name ‘Method callbacks’ is somewhat misleading as it doesn’t really describe what’s going on. What you’re doing when you use a method callback is adding a method to your bean, which you’re then reference in your XML config. When Spring reads the config it figures out that there’s a bean of type X with a method that it needs to call on startup and another it needs to call on shutdown.

What we need now is a scenario and because the one of the reasons for bean callback methods is so you can initialise external systems, I’m go going to suggest that you’re working for a direct marketing company and that you’ve been given the job of writing one of those annoying applications that dials random numbers in the middle of the night and plays a recorded message to the receiver telling them how they can obtain bucket loads of personal injury compensation, a.k.a cash, by suing some company for an accident they’ve never had.

The idea is that the Dialer is an external system that you have to write the controller for. When the contoller starts up it has to connect to the Dialer and when it shuts down, disconnect.

/**
   * Dial the number
   *
   * @param phoneNumber
   *            the phone number as a string
   * @return true if the number is dialed successfully
   */
  public boolean dial(String phoneNumber);

  /**
   * Play a message
   */
  public void playMessge();

  /**
   * Hang up the line...
   */
  public boolean hangUp();

The DialerController is defined by the interface above and as you’d expect it has a few telephone type methods, such as dial(...), playMessage() and hangUp(). The next thing to do is to create a bean that Implements these methods, which I’ve done below.

@Component
public class DialerControllerImpl implements DialerController {

  private boolean connected;

  @Override
  public boolean dial(String phoneNumber) {

    boolean retVal = false;
    if (isMiddleOfTheNight()) {
      testConnection();
      System.out.println("Dialing number: " + phoneNumber);
      retVal = true;
    }

    return retVal;
  }

  private boolean isMiddleOfTheNight() {
    return true;
  }

  @Override
  public void playMessge() {
    testConnection();
    System.out.println("Hello, do not hang up you may be entitled to...");
  }

  @Override
  public boolean hangUp() {
    testConnection();
    System.out.println("Hangup!");
    return true;
  }

  public void init() {
    connected = true;
    System.out.println("Connect to dialer");
  }

  public void destroy() {
    connected = false;
    System.out.println("Close connection to dialer");
  }

  private void testConnection() {
    if (connected == false) {
      throw new RuntimeException("Not connected to external system error");
    }
  }
}

The dial(...), playMessage() and hangUp() methods are nothing special; they check that the bean is connected to the external dialer it’s contolling and then does its job. The interesting point about this class are the init() and destroy() methods as these are the methods that we want Spring to call during startup and shutdown respectively.

To ensure that Spring does call our bean, we need to do some jiggery-pokery in the Spring config XML.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">


 <bean id="dialerController" class="example_2_lifecycle_management.method_based.DialerControllerImpl" init-method="init" destroy-method="destroy" />

</beans>

In this example, I’m using explicit bean configuration, (which means that you can ignore the @Component attribute in the code above as its not used for now, but it is needed later) and the thing to notice about the bean config is the additional attributes init-method and destroy-method. These are used to define the names of your bean’s methods that you want Spring to call when it’s initialising and shutting down. In this example they correspond to the init() and destroy() methods in the DialerControllerImpl class above.

@Test
  public void testLifeCycle_using_per_bean_declaration() {

    ctx = new ClassPathXmlApplicationContext("dialer.xml");
    ctx.registerShutdownHook();

    instance = ctx.getBean(DialerControllerImpl.class);

    if (instance.dial("555-1234")) {
      instance.playMessge();
      instance.hangUp();
    }
  }

The code above demonstrates a simple unit test that runs the code (it’s not a a real test as it doesn’t assert anything). The main point to note here is that after creating the Spring Application Context I’ve added a call
registerShutdownHook(). This is because you need to tell the JVM to tell Spring to call your destroy() method. You can create and handle the shutdown hook yourself as I’ve done in my DisposableBean blog, and sometimes there are advantages in doing this, but more on that another day.

The question I can hear now is ‘what if I’m using autowiring?’ It transpires that the Guys at Spring have added a new declarative method callback technique in Spring 3.1, called ‘default method callback’. The big idea here is that you declare initialisation and shutdown method names in the <beans/> element at the top of your XML config file as shown below:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:context="http://www.springframework.org/schema/context"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"
  default-init-method="init" 
  default-destroy-method="destroy">


 <!-- Enable autowiring -->
  <context:component-scan base-package="example_2_lifecycle_management.method_based" /> 
</beans>

When selecting you bean life cycle technique remember that the Guys at Spring recommend that you choose method based callbacks over interface based callbacks. The reason for this is that in choosing the interface callback route you tie your beans to Spring. This may, or may not be a problem and it all really depends on the rest your application as using many other Spring techniques will also tie your application to Spring.
 

Reference: Spring Framework: Three Spring Bean Lifecycle Techniques from our JCG partner Roger Hughes at the Captain Debug’s Blog blog.

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button