The Problem
In the rich domain model recommended by Martin Fowler, Persistence technologies such as Hibernate is responsible for instantiating domain objects that implements domain logic. Sometimes, it is necessary for a domain object to have some dependencies on other modules of the system to finish the job that the domain logic requires.
The persistence layer has no way to know about such dependencies to inject the right objects to them. So it becomes a problem to resolve the dependencies for these domain objects.
It is always possible to set these dependencies manually, for example:
BankAccount account = session.find(...); account.setStmpService(stmp); ...
However, such manual approach may become difficult when the dependencies to inject scale up. It is then a natural thought to try to use the container for the job because it is already a dependency injection framework.
Some propose to use AOP together with IOC container such as Spring to get the dependencies set up automatically. It looks like a handy approach but we are not sure if this is a perfect fit for AOP because the dependency injection here is really a necessary part of the integral work, rather than a cross-cut concern. The introduction of AOP in this senario makes the code less understandable and less unit-testable because we will always have to have the AOP in place to keep the domain object in a consistent state.
In this article we will show a solution without using AOP.
Dependency Injection with no dependency on container
First, an interface can be introduced to abstract the notion of dependency injection:
public interface Injector{ void inject(Object obj); }
This interface is designed in the application domain to represent the dependency injection requirement we have here. You can give it any name you like.
Next, in the class that creates the domain object (in this example, BankAccountService), we declare an Injector member variable:
class BankAccountService{
private final Injector injector;
private final BankAccountDao dao;
BankAccountService(Injector injector, BankAccountDao dao){
this.injector = injector;
this.dao = dao;
}
BankAccount findAccountById(String id){
BankAccount acc = dao.getById(id);
injector.inject(acc);
return acc;
}
}
This class is less-fragile in the sense that it does not require AOP to work. We can always inject a dumb Injector object that manually inject dependencies just as we did in the first example. This makes it easier to understand and unit-testable.
Inject dependency from container
Yan can inject dependencies into objects that are not managed by Yan. Such logic can be implemented in a Binder object.
Here's an example:
Binder injection = new Binder(){ public Creator bind(Object obj){ return Components.value(obj) .bean(new String[]{"smtpService"}); } };
This Binder object will create a Component object that injects dependency for the "smtpService" property to obj.
Next, the helper class InjectorHelper is used to convert the Binder object that encapsulates the injection logic to a Component object that instantiates an Injector instance:
void registerBankAccountService(Container yan){
Component some_dao_component = ...;
Binder injection = ...;
InjectorHelper helper = new InjectorHelper();
Component injector = helper.getInjectorComponent(Injector.class, injection);
yan.registerComponent("bankaccount_service",
Components.ctor(BankAccountService.class)
.withArgument(0, some_dao_component)
.withArgument(1, injector);
}
- new InjectorHelper() creates an InjectorHelper object. This object can be reused for injectors of other domain objects to enhance performance.
- helper.getInjectorComponent(Injector.class, injection) converts the injection object to a Component that instantiates an instance of interface Injector. When Injector.inject(some_obj) is invoked, the "smtpService" property of this object gets set by the container.
- Finally, this injector component is registered as the 2nd parameter of the constructor of BankAccountService class. Therefore, BankAccountService objects created from the container will have the injector object properly configured.
An xml wrapper can also be created to represent the injection logic.
Created by benyu
On Mon Oct 31 07:43:51 CST 2005
Using TimTam
