Bean Lifecycle Management

An important part of any IoC container, Spring included, is that beans can be constructed in such a way that they receive notifications at certain points in their lifecycle. This enables your beans to perform relevant processing at certain points throughout their life. The number of possible notifications is huge, but in general, two lifecycle events are particularly relevant to a bean: post-initialization and pre-destruction.

In the context of Spring, the post-initialization event is raised as soon as Spring finishes setting all the property values on the bean and finishes any dependency checks that you configured it to perform. The pre-destruction event is fired just before Spring destroys the bean instance. Both of these lifecycle events are only fired for beans that are singletons. Spring doesn't manage the lifecycle of beans that are configured as non-singletons.

Spring provides two mechanisms a bean can use to hook into each of these events and perform some additional processing: interface-based or method-based. Using the interface- based mechanism, your bean implements an interface specific to the type of notification it wishes to receive, and Spring notifies the bean via a callback method defined in the interface. For the method-based mechanism, Spring allows you to specify, in your BeanFactory configu- ration, the name of a method to call when the bean is initialized and the name of a method to call when the bean is destroyed.

In the case of both events, the mechanisms achieve exactly the same goal. The interface mechanism is used extensively throughout Spring so that you don't have to remember to specify the initialization or destruction each time you use one of Spring's components. However, in your own beans, you may be better served using the method-based mechanism because your beans do not need to implement any Spring-specific interfaces. Although we stated that portability often isn't as important a requirement as many texts lead you to believe, this does not mean you should sacrifice portability when a perfectly good alternative exists. That said, if you are coupling your application to Spring in other ways, using the interface method allows you to specify the callback once and then forget about it. If you are defining a lot of beans of the same type that need to take advantage of the lifecycle notifications, then using the interface mechanism is the perfect way to avoid needing to repeat yourself—an extra coding burden that can lead to hard-to-diagnose errors and an application that is problematic to maintain.

Overall, the choice of which mechanism you use for receiving lifecycle notifications depends on your application requirements. If you are concerned about portability or you are just defining one or two beans of a particular type that need the callbacks, then use the method- based mechanism. If you are not too concerned about portability or you are defining many beans of the same type that need the lifecycle notifications, then using the interface-based mechanism is the best way to ensure that your beans always receive the notifications they are expecting. If you plan to use a bean across many different Spring projects, then you almost certainly want the functionality of that bean to be as self-contained as possible, so you should definitely use the interface-based mechanism.

Hooking into Bean Creation

By being aware of when it is initialized, a bean can check to see whether all its required dependencies are satisfied. Although Spring can check dependencies for you, it is pretty much an allor-nothing approach, and it doesn't offer any opportunities for applying additional logic to the dependency resolution procedure. Consider a bean that has four dependencies declared as setters, two of which are required and one of which has a suitable default in the event that no dependency is provided. Using an initialization callback, your bean can check for the dependencies it requires, throwing an exception or providing a default as needed.

A bean cannot perform these checks in its constructor because at this point, Spring has not had an opportunity to provide values for the dependencies it can satisfy. The initialization call- back in Spring is called after Spring finishes providing the dependencies that it can and performs any dependency checks that you ask it to.

You are not limited to using the initialization callback just to check dependencies; you can do anything you want in the callback, but it is most useful for the purpose we have described. In many cases, the initialization callback is also the place to trigger any actions that your bean must take automatically in response to its configuration. For instance, if you build a bean to run scheduled tasks, the initialization callback provides the ideal place to start the scheduler— after all, the configuration data is set on the bean.

Note 

You will not have to write a bean to run scheduled tasks because this is something Spring can do automatically through its integration with the Quartz scheduler. We cover this in more detail in Chapter 14.

Specifying an Initialization Method

As we mentioned previously, one way to receive the initialization callback is to designate a method on your bean as an initialization method and tell Spring to use this method as an initialization method. As discussed, this callback mechanism is useful when you only have a few beans of the same type or when you want to keep your application decoupled from Spring. Another reason for using this mechanism is to enable your Spring application to work with beans that were built previously or were provided by third-party vendors.

Specifying a callback method is simply a case of specifying the name in the init-method attribute of a bean's <bean> tag. Listing 5-1 shows a basic bean with two dependencies.

Listing 5-1: The SimpleBean Class

