In Post Instantiation with AspectJ, we described a solution that can inject dependencies into objects created outside of the container.
Typically such objects are a rich domain objects that are created by persistence layer. In the example we showed a simplified Customer class:
In reality, such domain object will have many more properties, such as name, social security number, age, gender etc. Most of them are filled out by the persistence layer such as Hibernate and should not be injected by the container.
This is not a problem when we use manual wiring because we just know the names of the properties that need to be injected.
But manual wiring may sometimes become tedious and unnecessary in the one-instance-per-type senario. By-type auto wiring will make configuration much easier in this case.
In order to do auto-wiring, we need to tell the container that which properties need to be injected and which ones don't. The most straight-forward way would be to specify the "property-names" attribute in the <bean> tag, for example:
This solution is simple and works fine. There are two problems though:
- we have to list the property names.
- In case the domain object is refactored, more dependencies are added or some dependencies are removed, the property names will have to be maintained consistently.
Lazy as we are, we want to be able to say:
And hopefully the <bean> tag knows automatically which properties need to be injected.
One possible solution is to use the Java 5 Annotation mechanism so that the source code itself can tell us "I need you to inject service1 and service2, leave everything else alone".
Before going into implementation detail, let me show the result first.
That's it. We have one piece of xml configuration code that handles all domain object types; Only one aspect is required.
Annotate the domain object
First step, we create a marker annotation that can be used to say "inject me".
The Retention policy has to be RUNTIME for obvious reasons.
The Customer class can then be written as:
It is then the responsibility of the containe to provide support that recognizes the annoation and auto wire based on meta data.
Using Nuts' extensible tag, there's a tag class already built for this purpose: AnnotationAwareBeanNut.
Using this class, we can do auto-wiring for the post-instantiation example as:
- First we load the nut class using <nut>, and name it "abean".
- When configuring a customer object, we specify autowire mode as "bytype" and use <abean> to wire by annotation.
Even better, you may or may not have realized, with auto-wiring, we now don't really need a <function> for every different domain type. Instead, we can just use a general one. We also don't need to create one aspect per domain type either, a general aspect will probably suffice too:
Let the meta data and auto-wiring take care of the differences, why do we care?
It is also possible to mix manual wiring and auto-wiring together, for example, we could explicitly specify service1 and leave the rest to auto-wiring:
In the previous example, the annotation "Inject" is pre-defined in the container API. This means that the domain object still has dependency onto the container. Practically this is not too bad, because the domain object has to use an annotation anyway. But, in case this does become a problem, or when a domain object is already annotated by a different annotation type for the same purpose, Nuts offer the flexibility to use the custom annotation.
And that is easy, just pass <abean> the annotation type that you want to use. Let's say the client has a @InjectMe tag that serves the same purpose:
The configuration will become:
What if the "InjectMe" has an aweful long fully qualified name: "com.blahblahblah.blahblahblah.blahblahblah.InjectMe"? And what if we need to use <abean> 100 times? Isn't the need to specify the annotation type again and again too tedious?
Well, yes. And we have a simple solution for it. The following class extends from AnnotationAwareBeanNut by providing "InjectMe" as the default annotation type:
Now, load this CustomBeanNut instead of AnnotationAwareBeanNut:
Everything else remain the same. Use <abean> for as many times as you want, your damn long annotation name don't ever have to show up in the configuration any more.