What is inversion of control (IOC) which is also known as dependency injection
Inversion of control (IOC) or Dependency Injection (DI) is a term used to resolve component dependencies by injecting an instantiated component to satisfy dependency as opposed to explicitly requesting a component. So components will not be explicitly requested but components are provided as needed with the help of an Inversion of controller containers. This is analogous to the Hollywood principal where the servicing components say to the requesting client code “don’t call us, we’ll call you”. Hence it is called inversion of control.
Most of you all are familiar with the software development context where client code (requesting code) collaborates with other dependent components (or servicing components) by knowing which components to talk to, where to locate them and how to talk with them. This is achieved by embedding the code required for locating and instantiating the requested components within the client code. The above approach will tightly couple the dependent components with the client code. This tight coupling can be resolved by applying the factory design pattern and program to interfaces not to implementations driven development. But the factory design pattern is still an intrusive mechanism because servicing components need to be requested explicitly. Let us look at how dependency injection comes to our rescue. It takes the approach that clients declare their dependency on servicing components through a configuration file (like xml) and some external piece of code assumes the responsibility of locating and instantiating these servicing components and supplying the relevant references when needed to the client code. This external piece of code is often referred to as IOC (aka dependency injection) container or framework.
IOC or dependency injection containers generally control creation of objects (by calling “new”) and resolve dependencies between objects it manages. Spring framework, Pico containers, Hivemind etc are IoC containers to name a few. IOC containers support eager instantiation, which is quite useful if you want self-starting services that “come up” on their own when the server starts. They also support lazy loading, which is useful when you have many services which may only be sparsely used. Here is pseudo code for how IOC would work:
XML declaration (beans.xml) showing objects that need to be instantiated and dependencies between them. Dependency is declared as property element.
<bean id=”FlightReservation” class=”com.FlightReservation” />
<bean id=”FlightReservation” class=”com.HotelReservation” />
<bean id=”TripPlanner” class=”com.TripPlanner”>
<property name=”flight”><ref bean=”FlightReservation”/></property>
<property name=”hotel”><ref bean=”HotelReservation”/></property>
To initialize the container
ClassPathResource res = new ClassPathResource(“beans.xml”);
BeanFactory factory = new XmlBeanFactory(res);
The references to the implementation can be retrieved based on “id” attribute in the xml configuration file and all the dependent components are implicitly instantiated in specified order and setter methods (i.e. setter injection) are called to resolve the dependencies.
Dependencies can be wired by either using Java code or using XML.
Types of Dependency Injections:
There are three types of dependency injections.
Constructor Injection (e.g. Pico container, Spring etc): Injection is done through constructors.
Setter Injection (e.g. Spring): Injection is done through setter methods.
Interface Injection (e.g. Avalon): Injection is done through an interface.
Benefits of IOC:
- Minimises the amount of code in your application. With IOC containers you do not care about how services are created and how you get references to the ones you need. You can also easily add additional services by adding a new constructor or a setter method with little or no extra configuration.
- Make your application more testable by not requiring any singletons or JNDI lookup mechanisms in your unit test cases. IOC containers make unit testing and switching implementations very easy by manually allowing you to inject your own objects into the object under test.
- Loose coupling is promoted with minimal effort and least intrusive mechanism. The factory design pattern is more intrusive because components or services need to be requested explicitly whereas in IOC the dependency is injected into requesting piece of code. Also some containers promote the design to interfaces not to implementations design concept by encouraging managed objects to implement a well-defined service interface of your own.
- IOC containers support eager instantiation and lazy loading of services. Containers also provide support for instantiation of managed objects, cyclical dependencies, life cycles management, and dependency resolution between managed objects etc.