Recap of IoC and Spring's implementation
Earlier this year, Martin Fowler penned an article (http://www.martinfowler.com/articles/injection.html
) that coalesced the different approaches lightweight containers take to provide Inversion of Control also called dependency injection.
There are three commonly found types of dependency injection, and Spring offers some level of support for each of them. Read the Fowler article for a full discussion of each type of IoC.
Type 1 IoC (interface injection)
Spring doesn't offer support for interface injection as Fowler described it, but Spring does provide a couple types of "autowire" capabilities. Autowire is a means of automagically invoking setters on a target object by looking for source objects in the entire Spring context. If the target object has a setter that takes a single parameter, the type of that parameter is located in the Spring context. If Spring knows of any bean that matches the setter's type, it will invoke the setter.
As an example, if a JavaBean in Spring is set to "autowire=byType" and has a method signature of void setProperties(Properties props), Spring will look for a bean in its context that is of type Properties. If it finds one, it will pass it to the target bean's setter method. If it finds more than one source object of type Properties it will throw an exception.
Spring also has another type of autowire that uses the name of the setter property and looks for a source object with the same name. If found, the source object is passed to the target object's setter method.
Type 2 IoC (setter injection)
This is the most often demonstrated use of Spring and uses a configuration file to specify the name of a target object and its JavaBean property and a source object to "wire" to the target object's property. Spring uses the property name to determine the mutator (setter) method on the target object and uses reflection to pass the source object to the setter method.
Spring also will attempt to convert the type of the source object to the setter's parameter type if it can. Spring has built in property editor support to convert a large range of common types and collection classes.
Type 3 IoC (constructor injection)
In much the same manner as Type 2 IoC , constructor injection can be accomplished by specifying constructor arguments for a target object using a configuration file. The source objects cannot have a property name associated with them, but a specific constructor on the target object can be matched to a list of source objects based on the number of source objects and their types.
Interface Injection
As noted above, Spring's support of Interface Injection is an all or nothing approach and cannot be controlled very well. Autowire by type is a very magical type of IoC that proves too loose in many large applications, primarily because there isn't an efficient way to tell Spring which methods should be autowired and which ones are strictly off-limits.
In Fowler's article he points to Jakarta Avalon as an example of a lightweight
container that supports interface injection. In applications that we are building with Spring, we too wanted an easy way to wire DAO interfaces to our WebWork action classes that we manage with Spring. We decided to create an interface that an action class would implement and let Spring "autowire" a specific DAO to an action based on the action's aware interfaces. This turns out to be a technique that gives Spring a concrete implementation of Interface Injection.
The Configuration
Let's start with a quick look at the configuration necessary to pull this off. The configuration also introduces the class that performs the Type 1 IoC, but we will discuss that last.
The bean 'foo.ProjectAction' is a WebWork action that implements the foo.aware.AuditDAOAware interface. When Spring hands this action to WebWork, we fully expect it to have access to an implementation of foo.dao.AuditDAO.
The AuditDAO itself is a singleton implementation of the foo.dao.AuditDAO class and in our case, it is a Hibernate implementation.
The last bean shown is the class that provides the Interface Injection. It takes three parameters:
- interface - the name of the interface that the Type 1 IoC is concerned with. Any object Spring creates that implements this interface will receive the injection.
- property - the property on the target object that will get injected with the source object.
- value - the source object that will be passed to the setter on the target object.
The Code
package foo.spring;
import org.springframework.beans.BeansException;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.logging.Log;
/**
* <p>AutowireByInterface</p>
*
* @author <a href="mailto:jimcook@visualxs.com">James Cook</a>
*/
public class AutowireByInterface implements BeanPostProcessor, Ordered {
private static final Log LOG = LogFactory.getLog(AutowireByInterface.class);
private Class _interface;
private String _property;
private Object _value;
private int _order;
public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException {
return bean;
}
public Object postProcessAfterInitialization(Object bean, String name) throws BeansException {
if (_interface == null || _property == null) {
throw new IllegalArgumentException("Use of the AutowireByInterface component " +
"requires that an interface and property value be provided.");
}
if (_interface.isAssignableFrom(bean.getClass())) {
if (LOG.isDebugEnabled()) {
LOG.debug("Found an instance of " + _interface.getName() +
" on " + bean.getClass().getName() + ". Setting '" +
_property + "' to " + _value);
}
BeanWrapper bw = new BeanWrapperImpl(bean);
bw.setPropertyValue(_property, _value);
}
return bean;
}
public void setInterface(Class anInterface) {
_interface = anInterface;
}
public void setProperty(String property) {
_property = property;
}
public void setValue(Object value) {
_value = value;
}
public int getOrder() {
return _order;
}
public void setOrder(int order) {
_order = order;
}
}
Things to Watch Out For
As one might expect the degree of control that Interface Injection provides over Spring's "autowire" capabilities are very useful, and they can eliminate a great deal of configuration. There is one thing to watch out for however.
In the example above, AutowireByInterface takes a DAO object as a property. In most situations, a DAO object should be proxies by Spring's transaction management. We had enjoyed using Spring's BeanNameAutoProxyCreator to generate proxied classes for all of our DAO's automagically. For some reason however, the auto proxy feature failed to fire when using our DAO with the AutowireByInterface implementation. Perhaps it has something to do with the fact that AutowireByInterface and BeanNameAutoProxyCreator are both BeanPostProcessors, but I was unable to get it to work properly.
Instead, if you are going to use AutowireByInterface, be sure to use the TransactionProxyFactoryBean approach of adding transaction semantics to your DAO objects.
The Future
I didn't see it on any roadmaps, but good support for Type 1 IoC should probably be a goal of Spring since it supports the other two techniques so well. Perhaps AutowireByInjection should be added to core Spring?