Dependency Injection (DI) is a design pattern in which an object receives the required objects (dependencies) from an external source instead of creating them itself.
- DI helps reduce tight coupling between classes
- Improves code reusability and testability.
- Widely used in enterprise Java applications
Example: A Car class may require an Engine object to work. Instead of creating the Engine inside the Car class using new Engine(), the Engine object is provided externally by a framework such as Spring.
Dependency Between Classes
A dependency is an object that another object requires to perform its work.

The diagram explains:
- A class may use functionality provided by another class
- The class being used is called a dependency
- Direct object creation can lead to tight coupling
- Changes in one implementation may affect multiple classes
- Dependency Injection helps manage dependencies efficiently
Tight Coupling Problem
Without Dependency Injection
public class LegacyEngine {
public void start() {
System.out.println("Legacy Engine Started");
}
}
public class Car {
LegacyEngine engine = new LegacyEngine();
public void drive() {
engine.start();
}
}
Problems with This Approach
- Car is tightly coupled to LegacyEngine
- Difficult to replace the engine implementation
- Code changes are required in multiple places
- Harder to test and maintain
- Less flexible design
Suppose a new engine model is introduced.
public class NewEngine {
public void start() {
System.out.println("New Engine Started");
}
}
Now the Car class must be modified manually.
public class Car {
NewEngine engine = new NewEngine();
}
magine hundreds of classes using the old engine implementation. Updating all these classes manually becomes difficult and time-consuming. This is the problem that Dependency Injection solves.
Dependency Injection and Inversion of Control

Dependency Injection
Dependency Injection provides the required dependency object from an external source instead of creating it manually inside the class.
Instead of:
Engine engine = new DieselEngine();
The dependency is injected externally.
Inversion of Control (IoC)
Inversion of Control is a principle in which the responsibility of creating and managing objects is transferred from the application code to a framework or container.
In Spring:
- The IoC Container creates objects
- Manages dependencies
- Injects required dependencies automatically
This reduces manual object creation and improves loose coupling.
Example of Dependency Injection Design Patterns in Java
Problem Statement
A vehicle such as a Car depends on multiple components like:
- Engine
- Headlights
- Brakes
These components are called dependencies. Instead of creating these dependencies manually inside the class, Dependency Injection provides them externally using frameworks such as Spring.

These components can be considered as your dependencies. Without any of these dependencies, our car can never be considered as a complete entity.
- The left side represents multiple dependencies required by an application.
- The “Engine” acts as a dependency used by different vehicle classes.
- The “External Agent” represents the Spring Framework or IoC Container.
- The framework injects the required dependency into classes like Car, Bike, and Scooter.
- This reduces tight coupling and makes the code more reusable and maintainable
Step by Step Implementation for the above Problem
In the below solution we will understand above problem using code:
Real-world problem that occurs with Java Dependency Injection Design Pattern

Step 1: Create the Dependency Interface
Define a common interface for all engine types.
- Helps achieve loose coupling
- Supports multiple engine implementations
- Improves flexibility
public interface Engine {
void start();
}
Step 2: Create Dependency Implementation
Create a concrete implementation of the Engine interface.
Legacy Engine
public class LegacyEngine implements Engine {
@Override
public void start() {
System.out.println("Legacy Engine Started");
}
}
New Engine
public class NewEngine implements Engine {
@Override
public void start() {
System.out.println("New Engine Started");
}
}
Step 3: Inject Dependency Using Constructor Injection
public class Car {
private Engine engine;
public Car(Engine engine) {
this.engine = engine;
}
public void drive() {
engine.start();
System.out.println("Car is Running");
}
}
Step 4: Use Different Implementations
public class Main {
public static void main(String[] args) {
Engine engine = new NewEngine();
Car car = new Car(engine);
car.drive();
}
}
Step 5. Run Application
Output
New Engine Started
Car is Running
Now the Car class does not depend on a specific engine implementation. The same dependency can be reused across:
- Car
- Bike
- Scooter
without modifying their source code.

Dependency Injection Using Spring Framework
Spring Framework provides automatic dependency management using the IoC Container.
The framework:
- Creates dependency objects
- Manages object lifecycle
- Injects required dependencies automatically
Step 6: Create Configuration Class
Use Spring Framework to manage dependencies.
- @Configuration marks the configuration class
- @Bean creates and manages dependency objects
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class BeanConfig {
@Bean
public Engine engine() {
return new NewEngine();
}
}
Step 7: Inject Dependency into Car Class
Use @Autowired to inject dependency automatically.
- Spring injects dependencies automatically
- No manual object creation required
- Supports loose coupling
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Car {
private final Engine engine;
@Autowired
public Car(Engine engine) {
this.engine = engine;
}
public void drive() {
engine.start();
System.out.println("Car is Running");
}
}
Step 8: Reuse Dependency in Other Classes
The same dependency can be injected into other vehicle classes.
- Same dependency reused across multiple classes.
- Reduces duplicate code changes.
Bike class:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Bike {
private final Engine engine;
@Autowired
public Bike(Engine engine) {
this.engine = engine;
}
}
Scooter class:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Scooter {
private final Engine engine;
@Autowired
public Scooter(Engine engine) {
this.engine = engine;
}
}
Step 9: Add Spring Dependency in pom.xml
- Adds Spring MVC support to the project.
- Provides annotations like
@Autowiredand@Bean
pom.xml:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
Step 7: Run the Spring Boot Application
When the application starts, the Spring IoC Container:
- Creates the Engine object
- Injects it into the Car class
- Executes the drive() method
Main Class
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ApplicationContext context =
SpringApplication.run(DemoApplication.class, args);
Car car = context.getBean(Car.class);
car.drive();
}
}
Output
New Engine Started
Car is Running