image from book
package com.apress.prospring.ch5.lifecycle;      import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.FileSystemResource;      public class SimpleBean {          private static final String DEFAULT_NAME = "Luke Skywalker";          private String name = null;          private int age = Integer.MIN_VALUE;          public void setName(String name) {         this.name = name;     }          public void setAge(int age) {         this.age = age;     }          public void init() {         System.out.println("Initializing bean");              if (name == null) {             System.out.println("Using default name");             name = DEFAULT_NAME;         }              if (age == Integer.MIN_VALUE) {             throw new IllegalArgumentException(                     "You must set the age property of any beans of type "                                            + SimpleBean.class);         }     }          public String toString() {         return "Name: " + name + "\nAge: " + age;     }          public static void main(String[] args) {         BeanFactory factory = new XmlBeanFactory(new FileSystemResource(                 "./ch5/src/conf/lifecycle/initMethod.xml"));              SimpleBean simpleBean1 = getBean("simpleBean1", factory);                 SimpleBean simpleBean2 = getBean("simpleBean2", factory);         SimpleBean simpleBean3 = getBean("simpleBean3", factory);     }          private static SimpleBean getBean(String beanName, BeanFactory factory) {         try {             SimpleBean bean =(SimpleBean) factory.getBean(beanName);             System.out.println(bean);             return bean;         } catch (BeanCreationException ex) {             System.out.println("An error occured in bean configuration: "                     + ex.getMessage());             return null;         }     }      }
image from book

Notice that we have defined a method, init(), to act as the initialization callback. The init() method checks to see if the name property has been set, and if it has not, it uses the default value stored in the DEFAULT_VALUE constant. The init() method also checks to see if the age property is set and throws an IllegalArgumentException if it is not.

The main() method of the SimpleBean class attempts to obtain three beans from the BeanFactory, all of type SimpleBean, using its own getBean() method. Notice that in the getBean() method, if the bean is obtained successfully, then its details are written to stdout. If an exception is thrown in the init() method, as will occur in this case if the age property is not set, then Spring wraps that exception in a BeanCreationException. The getBean() method catches these exceptions and writes a message to stdout informing us of the error.

Listing 5-2 shows a BeanFactory configuration that defines the beans used in Listing 5-1.

Listing 5-2: Configuring the SimpleBeans

image from book
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd"> <beans>     <bean                         init-method="init">         <property name="name">             <value>Rob Harrop</value>         </property>         <property name="age">             <value>100</value>         </property>     </bean>     <bean                         init-method="init">         <property name="age">             <value>100</value>         </property>     </bean>     <bean                         init-method="init">         <property name="name">             <value>Rob Harrop</value>         </property>     </bean> </beans>
image from book

As you can see, the <bean> tag for each of the three beans has an init-method attribute that tells Spring that it should invoke the init() method as soon as it finishes configuring the bean. The simpleBean1 bean has values for both the name and age properties, so it passes through the init() method with absolutely no changes. The simpleBean2 bean has no value for the name property, meaning that in the init() method, the name property is given the default value. Finally, the simpleBean3 bean has no value for the age property. The logic defined in the init() method treats this as an error so an IllegalArgumentException is thrown. Running this example yields the following output:

Initializing bean Name: Rob Harrop Age: 100 Initializing bean Using default name Name: Luke Skywalker Age: 100 Initializing bean An error occured in bean configuration: ¿ Error creating bean with name 'simpleBean3' defined in file ¿ [D:\projects\pro-spring\.\ch5\src\conf\lifecycle.xml]: ¿ Initialization method 'init' threw exception; ¿ nested exception is java.lang.IllegalArgumentException: ¿ You must set the age property of any beans of type class ¿ com.apress.prospring.ch5.lifecycle.SimpleBean

From this output you can see that simpleBean1 was configured correctly with the values that we specified in the configuration file. For simpleBean2, the default value for the name property was used because no value was specified in the configuration. Finally, for simpleBean3, no bean instance was created because the init() method raised an error due to the lack of a value for the age property.

As you can see, using the initialization method is an ideal way to ensure that your beans are configured correctly. By using this mechanism, you can take full advantage of the benefits of IoC without losing any of the control you get from manually defining dependencies. The only constraint on your initialization method is that it cannot accept any arguments. You can define any return type, although it is ignored by Spring, and you can even use a static method, but the method must accept no arguments.

The benefits of this mechanism are negated when using a static initialization method, because you cannot access any of the bean's state to validate it. If your bean is using static state as a mechanism for saving memory and you are using a static initialization method to validate this state, then you should consider moving the static state to instance state and using a non- static initialization method. If you use Spring's singleton management capabilities, the end effect is the same, but you have a bean that is much simpler to test and you also have the increased effect of being able to create multiple instances of the bean with their own state when necessary. Of course, there are instances in which you need to use static state shared across multiple instances of a bean, in which case, you can always use a static initialization method.

