Skip to end of metadata
Go to start of metadata

The Problem

In Simple Rich Domain Injection, we gave a solution that injects dependencies into objects who are not born by the container.

If you are not familiar with this concept, I'd suggest to read that article as well as Craig Wall's Blog to get some idea of the background.

Dependency on Dependent

In Craig Wall's blog, he presented a solution that uses AspectJ+Spring to address the problem. A dependency injection aspect is responsible for looking up the container for the dependency value and setting it into the domain object.

The aspect by default uses the fully qualified class name of the domain object to lookup the container. This imposes a naming convention contract between the container and the aspect. It should typically work but maybe not always.

If the domain object uses an annotation to specify the bean name, the aspect can use that bean name instead. This requires domain objects to know about the "bean name" that should not be a concern of the domain object at all, hence not a good choice.

According to Rod Johnson, there's a BeanWiringInfoResolver interface that can handle special cases without coupling to Spring, I have yet to see the details though.

To AOP or Not to AOP?

Another major difference between Nuts' Simple Rich Domain Injection and the Spring 2.0 approach is Aspectj. Spring 2.0 uses AspectJ to get transparent dependency injection even when you call "new Customer()". The dependencies are just auto-magically injected for you no matter what.

This is convenient, but effectively lock the domain object into AspectJ (well, or any other language level aop enhancement). The "new Customer().doBusiness()" is never gonna work without bytecode enhancement.

Nowadays, when people are raving about light-weight, they pretty much mean that whatever the framework is, it doesn't lock you in. Take IOC container for example, we want our components to work in container 1, and in container 2, and even outside any container.

The Nuts' solution is free of any framework lock-in. It is a pure OO solution. We can always throw away the container and manually inject dependencies if we want.

That being said, I'm not saying that Nuts' solution is superior over an Aspectj solution. There are just trade-offs. With Nuts, you get zero framework lock-in, but you do pay extra effort such as a Factory; with Aspectj, yes, you are locked in, but you get the convenience that brings benefits right away. Practically, this benefit may be more attractive to some (Count me among the "some" (smile)).

Nuts+AOP

Being practical, we just can't ignore the convenience that AspectJ brings. Yet, the dependency onto the container is still un-desirable. Therefore, we boils Nuts and AspectJ together to see what differerence the marriage between Nuts and AspectJ can bring us.

I'll show the result first, just so you can simply walk away if it doesn't interest you.

Domain object

"Service1" and "Service2" are just two external dependencies that we don't care what they are.

As you can see, there's absolutely no dependency on the container. It is just pure pojo.

Configuration

All you really care is the domain object and the configuration. Hopefully this configuration looks simple to you:

In this configuration, we desribe the dependency injection in xml as a function. The function parameter is the object that needs dependency injection.

We also use the <injector> tag to tell the CustomerInjectionAspect aspect to weave the injection into the target objects. This <injector> tag is built to ease configuration on aspects.

Aspect

For each different domain object type, we will need an aspect that specifies the pointcut. This step could have been saved if pointcut could be configured by xml. Unfortunately that's impossible with AspectJ. We do have a solution though, but in order to keep the example simple, we'll not go into that detail here. You can read Type Based Post Instantiation if interested.

The aspect for Customer is:

BaseInjectionAspect

Time to see what's going on under the hood.

In Craig Wall's blog, the aspect actively calls Spring API to lookup the dependencies. That's why the aspect needs the bean name or at least "guess" the bean name. We take a different approach to make the aspect trivial and the "bean name" irrelavant.

In order to do that, we need an interface "Injection" that abstracts the notion of dependency injection, and here we go:

Next, we create a base aspect that can be reused for all kinds of domain objects:

This is a very simple aspect. And, with the InjectorAspectNut class we created to simplicify configuration, the work is done. I'm not gonna show the implementation of InjectorAspectNut since such implementation detail probably doesn't interest most of my readers.

Summary

In this article, we showed a solution that combines Nuts and AspectJ to provide a succinct solution for dependency injection to rich domain objects created outside of the container.

This solution imposes no dependency on the container api, no container specific annotation. The domain object is pure pojo. In fact, it works with Java 1.4 as well.

The only awkward problem is: we need to define one trivial aspect per domain type. See Type Based Post Instantiation for a solution.

Enhancing with Meta Data

Annotation Based Auto Wiring can help reduce configuration effort and the awkward one-aspect-per-type.

  • No labels