The Spring Framework provides several callback interfaces to
change the behavior of your bean in the container; they include
InitializingBean
and
DisposableBean
. Implementing these
interfaces will result in the container calling
afterPropertiesSet()
for the former and
destroy()
for the latter to allow the bean to
perform certain actions upon initialization and destruction.
Internally, the Spring Framework uses
BeanPostProcessor
implementations to
process any callback interfaces it can find and call the appropriate
methods. If you need custom features or other lifecycle behavior Spring
doesn't offer out-of-the-box, you can implement a
BeanPostProcessor
yourself. More
information about this can be found in the section entitled Section 4.7, “Container extension points”.
All the different lifecycle callback interfaces are described below. In one of the appendices, you can find diagrams that show how Spring manages beans, how those lifecycle features change the nature of your beans, and how they are managed.
Implementing the
org.springframework.beans.factory.InitializingBean
interface allows a bean to perform initialization work after all
necessary properties on the bean have been set by the container. The
InitializingBean
interface specifies
exactly one method:
void afterPropertiesSet() throws Exception;
Generally, the use of the
InitializingBean
interface can be
avoided and is actually discouraged since it unnecessarily couples the
code to Spring. As an alternative, bean definitions provide support
for a generic initialization method to be specified. In the case of
XML-based configuration metadata, this is done using the
'init-method'
attribute. For example, the following
definition:
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean { public void init() { // do some initialization work } }
...is exactly the same as...
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements InitializingBean { public void afterPropertiesSet() { // do some initialization work } }
... but does not couple the code to Spring.
Implementing the
org.springframework.beans.factory.DisposableBean
interface allows a bean to get a callback when the container
containing it is destroyed. The
DisposableBean
interface specifies a
single method:
void destroy() throws Exception;
Generally, the use of the
DisposableBean
callback interface can
be avoided and is actually discouraged since it unnecessarily couples
the code to Spring. As an alternative, bean definitions provide
support for a generic destroy method to be specified. When using
XML-based configuration metadata this is done via the
'destroy-method'
attribute on the
<bean/>
. For example, the following
definition:
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
public class ExampleBean { public void cleanup() { // do some destruction work (like releasing pooled connections) } }
...is exactly the same as...
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements DisposableBean { public void destroy() { // do some destruction work (like releasing pooled connections) } }
... but does not couple the code to Spring.
When writing initialization and destroy method callbacks that do
not use the Spring-specific
InitializingBean
and
DisposableBean
callback interfaces, one
typically finds oneself writing methods with names such as
init()
, initialize()
,
dispose()
, etc. The names of such lifecycle
callback methods are (hopefully!) standardized across a project so
that all developers on a team use the same method names and thus
ensure some level of consistency.
The Spring container can be configured to
'look'
for named initialization and destroy
callback method names on every bean. This means
that you, as an application developer, can simply write your
application classes, use a convention of having an initialization
callback called init()
, and then (without having to
configure each and every bean with, in the case of XML-based
configuration, an 'init-method="init"'
attribute)
be safe in the knowledge that the Spring IoC container
will call that method when the bean is being
created (and in accordance with the standard lifecycle callback
contract described previously).
Let's look at an example to make the use of this feature
completely clear. For the sake of the example, let us say that one of
the coding conventions on a project is that all initialization
callback methods are to be named init()
and that
destroy callback methods are to be called
destroy()
. This leads to classes like so...
public class DefaultBlogService implements BlogService { private BlogDao blogDao; public void setBlogDao(BlogDao blogDao) { this.blogDao = blogDao; } // this is (unsurprisingly) the initialization callback method public void init() { if (this.blogDao == null) { throw new IllegalStateException("The [blogDao] property must be set."); } } }
<beans default-init-method="init"> <bean id="blogService" class="com.foo.DefaultBlogService"> <property name="blogDao" ref="blogDao" /> </bean> </beans>
Notice the use of the 'default-init-method'
attribute on the top-level <beans/>
element.
The presence of this attribute means that the Spring IoC container
will recognize a method called 'init'
on beans as
being the initialization method callback, and when a bean is being
created and assembled, if the bean's class has such a method, it will
be invoked at the appropriate time.
Destroy method callbacks are configured similarly (in XML that
is) using the 'default-destroy-method'
attribute on
the top-level <beans/>
element.
The use of this feature can save you the (small) housekeeping chore of specifying an initialization and destroy method callback on each and every bean, and it is great for enforcing a consistent naming convention for initialization and destroy method callbacks, as consistency is something that should always be aimed for.
Consider the case where you have some existing beans where the
underlying classes already have initialization callback methods that
are named at variance with the convention. You can
always override the default by specifying (in XML
that is) the method name using the 'init-method'
and 'destroy-method'
attributes on the
<bean/>
element itself.
Finally, please be aware that the Spring container guarantees that a configured initialization callback is called immediately after a bean has been supplied with all of its dependencies. This means that the initialization callback will be called on the raw bean reference, which means that any AOP interceptors or suchlike that will ultimately be applied to the bean will not yet be in place. A target bean is fully created first, then an AOP proxy (for example) with its interceptor chain is applied. Note that, if the target bean and the proxy are defined separately, your code can even interact with the raw target bean, bypassing the proxy. Hence, it would be very inconsistent to apply the interceptors to the init method, since that would couple the lifecycle of the target bean with its proxy/interceptors and leave strange semantics when talking to the raw target bean directly.
As of Spring 2.5, there are three options for controlling bean
lifecycle behavior: the InitializingBean
and DisposableBean
callback interfaces; custom init()
and
destroy()
methods; and the @PostConstruct
and @PreDestroy
annotations.
When combining different lifecycle mechanisms - for example, in a class hierarchy in which various lifecycle mechanisms are in use - developers should be aware of the order in which these mechanisms are applied. The following is the ordering for initialization methods:
Methods annotated with
@PostConstruct
afterPropertiesSet()
as defined by the
InitializingBean
callback
interface
A custom configured init()
method
Destroy methods are called in the same order:
Methods annotated with
@PreDestroy
destroy()
as defined by the
DisposableBean
callback
interface
A custom configured destroy()
method
![]() | Note |
---|---|
If multiple lifecycle mechanisms are configured for a given
bean, and each mechanism is configured with a different method name,
then each configured method will be executed in the order listed
above; however, if the same method name is configured - for example,
|
![]() | Note |
---|---|
This next section does not apply to web applications (in case
the title of this section did not make that abundantly clear).
Spring's web-based |
If you are using Spring's IoC container in a non-web application environment, for example in a rich client desktop environment, and you want the container to shutdown gracefully and call the relevant destroy callbacks on your singleton beans, you will need to register a shutdown hook with the JVM. This is quite easy to do (see below), and will ensure that your Spring IoC container shuts down gracefully and that all resources held by your singletons are released. Of course it is still up to you to both configure the destroy callbacks for your singletons and implement such destroy callbacks correctly.
So to register a shutdown hook that enables the graceful
shutdown of the relevant Spring IoC container, you simply need to call
the registerShutdownHook()
method that is
declared on the AbstractApplicationContext
class. To wit...
import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public final class Boot { public static void main(final String[] args) throws Exception { AbstractApplicationContext ctx = new ClassPathXmlApplicationContext(new String []{"beans.xml"}); // add a shutdown hook for the above context... ctx.registerShutdownHook(); // app runs here... // main method exits, hook is called prior to the app shutting down... } }
A class which implements the
org.springframework.beans.factory.BeanFactoryAware
interface is provided with a reference to the
BeanFactory
that created it, when it is
created by that BeanFactory
.
public interface BeanFactoryAware { void setBeanFactory(BeanFactory beanFactory) throws BeansException; }
This allows beans to manipulate the
BeanFactory
that created them
programmatically, through the
BeanFactory
interface, or by casting
the reference to a known subclass of this which exposes additional
functionality. Primarily this would consist of programmatic retrieval
of other beans. While there are cases when this capability is useful,
it should generally be avoided, since it couples the code to Spring
and does not follow the Inversion of Control style, where
collaborators are provided to beans as properties.
An alternative option that is equivalent in effect to the
BeanFactoryAware
-based approach is to
use the
org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean
.
(It should be noted that this approach still does not reduce the
coupling to Spring, but it does not violate the central principle of
IoC as much as the
BeanFactoryAware
-based
approach.)
The ObjectFactoryCreatingFactoryBean
is a
FactoryBean
implementation that returns a reference to an object (factory) that
can in turn be used to effect a bean lookup. The
ObjectFactoryCreatingFactoryBean
class does
itself implement the BeanFactoryAware
interface; what client beans are actually injected with is an instance
of the ObjectFactory
interface. This is
a Spring-specific interface (and hence there is still no total
decoupling from Spring), but clients can then use the
ObjectFactory
's
getObject()
method to effect the bean lookup
(under the hood the ObjectFactory
implementation instance that is returned simply delegates down to a
BeanFactory
to actually lookup a bean
by name). All that you need to do is supply the
ObjectFactoryCreatingFactoryBean
with the name
of the bean that is to be looked up. Let's look at an example:
package x.y; public class NewsFeed { private String news; public void setNews(String news) { this.news = news; } public String getNews() { return this.toString() + ": '" + news + "'"; } }
package x.y; import org.springframework.beans.factory.ObjectFactory; public class NewsFeedManager { private ObjectFactory factory; public void setFactory(ObjectFactory factory) { this.factory = factory; } public void printNews() { // here is where the lookup is performed; note that there is no // need to hard code the name of the bean that is being looked up... NewsFeed news = (NewsFeed) factory.getObject(); System.out.println(news.getNews()); } }
Find below the XML configuration to wire together the above
classes using the
ObjectFactoryCreatingFactoryBean
approach.
<beans> <bean id="newsFeedManager" class="x.y.NewsFeedManager"> <property name="factory"> <bean class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean"> <property name="targetBeanName"> <idref local="newsFeed" /> </property> </bean> </property> </bean> <bean id="newsFeed" class="x.y.NewsFeed" scope="prototype"> <property name="news" value="... that's fit to print!" /> </bean> </beans>
And here is a small driver program to test the fact that new
(prototype) instances of the newsFeed
bean are
actually being returned for each call to the injected
ObjectFactory
inside the
NewsFeedManager
's
printNews()
method.
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import x.y.NewsFeedManager; public class Main { public static void main(String[] args) throws Exception { ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); NewsFeedManager manager = (NewsFeedManager) ctx.getBean("newsFeedManager"); manager.printNews(); manager.printNews(); } }
The output from running the above program will look like so (results will of course vary on your machine).
[email protected]: '... that's fit to print!' [email protected]: '... that's fit to print!'
As of Spring 2.5, you can rely upon autowiring of the
BeanFactory
as yet another alternative
to implementing the BeanFactoryAware
interface. The "traditional" constructor
and
byType
autowiring modes (as described in the
section entitled Section 4.3.5, “Autowiring collaborators”) are now
capable of providing a dependency of type
BeanFactory
for either a constructor
argument or setter method parameter respectively. For more flexibility
(including the ability to autowire fields and multiple parameter
methods), consider using the new annotation-based autowiring features.
In that case, the BeanFactory
will be
autowired into a field, constructor argument, or method parameter that
is expecting the BeanFactory
type as
long as the field, constructor, or method in question carries the
@Autowired
annotation. For more
information, see the section entitled Section 4.11.2, “@Autowired”.
If a bean implements the
org.springframework.beans.factory.BeanNameAware
interface and is deployed in a
BeanFactory
, the
BeanFactory
will call the bean through
this interface to inform the bean of the name it
was deployed under. The callback will be invoked after population of
normal bean properties but before an initialization callback like
InitializingBean
's
afterPropertiesSet or a custom
init-method.