Implementing the InitializingBean Interface

The InitializingBean interface defined in Spring allows you to define inside your bean code that you want the bean to receive notification that Spring has finished configuring it. In the same way as when you are using an initialization method, this gives you the opportunity to check the bean configuration to ensure that it is valid, providing any default values along the way.

The InitializingBean interface defines a single method, afterPropertiesSet(), that serves the same purpose as the init() method in Listing 5-1. Listing 5-3 shows a reimplementation of the previous example using the InitializingBean interface in place of the initialization method.

Listing 5-3: Using the InitializingBean Interface

image from book
package com.apress.prospring.ch5.lifecycle;      import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.FileSystemResource;      public class SimpleBeanWithInterface implements InitializingBean {          private static final String DEFAULT_NAME = "Luke Skywalker";          private String name = null;          private int age = Integer.MIN_VALUE;          public void setName(String name) {         this.name = name;     }          public void setAge(int age) {         this.age = age;     }          public void afterPropertiesSet() throws Exception {         System.out.println("Initializing bean");              if (name == null) {             System.out.println("Using default name");             name = DEFAULT_NAME;         }              if (age == Integer.MIN_VALUE) {             throw new IllegalArgumentException(                     "You must set the age property of any beans of type " +                       SimpleBean.class);         }     }          public String toString() {         return "Name: " + name + "\nAge: " + age;     }          public static void main(String[] args) {         BeanFactory factory = new XmlBeanFactory(new FileSystemResource(                 "./ch5/src/conf/lifecycle/initInterface.xml"));              SimpleBeanWithInterface simpleBean1 = getBean("simpleBean1", factory);         SimpleBeanWithInterface simpleBean2 = getBean("simpleBean2", factory);         SimpleBeanWithInterface simpleBean3 = getBean("simpleBean3", factory);     }          private static SimpleBeanWithInterface getBean(String beanName,             BeanFactory factory) {         try {             SimpleBeanWithInterface bean =                                 (SimpleBeanWithInterface) factory.getBean(beanName);             System.out.println(bean);             return bean;         } catch (BeanCreationException ex) {             System.out.println("An error occured in bean configuration: "                     + ex.getMessage());             return null;         }     } }
image from book

As you can see, not much in this example has changed. Aside from the obvious class name change, the only difference is that this class implements InitializingBean and the initialization logic has moved into the InitializingBean.afterPropertiesSet() method.

In Listing 5-4, you can see the configuration for this example.

Listing 5-4: Configuration Using InitializingBean

image from book
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd"> <beans>     <bean             >         <property name="name">             <value>Rob Harrop</value>         </property>         <property name="age">             <value>100</value>         </property>     </bean>     <bean             >         <property name="age">             <value>100</value>         </property>     </bean>     <bean             >         <property name="name">             <value>Rob Harrop</value>         </property>     </bean> </beans>
image from book

Again, not much of a difference between the configuration code in Listing 5-4 and the configuration code from Listing 5-2. The noticeable difference is the omission of the init-method attribute. Because the SimpleBeanWithInterface class implements the InitializingBean inter- face, Spring knows which method to call as the initialization callback, thus removing the need for any additional configuration. The output from this example is shown here:

Initializing bean Name: Rob Harrop Age: 100 Initializing bean Using default name Name: Luke Skywalker Age: 100 Initializing bean An error occured in bean configuration: ¿ Error creating bean with name 'simpleBean3' defined in file ¿ [D:\projects\pro-spring\.\ch5\src\conf\lifecycle\initInterface.xml]: ¿ Initialization of bean failed; ¿ nested exception is java.lang.IllegalArgumentException: ¿ You must set the age property of any beans of type class ¿ com.apress.prospring.ch5.lifecycle.SimpleBean 

As you can see, there is no difference in the output of the two examples; both work in exactly the same way. As we discussed earlier, both approaches have their benefits and drawbacks. Using an initialization method, you have the benefit of keeping your application decoupled from Spring, but you have to remember to configure the initialization method for every bean that needs it. Using InitializingBean, you have the benefit of being able to specify the initialization callback once for all instances of your bean class, but you have to couple your application to do so. In the end, you should let the requirements of your application drive the decision about which approach to use. If portability is an issue, then use the initialization method, otherwise use the InitializingBean interface to reduce the amount of configuration your application needs and the chance of errors creeping into your application due to misconfiguration.

Order of Resolution

