Thursday, December 18, 2008

Constructor Injection: how it works? Impl using Pico


Implementation of Constructor Injection using Pico. How CI works?

Constructor Injection is mainly used by a highly embeddable, lightweight, and full-service IoC Container named PicoContainer. Should you need to refresh your understanding of DI and its types, go through this article - Dependency Injection & its types>>

Let's see how the Constructor Injection phenomenon works. We will take one example scenario and will implement that using PicoContainer. First we'll see the source code and subsequently we'll discuss how the various pieces of the code fit together and how actually do they work.


Example Scenario: Suppose we have a requirement of displaying bank account details which might come from various sources. The requirement is fairy simple and to implement it we can easily think of having two interfaces, one which would take care of the presentation of the account details and the other would capture the details. Say for example we have named these interfaces as 'DisplayAccount' and 'BankAccount'.




interface DisplayAccount{
void displayAccountDetails(BankAccount bankAccount);
}

interface BankAccount{
Map getAccountDetails(long accountNumber);
}



Source Code: How to code Constructor Injection using PicoContainer?

Since the injection is handled by the constructor and hence it should have the declaration of all the objects that it might depend on.




class DisplayAccountImpl implements DisplayAccount{

private BankAccount bankAccount;
...

public DisplayAccountImpl(BankAccount bankAccount){

this.bankAccount = bankAccount;
}

public void displayAccountDetails(){

/*... display the details as required ...*/
Map acDetails = bankAccount.getAccountDetails();
...
}
...
}

class BankAccountImpl implements BankAccount{

private long accountNumber;
private Map acDetails;
...
...

public BankAccountImpl(long accountNumber){

this.accountNumber = accountNumber;
}

public Map getAccountDetails(){

Map acDetails = new HashMap();

/*... get the details maybe from a DB...*/
/*... form a Map object out of the details ...*/
...
...
...

return acDetails;
}
...
}



Having these two classes well in place, we can now use PicoContainer (or any other IoC Container supporting Constructor Injection such as Spring) to implement the Constructor Injection.




class TestConstructorInjectionWithPico{

private MutablePicoContainer getConfiguredContainer() {

/*... Step #1: get a default Container instance ...*/
MutablePicoContainer picoContainer = new DefaultPicoContainer();

/*... Step #2: populate the Container parameters ...*/
/*... we need to make the parameter value(s) available in this scope ...*/
ConstantParameter constantParameter1 = new ConstantParameter(accountNumber);
...

Parameter[] parametersForBankAccountImpl = {constantParameter1, ...};


/*... Step #3: registering the components with the Container ...*/
picoContainer.registerComponentImplementation(BankAccount.class, BankAccountImpl.class, requiredParameters);
pico.registerComponentImplementation(DisplayAccountImpl.class);

/*... Step #4: return the configured Container instance ...*/
return picoContainer;
}

...

public static void main(String[] args){

/*... get an instance of configured Container ...*/
MutablePicoContainer picoContainer = getConfiguredContainer();

/*... get the components having the injected dependencies ...*/
DisplayAccountImpl displayAccountImpl = (DisplayAccountImpl) picoContainer.getComponentInstance(DisplayAccountImpl.class);

/*... use ready-to-be used components: using for display ac details ...*/
displayAccountImpl.displayAccountDetails();
}
}




Let's try to understand how the above code-segments work. The sample class and interface definitions are fairly simple and straightforward. Coming directly to the getConfiguredContainer method where we are creating an instance of the PicoContainer and configuring the instance. PicoContainer is actually an interface having MutablePicoContainer as one of its various sub-interfaces. The MutablePicoContainer interface has many variants of the method registerComponentImplementation. The two variants which we have used have their signatures as:

  • ComponentAdapter registerComponentImplementation(Object componentKey, Class componentImplementation, Parameter[] parameters) throws PicoRegistrationException;
  • ComponentAdapter registerComponentImplementation(Class componentImplementation) throws PicoRegistrationException;
Here the 'componentKey' is the unique Key within the Container used to identify the component and 'componentImplementation' is the component's implementation class which should of course be a concrete class as otherwise the Container won't be able to create an object of it. The third parameter 'parameters' of type Parameter is an array and it serves as the hint about what all parameters the constructor of the implementing class would require. Depending upon the implementation of the Container, one or more of these hints may be ignored.

Second variant is internally translated into a call of this variant 'registerComponentImplementation(Object componentKey, Class componentImplementation)' which means the same type is used as the key to register the component with the Container. Another point to note here is that we didn't require to pass the parameter of type 'BankAccount' as it's already registered and the Container will pick that automatically. Moreover, as mentioned above the parameters passed serve just as hints and how many of them are treated (or ignored) by the Container depends on the particular implementation of the Container.


Once we have got the configured PicoContainer instance then we have used the getComponentInstance method (this method is a member of the PicoContainer interface and its signature is 'Object getComponentInstance(Object componentKey);' and it finds the component registered with the specified key) to fetch the well-built instances of the classes having all their dependencies injected into them. Notice that we have used 'DisplayAccountImpl.class' to fetch the instance of type 'DisplayAccountImpl' as we registered the instance with the same key.


Now we can use those instances as per the application requirements - in our case we are using the instance to call a public method named 'displayAccountDetails'.


The code-snippets listed above is just to give you an idea of how can we actually use a IoC Container to exploit the Constructor Injection feature. I've not compiled or tested the code. You may probably need to make some changes, but I suppose most of them would be cosmetic in nature.


Liked the article? You may like to Subscribe to this blog for regular updates. You may also like to follow the blog to manage the bookmark easily and to tell the world that you enjoy GeekExplains. You can find the 'Followers' widget in the rightmost sidebar.



Share/Save/Bookmark


No comments: