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>
The upshot of this is that when loading Spring will check all your beans, searching for methods with these names. If they exist then it'll call them at the appropriate moment.
If you change the name of the config file to point the one above and re-run same unit test with this code it'll still work. This time it's autowired using the @Component annotation, which I ignored above.
@Test
public void testLifeCycle_using_default_declaration() {
ctx = new ClassPathXmlApplicationContext("dialer2.xml");
ctx.registerShutdownHook();
instance = ctx.getBean(DialerControllerImpl.class);
if (instance.dial("555-1234")) {
instance.playMessge();
instance.hangUp();
}
}
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.
4 comments:
Hi,
Can you please tell me how can i call the destroy-method when application is stop from weblogic server rather than jvm down. I deploy my application on weblogicc server and registerShutdownHook with context but my destry-method is not calling when i stop the application by myself on weblogic.
Please tell me a way for that
Hi,
Can you please tell me how can i call the destroy-method when application is stop from weblogic server rather than jvm down. I deploy my application on weblogicc server and registerShutdownHook with context but my destry-method is not calling when i stop the application by myself on weblogic.
Please tell me a way for that
Noman
A really good question. I can't think of any 'official' way of doing this, but have a couple of ideas. Firstly, and I've not tried this, as this is Weblogic you can deploy your application as an EAR file rather than a WAR file. This means that you could include an EJB in your application. You can then annotate a method in your EJB with @PreDestroy. In this method you could get hold of the application context using ContextSingletonBeanFactoryLocator. With the ApplicationContext you can enumerate through the beans it contains calling their destroy() methods.
To make this work, You may have to ensure that the application context is loaded by annotating anther method with @PostConstruct.
There is a really good book on Weblogic Server available on Amazon, which might contain a better answer.
Hey Roger,
Thanks for given a hint :) @PreDestroy in EJB solve my issue. I already using EJB+Spring but forgot to try with this.
It is really frustrated that weblogic10.3.5 not supported servlet3.0 and spring3.0. Otherwise i was trying to dynamic initialization servlet and use its destroy method. Thanks again :)
Post a comment