You can use both an initialization method and the InitializingBean on the same bean instance. In this case, Spring invokes InitializingBean.afterPropertiesSet() first, followed by your initialization method. This can be useful if you have an existing bean that performs some initialization in a specific method, but you need to add some more initialization code when you use Spring. However, a better approach is to call your bean's initialization method from afterPropertiesSet(). This way, if Spring changes the initialization order in a future release, your code continues to work as it should.

Hooking into Bean Destruction

When using a BeanFactory implementation that implements the ConfigurableListableBeanFactory interface (such as XmlBeanFactory), you can signal the BeanFactory that you want to destroy all singleton instances with a call to destroySingletons(). Typically, you do this when your application shuts down, and it allows you to clean up any resources that your beans might be holding open, thus allowing your application to shut down gracefully. This callback also provides the perfect place to flush any data you are storing in memory to persistent storage and to allow your beans to end any long-running processes they may have started.

To allow your beans to receive notification that destroySingletons() has been called, you have two options, both similar to the mechanisms available for receiving an initialization call- back. The destruction callback is often used in conjunction with the initialization callback. In many cases, you create and configure a resource in the initialization callback and then release the resource in the destruction callback.

Executing a Method When a Bean Is Destroyed

To designate a method to be called when a bean is destroyed, you simply specify the name of the method in the destroy-method attribute of the bean's <bean> tag. Spring calls it just before it destroys the singleton instance of the bean. Listing 5-5 shows a simple class that implements InitializingBean, and in the afterPropertiesSet() method, it creates an instance of FileInputStream and stores this in a private field.

Listing 5-5: Using a destroy-method Callback

image from book
package com.apress.prospring.ch5.lifecycle;      import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream;      import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.FileSystemResource;      public class DestructiveBean implements InitializingBean {          private InputStream is = null;          public String filePath = null;          public void afterPropertiesSet() throws Exception {              System.out.println("Initializing Bean");              if (filePath == null) {             throw new IllegalArgumentException(                     "You must specify the filePath property of " +                             DestructiveBean.class);         }              is = new FileInputStream(filePath);     }          public void destroy() {              System.out.println("Destroying Bean");              if (is != null) {             try {                 is.close();                 is = null;             } catch (IOException ex) {                 System.err.println("WARN: An IOException occured"                         + " trying to close the InputStream");             }         }     }          public void setFilePath(String filePath) {         this.filePath = filePath;     }          public static void main(String[] args) throws Exception {         ConfigurableListableBeanFactory factory = new XmlBeanFactory(                 new FileSystemResource(                         "./ch5/src/conf/lifecycle/disposeMethod.xml"));                       DestructiveBean bean = (DestructiveBean)                                          factory.getBean("destructiveBean");                   System.out.println("Calling destroySingletons()");         factory.destroySingletons();         System.out.println("Called destroySingletons()");     } }
image from book

This code also defines a destroy() method, in which the FileInputStream is closed and set to null, releasing the resource and allowing it to be garbage collected. The main() method retrieves a bean of type DestructiveBean from the XmlBeanFactory and then invokes ConfigurableListableBeanFactory.destroySingletons(), instructing Spring to destroy all the singletons it is managing. Both the initialization and destruction callbacks write a message to stdout informing us that they have been called. In Listing 5-6, you can see the configuration for the destructiveBean bean.

Listing 5-6: Configuring a destroy-method Callback

image from book
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd"> <beans>     <bean                        destroy-method="destroy">         <property name="filePath">             <value>d:/tmp/test.txt</value>         </property>     </bean> </beans>
image from book

Notice that we have specified the destroy() method as the destruction callback using the destroy-method attribute. Running this example yields the following output:

Initializing Bean Calling destroySingletons() Destroying Bean Called destroySingletons() 

As you can see, Spring first invokes the initialization callback, and the DestructiveBean instance creates the FileInputStream instance and stores it. Next, during the call to destroySingletons(), Spring iterates over the set of singletons it is managing, in this case just one, and invokes any destruction callbacks that are specified. This is where the DestructiveBean instance closes the FileInputStream and sets the reference to null.

Implementing the DisposableBean Interface

As with initialization callbacks, Spring provides an interface, in this case DisposableBean, that can be implemented by your beans as a mechanism for receiving destruction callbacks. The DisposableBean interface defines a single method, destroy(), which is called just before the bean is destroyed. Using this mechanism is orthogonal to using the InitializingBean interface to receive initialization callbacks. Listing 5-7 shows a modified implementation of the DestructiveBean class that implements the DisposableBean interface.

Listing 5-7: Implementing DisposableBean

