Get ahead
VMware offers training and certification to turbo-charge your progress.
Learn moreSpring's dependency injection (DI) mechanism allows configuring beans defined in application context. What if you want to extend the same idea to non-beans? Spring's support for domain object DI utilizes AspectJ weaving to extend DI to any object, even if it is created by, say, a web or an ORM framework. This enables creating domain behavior rich objects, since domain objects can now collaborate with the injected objects. In this blog, I discuss the latest improvements in the Spring framework in this area.
The core idea behind domain object DI is quite simple: An AspectJ-woven aspect selects join points corresponding to creation or deserialization of any object matching certain specification. Advice to those join points inject dependencies into the object being created or deserialized. Of course, the devil is in the details. For example, how do you select join point corresponding to deserialization or how do you inject dependency only once per object? By offering a few pre-written aspects, Spring shields developers from all these details.
Currently, most Spring users use the @Configurable annotation to designate the configurable classes. With latest improvements in upcoming Spring 2.5.2, available starting with nightly build 379, you have a few more options making this feature a lot more powerful. The new improvements follow the "Make simple things simple, complex things possible" principle. Depending on your familiarity with AspectJ and expected design sophistication, one of the options will serve well. Figure 1 shows the new aspect hierarchy that makes a combination of simplicity and flexibility possible.
Figure 1: Inheritance hierarchy for domain object dependency injection aspects.
So what does each of these aspects offer? Let's go bottom up.
@Configurable
public class Order {
private transient MailSender mailSender;
public void setMailSender(MailSender mailSender) {
this.mailSender = mailSender;
}
public void process() {
...
mailSender.send(...);
...
}
}
Next, you instruct Spring on how to configure objects of the Order type. The instructions follow the standard bean definition for a prototype bean as follows:
<context:spring-configured/>
<bean class="example.Order" scope="prototype">
<property name="mailSender" ref="externalMailSender"/>
</bean>
<bean id="externalMailSender" ...>
...
</bean>
Now upon any Order creation or deserialization, Spring will set the mailSender property of the created object with the externalMailSender bean.
Spring 2.5 features a new annotation-based configuration option that allows eliminating or reducing the attendant XML. The @Configurable annotation-based DI benefits from it as well. For example, you can mark the mailSender property as @Autowired as follows:
@Configurable
public class Order {
private transient MailSender mailSender;
@Autowired
public void setMailSender(MailSender mailSender) {
this.mailSender = mailSender;
}
public void process() {
...
mailSender.send(...);
...
}
}
You can get rid of even the setter by annotating the field itself, reducing the above code to:
@Configurable
public class Order {
@Autowired private transient MailSender mailSender;
public void process() {
...
mailSender.send(...);
...
}
}
In either case, the attendant XML configuration reduces to the following (note the use of <context:annotation-config/>):
<context:spring-configured/>
<context:annotation-config/>
<bean id="externalMailSender" ...>
...
</bean>
For more details on this domain object DI option, please see Using AspectJ to dependency inject domain objects with Spring.
At the design level, this aspect configures any domain object whose type implements the ConfigurableObject interface. While, having a type implement the ConfigurableObject interface directly is certainly a valid choice, an elegant alternative is to use a declare parents statement in another aspect (a subaspect of AbstractInterfaceDrivenDependencyInjectionAspect would be a logical choice). The statement would declare a configurable class as implementing the ConfigurableObject interface. This keeps your domain classes free from Spring-specific artifacts, while benefiting from the DI mechanism. Let's see an example of such usage.
Consider the Order class from the earlier section. Instead of using the @Configurable, you can let it implement a domain-specific MailSenderClient interface that signifies that it uses a MailSender.
public class Order implements MailSenderClient {
private transient MailSender mailSender;
public void setMailSender(MailSender mailSender) {
this.mailSender = mailSender;
}
public void process() {
...
mailSender.send(...);
...
}
}
Next you write a subaspect of AbstractInterfaceDrivenDependencyInjectionAspect to inject dependencies into any MailSenderClient objects.
public aspect MailClientDependencyInjectionAspect extends
AbstractInterfaceDrivenDependencyInjectionAspect {
private MailSender mailSender;
declare parents: MailSenderClient implements ConfigurableObject;
public pointcut inConfigurableBean() : within(MailSenderClient+);
public void configureBean(Object bean) {
((MailSenderClient)bean).setMailSender(this.mailSender);
}
public void setMailSender(MailSender mailSender) {
this.mailSender = mailSender;
}
}
There are two AspectJ constructs used in the aspect:
The configureBean() method performs injection into the bean by making direct calls to the appropriate setters. Of course, any other logic appropriate for bean configuration such as calling a multi-argument method or calling any initialization methods would work just fine. Note that direct calls used in this way avoid reflection and can yield noticeable performance improvements if the rate of domain object creation is high.
You need to configure the MailClientDependencyInjectionAspect aspect instance itself to inject its dependency--the mailSender property. The Spring way would be to create a bean for the aspect and configure it in application context:
<bean class="example.MailClientDependencyInjectionAspect"
factory-method="aspectOf">
<property name="mailSender" ref="externalMailSender"/>
</bean>
<bean id="externalMailSender" ...>
...
</bean>
There are a few additional patterns around this aspect:
However, let's save these ideas for another blog entry.
The aspect declares six pointcuts that a subaspect may define:
This aspect also defines an abstract method configureBean(Object bean), whose implementation should specify the logic corresponding to dependency injection.
So there you have all options to enable domain object DI in your application. If you are into DDD or otherwise need DI extended to your domain objects, you have to look at these new set of aspects. Depending on your specific needs and AspectJ knowledge, you will find one of them helpful towards creating an elegant solution.