Dependency Injection

Dependency Injection was the main principle behind the invention of the Spring framework. It is the heart of the Spring framework. That’s why it’s so essential to get in-depth knowledge of the DI concept. It helps you to understand how Spring works internally.

Most Spring developers use the term Dependency Injection (DI) and Inversion Of Control (IOC) interchangeably. However, there is a difference between the two, especially from the design point of view.

 

Dependency Injection

In this article, we are going to get more in-depth details of dependency injection and related concepts. At the end of this article, you are going to have thoroughly understood them without any doubts.

How the DIP, IOC, and Dependency Injection (DI) correlate?

Before moving further, It’s essential to understand the concept from where the Dependency Injection (DI) and Inversion Of Control (IOC) are originated. It is the Dependency Inversion Principle (DIP).

In the past articles, we learned DIP and IOC in detail. The DIP talks about removing the dependency between modules (the What part). The IOC provides a way to do that (the How part).

The missing part in IOC was a mechanism to provide the dependencies. Many design patterns do this job. DI is one of them.

Developers often use the term Design Pattern and Design Principle equally. The difference is as follows.

  • Design Principle: Talks about what to do.
  • Design Pattern: Provide clear guidelines on how to do.

Dependency Injection is a design pattern, whereas DIP and IOC are design principles.

What is Dependency Injection (DI)?

DI helps in inverting the object creation process from the main program to another entity or framework. Dependency Injection is the act of providing the dependent object.

The client object (where the dependency is injected) defines the abstraction to set the dependency object. The actual implementation of dependency is resolved at runtime.

With DI, the client object calls the dependent object whenever needed instead of the dependent object calls it. This is similar to the Hollywood principle: “Don’t call us, we’ll call you on when we need to.”

Dependency Injection Type

The client object defines various entry points from where the dependency can be injected. Based on these entry points, DI is categorized in the following three ways. 

  • Construction injection
  • Setter injection
  • Interface injection

Construction injection

As its name suggests, the constructor is the entry point. This is a widely used way of injecting dependency. The client object defines a public constructor to pass the dependency object.

Since constructor injection sets all the dependencies during object initialization, you need to pass all the dependencies in the client object’s constructors. Few points about construction injection as follows:

  • Reduce circular dependency: Construction injection supply the dependencies during client object creation. We can catch the circular dependency during the system startup. This eventually helps in reducing circular dependency.
  • Dependency management: All mandatory dependencies can be supplied through constructor injection. It helps in achieving proper dependency management.
  • Lifecycle management: Since the constructor is called exactly once, the dependency object doesn’t be changed during the client object’s lifespan.

However, when you have many dependencies, passing all of them in the constructor is not the best way. You need to identify optional dependency and pass them through setter injection.

Let’s apply constructor injection to the BI system example we saw in the last article. The import and export data module’s object (dependency) can be passed to the DataAnalysis module with constructor injection as follows:

public class DataAnalysis {
    private ExportData exportData = null;
    private ImportData importData = null;

    // Passing dependency into constructor through abstraction
    public DataAnalysis(ExportData exportData, ImportData importData) {
        super();
        this.exportData = exportData;
        this.importData = importData;
    }

    public void processDataAnalysis(int inputMethod, int outputMethod) {
        List<Object[]> dataLst = importData.importData();
        exportData.exportData(dataLst);
    }
}

Setter injection

In this type, the public setter methods are the entry points to inject the dependencies. Some of the dependencies can be injected after client object creation. Such dependencies are called optional or conditional dependencies.

Setter injection is the best option for optional or conditional dependency. You need to consider a few things as follows:

  • You need to write a separate setter method for each optional dependency.
  • Setter methods need to be called explicitly after the client object is created to set the dependencies before using it.
  • Dependency can be overridden by calling the setter method at a later point in time.

Let’s apply dependency through setter injection as follows:

public class DataAnalysis {
    private ExportData exportData = null;
    private ImportData importData = null;

    public void setExportData(ExportData exportData) {
        this.exportData = exportData;
    }

    public void setImportData(ImportData importData) {
        this.importData = importData;
    }

    public void processDataAnalysis(int inputMethod, int outputMethod) {
        List<Object[]> dataLst = importData.importData();
        exportData.exportData(dataLst);
    }
}

Interface injection

In constructor and setter injection, the client object provides an entry point to supply the dependencies. While in the interface injection, the dependency provider defines a way to pass the dependency.

The dependency provider (entity or framework that provides the dependency) defines an interface (abstraction). The client object needs to implement this interface to get the dependency.

This is an interface injection – which means passing the dependency through an interface defined by the dependency provider. Though technically it’s similar to the setter injection, it’s not widely used compared to the other two types.

After applying interface inversion, our code looks as follows:

public interface ImportAndExport { // The provider defined interface.

    // Common method implemented by all low level modules (exporters)
    public File exportData(List<Object[]> dataLst);

    // Common method implemented by all low level modules (importers)
    public List<Object[]> importData();
}

public class DataAnalysis implements ImportAndExport { // The client implement the interface.

    private ExportData exportData = null;
    private ImportData importData = null;

    @Override
    public void setExportData(ExportData exportData) { // interface method to set dependency
        this.exportData = exportData;
    }

    @Override
    public void setImportData(ImportData importData) { // interface method to set dependency
        this.importData = importData;
    }

    public void processDataAnalysis(int inputMethod, int outputMethod) {
        List<Object[]> dataLst = importData.importData();
        exportData.exportData(dataLst);
    }
}

The dependency provider defines the ImportAndExport interface. The client (DataAnalysis) object must implement it. The provider then passes the dependencies through the interface method. 

In this type, the interface is the way to pass the dependency, called interface injection.

Summary

  • Dependency Injection is a design pattern to achieve IOC (Inversion Of Control).
  • Three ways to pass dependency are as follows.
    • Construction Injection
    • Setter Injection
    • Interface Injection
  • It’s crucial to understand the underlying concept of DI, IOC, and DIP (Dependency Inversion Principle). This profoundly helps you to start understanding the Spring framework (or any other IOC container) in a better way.
  • Try exploring more examples and apply these ways of passing the dependency to understand DI in more detail.

Recommended For You

About the Author: Nilang

Nilang Patel is a technology evangelist who loves to spread knowledge and helping people in all possible ways. He is an author of two technical books - Java 9 Dependency and Spring 5.0 Projects.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.