image from book
package com.apress.prospring.ch5.lifecycle;      import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream;      import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.FileSystemResource;      public class DestructiveBeanWithInterface implements InitializingBean,         DisposableBean {          private InputStream is = null;          public String filePath = null;          public void afterPropertiesSet() throws Exception {              System.out.println("Initializing Bean");              if (filePath == null) {             throw new IllegalArgumentException(                     "You must specify the filePath property of " +                              DestructiveBean.class);         }              is = new FileInputStream(filePath);     }          public void destroy() {              System.out.println("Destroying Bean");              if (is != null) {             try {                 is.close();                 is = null;             } catch (IOException ex) {                 System.err.println("WARN: An IOException occured"                         + " trying to close the InputStream");             }         }     }          public void setFilePath(String filePath) {         this.filePath = filePath;     }          public static void main(String[] args) throws Exception {         final ConfigurableListableBeanFactory factory = new XmlBeanFactory(                 new FileSystemResource(                         "./ch5/src/conf/lifecycle/disposeInterface.xml"));              DestructiveBeanWithInterface bean =              (DestructiveBeanWithInterface)factory.getBean("destructiveBean");              System.out.println("Calling destroySingletons()");         factory.destroySingletons();         System.out.println("Called destroySingletons()");          }      }
image from book

Again, there is not much difference between the code that uses the callback method mechanism and the code that uses the callback interface mechanism. In this case, we even used the same method names. Listing 5-8 shows an amended configuration for this example.

Listing 5-8: Configuration Using the DisposableBean Interface

image from book
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd"> <beans>     <bean             >         <property name="filePath">             <value>d:/tmp/test.txt</value>         </property>     </bean> </beans>
image from book

As you can see, aside from the different class name, the only difference is the omission of the destroy-method attribute.

Running this example yields the following output:

Initializing Bean Calling destroySingletons() Destroying Bean Called destroySingletons()

Again, the output from the two different mechanisms is exactly the same. The destruction callback is an ideal mechanism for ensuring that your applications shut down gracefully and do not leave resources open or in an inconsistent state. However, you still have to decide whether to use the destruction method callback or the DisposableBean interface. Again, let the requirements of your application drive your decision in this respect; use the method callback where portability is an issue, otherwise use the DisposableBean interface to reduce the amount of configuration required.

Using a Shutdown Hook

The only drawback of the destruction callbacks in Spring is that they are not fired automatically; you need to remember to call destroySingletons() before your application is closed. When your application runs as a servlet, you can simply call destroySingletons() in the servlet's destroy() method. However, in a stand-alone application, things are not quite so simple, especially if you have multiple exit points out of your application. Fortunately, there is a solution. Java allows you to create a shutdown hook, a thread that is executed just before the application shuts down. This is the perfect way to invoke the destroySingletons() method of your BeanFactory. The easiest way to take advantage of this mechanism is to create a class that implements the Runnable interface and have the run() method call destroySingletons(). This is shown in Listing 5-9.

Listing 5-9: Implementing a Shutdown Hook

image from book
package com.apress.prospring.ch5.lifecycle;      import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;      public class ShutdownHook implements Runnable {          private ConfigurableListableBeanFactory factory;          public ShutdownHook(ConfigurableListableBeanFactory factory) {         this.factory = factory;     }          public void run() {         System.out.println("Destroying Singletons");         factory.destroySingletons();         System.out.println("Singletons Destroyed");     }      }
image from book

Using this class, you can then create an instance of Thread and register this Thread as a shutdown hook using Runtime.addShutdownHook(). This is shown in Listing 5-10.

Listing 5-10: Registering a Shutdown Hook

image from book
package com.apress.prospring.ch5.lifecycle;      import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.FileSystemResource;      public class ShutdownHookExample {          public static void main(String[] args) {         ConfigurableListableBeanFactory factory = new XmlBeanFactory(                 new FileSystemResource(                         "./ch5/src/conf/lifecycle/disposeInterface.xml"));              Runtime.getRuntime().addShutdownHook(                 new Thread(new ShutdownHook(factory)));         DestructiveBeanWithInterface bean =           (DestructiveBeanWithInterface)factory.getBean("destructiveBean");     } } 
image from book

Notice that we obtain a reference to the current Runtime using Runtime.getRuntime() and then call addShutdownHook() on this reference. Running this example results in the following output:

Initializing Bean Destroying Singletons Destroying Bean Singletons Destroyed

As you can see, the destroySingletons() method is invoked, even though we didn't write any code to invoke it explicitly as the application was shutting down.



Pro Spring
Pro Spring
ISBN: 1590594614
EAN: 2147483647
Year: 2006
Pages: 189

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net