How to develop a new IoC Implementation

The IIoCRegistry interface

The IoC Utils API is defined by the IIoCRegistry interface. This interface defines the behavior of the IoC registry, an entity that stores the bindings between the interface and it's implementations. Creating a class that implements this interface is the starting point of any new implementation.

Refer to the IIoCRegistry javadoc documentation for a complete feature description.

The AbstractIoCRegistryImpl abstract class

The AbstractIoCRegistryImpl is a standard base implementation of the IIoCRegistry interface. This class is provided to help the development of new implementations. As such it offers a base implementation of some of it's methods and a full implementation of auxiliary tasks, such as the module searching mechanism.

Using AbstractIoCRegistryImpl as a base implementation one still needs to provide means of:

  • Instantiate the IoC: It's up to the implementation do determine how to instantiate the IoC framework, through a singleton pattern getter.
  • Access service instances (for single and multiple implementation bindings)
  • Manually inject services in the binders

The AbstractIoCRegistryImpl abstract class

For demonstration purposes we will create a mockup implementation that uses a fake IoC framework.

We will use AbstractIoCRegistryImpl as a starting point for the new implementation.

The IoCRegistryMockupImpl class

Here's the class that extends AbstractIoCRegistryImpl:

  public class IoCRegistryMockupImpl extends AbstractIoCRegistryImpl {
  }

There lacking methods implementations from the IIoCRegistry. The following sections shows how to complete the class.

Attributes and initialization

It is wise to use the Singleton DP to make sure that only one registry instance exists. As such, the class needs a private constructor (to prevent uncontrolled instantiation), a private attribute to store the instance and an accessor to that instance. The class will also need to store an instance of the underlying IoC framework. Here's the code:

 public class IoCRegistryMockupImpl extends AbstractIoCRegistryImpl {
  
        /** The registry instance. */
        private IoCRegistryMockupImpl instance;
         
        /** The IoC framework instance. */
        private IoCFakeFramework registry;
  
        /**
        * Default private ctor. Initializes the IoCFakeFramework.
        */
        private IoCRegistryMockupImpl() {
                this.registry = new IoCFakeFramework();
        }

        /**
        * Singleton instance accessor.
        *    
        * @return the registry instance
        */
        static public IIoCRegistry getRegistry() {
                if (this.instance == null)
                        this.instance = new IoCRegistryMockupImpl();
        
                return this.instance;
        }
 }

The singleton instance accessor method initializes the instance (which is declared in AbstractIoCRegistryImpl) by calling the private constructor. The constructor initializes a proprietary registry object from the hypothetical IoC framework (here implemented by the fake class IoCFakeFramework).

Modules Discovery

The service module discovery mechanism will not be explained here for simplicity's sake. It's assumed that the default mechanism will be used on this implementation. To see the inner workings of this mechanism check the AbstractIoCRegistryImpl javadoc.

The module discovery mechanism provides three search methods (defined in the ModuleParser enumeration):

  • FAST: Uses the class path utils getSystemResources for fast detection.
  • CRAWLER: Searches all class path entries in the file system, opening up directories and ZIP (JAR) files
  • PARANOID: When all else fails, combines both the previous methods for complete discovery. For "ClassLoader nightmare" solving, but slow.

The service module discovery mechanism returns a list of classes found from the configurations files.

It is possible to override the default service module discovery mechanism with a custom one or with one supplied by the IoC framework if it's available.

Implementing required methods

Three methods must be implemented by the user. These are implementation-dependent and could not be included in AbstractIoCRegistryImpl.

getImplementation(Class<T>)

getImplementation(Class<T>) is the getter for single-implementation services. It relies on the underlying IoC framework services to get an implementation for a given service.

...
        /**
        * @see pt.digitalis.utils.ioc.IIoCRegistry#getImplementation(java.lang.Class) 
        */
        public <T> T getImplementation(Class<T> serviceClass) {
                return this.registry.getInstance(serviceClass);
        }
...
getImplementationsMap(Class<T>)

getImplementationsMap(Class<T>) is the getter for multiple-implementation services. Not all IoC frameworks support multiple-implementation services, so a workaround might be needed if you wish to support this feature. The provided Google Guice implementation uses an annotation-based strategy that relies on different ID values for each contribution. The example assumes that the fake IoC framework offers a method that returns the map with all the implementations for a given service. The IoC Utils getImplementationsMap just calls this method in a delegation fashion.

...
        /**
        * @see pt.digitalis.utils.ioc.IIoCRegistry#getImplementationsMap(java.lang.Class)
        */
        public <T> Map<String, T> getImplementationsMap(Class<T> serviceClass) {
                return this.registry.getInstances(serviceClass);
        }
...
getImplementation(Class<T>, String)

getImplementation(Class<T>, , String) is the getter for multiple-implementation services based on ID. This implementation uses the services of getImplementationsMap to access the map of all implementations and then uses the map key to access the implementation with the given ID.

...
        /**
        * @see pt.digitalis.utils.ioc.IIoCRegistry#getImplementation(java.lang.Class, java.lang.String)
        */
        public <T> T getImplementation(Class<T> serviceInterface, String id) {
                return this.getImplementationsMap(serviceInterface).get(id);
        }
...    
injectServices(Object)

injectServices(Object) is an utility method that injects service implementation in a manually created object instances. Once again it assumes that such a service is available on the fake IoC framework and the injectServices method delegates the control flow to the framework's service. It is recommended that the used framework is investigated to check if this feature is provided.

...
        /**
        *  @see pt.digitalis.utils.ioc.IIoCRegistry#injectServices(java.lang.Object)
        */
        public void injectServices(Object obj) {
                this.registry.injectServices(obj);
        }
...

This ends our implementation and shows how easy is to create a custom IoC implementation for IoC Utils.

The complete listing of the example implementation below. If you would like to try the code, you can download the java source file here.

 public class IoCRegistryMockupImpl extends AbstractIoCRegistryImpl {
  
        /** The registry instance. */
        private IoCRegistryMockupImpl instance;
  
        /** The IoC framework instance. */
        private IoCFakeFramework registry;
  
        /**
        * Default private ctor. Initializes the IoCFakeFramework.
        */
        private IoCRegistryMockupImpl() {
                this.registry = new IoCFakeFramework();
        }

        /**
        * Singleton instance accessor.
        *    
        * @return the registry instance
        */
        static public IIoCRegistry getRegistry() {
                if (this.instance == null)
                        this.instance = new IoCRegistryMockupImpl();

                return this.instance;
        }
 
        /**
        * @see pt.digitalis.utils.ioc.IIoCRegistry#getImplementation(java.lang.Class) 
        */
        public <T> T getImplementation(Class<T> serviceClass) {
                return this.registry.getInstance(serviceClass);
        }
        
        /**
        * @see pt.digitalis.utils.ioc.IIoCRegistry#getImplementationsMap(java.lang.Class)
        */
        public <T> Map<String, T> getImplementationsMap(Class<T> serviceClass) {
                return this.registry.getInstances(serviceClass);
        }
             
        /**
        * @see pt.digitalis.utils.ioc.IIoCRegistry#getImplementation(java.lang.Class, java.lang.String)
        */
        public <T> T getImplementation(Class<T> serviceInterface, String id) {
                return this.getImplementationsMap(serviceInterface).get(id);
        }
         
        /**
        *  @see pt.digitalis.utils.ioc.IIoCRegistry#injectServices(java.lang.Object)
        */
        public void injectServices(Object obj) {
                this.registry.injectServices(obj);
        }
 }