|
From: Rod J. <rod...@in...> - 2004-06-24 16:20:45
|
Last night I committed two fairly significant enhancements to
the core IoC container.
1. Method Injection support. (Similar to what has been
described as "Getter Injection", but I believe that the
concept is more general and needs a more general name.) This
allows Spring to override abstract or concrete methods on
managed objects on deployment, to return the result of
looking up a named bean in the current factory. The lookup
will typically be of a non-singleton bean (although it can
also be a singleton).
This is useful when one object configured via Spring needs to
create instances of a non-singleton bean: for example, a
single-threaded, single-use processing object. E.g.
ThreadSafeService needs to create SingleShotHelper, which is
itself configured via DI. To date it was necessary for
ThreadSafeService to implement BeanFactoryAware, save the
BeanFactory reference and call
getBean("singleShotHelper")
each time it wanted to create a helper. That works fine, but
it's a Spring dependency, and we want a perfectly non-
invasive framework.
With the new functionality, it's possible to use an abstract
(or concrete) method such as
protected SingleShotHelper createSingleShotHelper()
and tell the container to override that method on deployment
to return a specific bean, like this:
<lookup-method name="createSingleShotHelper"
bean="singleShotHelper" />
The methods can be protected or public. Any number of methods
can be overridden.
This way there are no Spring dependencies. A corner case
closed off without needing to import a Spring API. This
feature was directly motivated by requirements in a client
project I'm working on.
Lookup methods can be combined with Setter Injection. They
can't presently be combined with Constructor Injection, but I
will add support for this, assuming that it's possible with
CGLIB.
The implementation uses CGLIB to subclass the class.
I've considered adding the ability to add arbitrary behaviour
in the overridden method--not just a bean lookup--and the
implementation allows this to be added fairly easily (I've
already prototyped it). However, I'm not convinced that the
more general case is an equally desirable feature. There are
other ways of doing this, such as subclassing the class and
overriding the method in the normal way, or using AOP. In the
case of the bean lookup, there is a very definite benefit in
the container doing the override, as it eliminates dependency
on a Spring API. It's also much simpler to describe in XML.
With the more general case, it's necessary to have a way of
identifying overloaded methods etc.
2. Support for creation of bean instances using static
factory methods, as an alternative to constructors. (Empty
JavaBean constructors or Constructor Injection constructors.)
This is potentially useful for legacy code. Joshua Bloch
advocates use of static factory methods in some cases instead
of constructors, and I'm sure many people have taken his
advice! (I doubt I would design new classes this way,
however.)
Arguments can be passed to the static factory methods using
<constructor-arg> elements. (None is required if the factory
method takes no arguments.)
This feature was motivated by the need to create AspectJ
aspects using the aspectOf() static factory method, but I
think it's a useful feature in general. See Adrian Colyer's
blog re AspectJ/Spring, at
http://www.aspectprogrammer.org/blogs/adrian/2004/05/what_the_
teache.html. This will be part of the AspectJ/Spring
integration in Spring 1.1. I think the potential for Spring
to configure AspectJ aspects using DI is very exciting.
The use of a factory method is specified by a new
optional "factory-method" attribute on the bean element. The
factory method must be on the class specified by the "bean"
attribute. This is normally, but not necessarily, the class
that is created by the factory method.
This can also be combined with Setter Injection.
Usage looks like this:
<bean id="whatever" class="whatever"
factory-method="createWhatever">
<constructor-arg index="0"><ref or value...
<property name="xxx"...
If the factory method is overloaded, the container will find
the correct one for the argument types.
Of course, these changes are backward compatible.
Rgds
Rod
|