Dependency Injection (DI)
Understanding Dependencies
Dependencies represent external components that a class requires to function properly. Consider a scenario where a Person class relies on CleanAir:
public class Person {
private CleanAir air;
public Person(CleanAir air) {
this.air = air;
}
}
public class CleanAir {
// Implementation details
}
This direct dependency creates tight coupling. If requirements change and DirtyAir becomes necessary, both the Person class and its instantiation code must be modified. To address this, we can introduce an interface:
public interface Air {
// Interface methods
}
public class CleanAir implements Air {
// Implementation
}
public class Person {
private Air air;
public Person(Air air) {
this.air = air;
}
}
Injection Mechanism
Injection involves providing dependencies to a class from external sources rather than having the class create them internally. This approach enables flexibility and easier testing.
Inversion of Control (IoC)
Control Management
Traditional object creation involves explicit instantiation using new operators. IoC containers manage object lifecycle and dependencies automatically. Instead of manually creating objects, developers configure dependencies through configuration files or annotations.
Inversion Principle
With standard dependency management, objects actively seek their dependencies. IoC reverses this flow: dependencies are provided to objects by the container. This passive reception of dependencies constitutes the inversion aspect.
Practical Implemantation
Spring Framework Example
Project Setup
Create a Maven project with Spring dependencies in pom.xml:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.0.RELEASE</version>
</dependency>
</dependencies>
Interface Definitions
public interface DataAccess {
String generateGreeting(String userName);
}
public interface BusinessService {
void executeService(String userName);
}
Implementation Classes
public class GreetingDao implements DataAccess {
public String generateGreeting(String userName) {
int currentHour = LocalDateTime.now().getHour();
if (currentHour < 6) return "Early morning, " + userName;
if (currentHour < 12) return "Good morning, " + userName;
if (currentHour < 13) return "Good noon, " + userName;
if (currentHour < 18) return "Good afternoon, " + userName;
return "Good evening, " + userName;
}
}
public class ServiceHandler implements BusinessService {
private DataAccess dataAccess;
public void setDataAccess(DataAccess dataAccess) {
this.dataAccess = dataAccess;
}
public void executeService(String userName) {
System.out.println(dataAccess.generateGreeting(userName));
}
}
Configuration File
Create application-config.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="greetingDao" class="com.example.GreetingDao"/>
<bean id="serviceHandler" class="com.example.ServiceHandler">
<property name="dataAccess" ref="greetingDao"/>
</bean>
</beans>
Application Usage
public class Application {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("application-config.xml");
BusinessService service =
(BusinessService) context.getBean("serviceHandler");
service.executeService("TestUser");
}
}
This implementation demonstrates how Spring's IoC container manages dependencies automatically, eliminating the need for explicit object creation and reducing coupling between components.