Dependency Injection Design Pattern in Java

Proper dependency management is required for building scalable and maintainable software applications. Dependency Injection (DI) is a design pattern that became popular in object-oriented programming languages. This method allows to build scalable, testable and maintainable systems.

Dependency Injection in Java

 

What Is Dependency Injection (in Java)?

Dependency Injection is a design pattern used in object-oriented programming to make code more modular, testable, and maintainable. It’s a way to provide objects (dependencies) that a class needs from an external source, rather than having the class create them itself.

The Problem Without DI (Tight Coupling)

public class UserService {

    private EmailService emailService;

    public UserService() {

        this.emailService = new EmailService(); // tight coupling

    }

    public void notifyUser(String message) {

        emailService.sendEmail(message);

    }

}

class EmailService {

    public void sendEmail(String message) {

        System.out.println("Sending email: " + message);

    }

}



Issues:

  • You can’t easily change EmailService to something else.
  • You can’t unit test UserService without also testing EmailService.

 

 

Types of Dependency Injection

1. Constructor Injection


public class UserService {

    private EmailService emailService;

    public UserService(EmailService emailService) {

        this.emailService = emailService; // injected dependency

    }

    public void notifyUser(String message) {

        emailService.sendEmail(message);

    }

}

class EmailService {

    public void sendEmail(String message) {

        System.out.println("Sending email: " + message);

    }

}

Now we can inject the dependency externally:


public class Main {

    public static void main(String[] args) {

        EmailService emailService = new EmailService();

        UserService userService = new UserService(emailService); // injecting dependency

        userService.notifyUser("Welcome!");

    }

}


1. Constructor Injection (most common)

Dependencies are passed through the constructor.


public class Car {

    private Engine engine;

    public Car(Engine engine) {

        this.engine = engine;

    }

}



2. Setter Injection

Dependencies are set using public setter methods.

java

CopyEdit

public class Car {

    private Engine engine;

    public void setEngine(Engine engine) {

        this.engine = engine;

    }

}

3. Field Injection (with frameworks like Spring)

Framework injects directly into fields (usually using @Autowired).


public class Car {

    @Autowired

    private Engine engine;

}

Using Dependency Injection with Spring Framework

Spring handles DI for you using annotations like @Component, @Autowired, and @Service.


@Component

public class EmailService {

    public void sendEmail(String message) {

        System.out.println("Sending email: " + message);

    }

}

@Service

public class UserService {

    private final EmailService emailService;

    @Autowired

    public UserService(EmailService emailService) {

        this.emailService = emailService;

    }

    public void notifyUser(String message) {

        emailService.sendEmail(message);

    }

}

And in the configuration:


@SpringBootApplication

public class App {

    public static void main(String[] args) {

        ApplicationContext context = SpringApplication.run(App.class, args);

        UserService userService = context.getBean(UserService.class);

        userService.notifyUser("Welcome to Spring DI!");

    }

}


Key Benefits of Dependency Injection in Java:

  • Dependency Injection pattern allows to modify the code without much of an effort 
  • We can create loosely coupled classes using Dependency Injection
  • By Implementing Dependency Injection we can easily test our code using Junit & Mockito Frameworks

Post a Comment

0 Comments