In the Transparent Dependency Injection for Rich Domain Objects, I presented a way to create proxy of the Data Access Objects and transparently inject dependencies managed by container.
The example in that article uses direct Java code, which is not very practical. In this article, we'll translate the solution into Nuts.
Firstly, let's analyze the problem. What we need are:
- The name of the Dao interface(s) to proxy.
- The name of the domain object types to inject dependencies to.
- The injection logic for each different domain object type.
And the tools we have are:
- <binder> tag to describe injection logic.
- The optional <typecase> tag to glue different injection logics together.
- The optional <inject> tag to create proxy for injecting dependencies.
- The optional <if> and <same> tag to check if the dependency is already set.
- The <synchronized> tag to provide thread safety.
- The optional <foreach> tag to traverse elements in an array or collection.
Secondly, let's do a brief rephrase of the problem to be resolved.
- We have a BankAccountDao that could return BankAccount and Bank objects.
- BankAccount object has a dependency on SmtpService. This needs to be injected.
- Bank object has a dependency on IRSService. It needs to be injected as well.
The java code that highlights the problems:
Solution Step by Step
Finally, with all the problems laid out and armed with tools, let's roll.
The injection logic for BankAccount is:
In order not to repeatedly set the smtp service, and to show how complex logic can be expressed in Nuts, we check the property first. Only when the property is null do we call the setter.
The injection logic for Bank looks very similar:
Don't worry about the "$irsservice" and "$smtpservice" yet. They are just two global components that you are gonna see.
Because the proxy of BankAccountDao needs to inject for both Bank and BankAccount, we need to glue the two injection logic together:
Still not done yet. Since the read and write of the same property are not one atomic operation, we need to synchronize to ensure thread safety:
So far, the "getAccountById" method is taken care of. But what about the "getAccounts" method? It returns a "java.util.List" object. The <setter> tag obviously doesn't work on a List.
The first time when I thought about this, I was like:"Hmm, that's ugly...". But it didn't hold me long. Nuts is an extensible system, all we have to do is to create a custom tag. The tag I created was a bit more general than just for this requirement. It is the <foreach> tag that can loop over every element in an array/Collection/Map.
The java pseudo code is:
As you may have noticed, the pseudo code has a recursion. It is for dealing with multi-dimensional collections such as list of list, array of list, array of array etc.
Using <foreach>, the code becomes:
Ok, the injection logic is done. Now we weave it into the BankAccountDao:
When wiring up objects, we use the "rich_dao" instead of the "original_dao". And the dependency is automatically set!
Put it together
The complete xml looks like: (To make it more interesting, we assume that the smtpservice and irsservice objects are from a jndi registry.)
Created by benyu benyu
On Sat Nov 12 18:40:00 CST 2005