Java: Control Inversion (IoC) and Dependency Injection (DI)

Time:2019-8-12

For a long time, I was vague about the two concepts of control inversion and dependency injection. Closing my eyes and thinking about it, I always felt dizzy. But in order to be a good Java engineer, it took me a week to figure them out.

01. Tight Coupling

In the process of coding, we usually need two or more classes to cooperate with each other to achieve business logic. That is to say, an object needs to obtain references to its cooperating objects. If this acquisition process needs to be implemented by itself, the code coupling will be high and the cost of maintenance will be high.

Let’s simulate it through actual combat. If Lao Wang is the chairman of Shaolin Temple, he wants the second monk to sweep the Damo courtyard. The code can be implemented in this way.

The code for Class II is as follows:

public class Xiaoer {
    public void saodi() {
        System.out.println("I'm in the courtyard of Saudamo in the second year";
    }
}

The code of Lao Wang class is as follows:

public class Laowang {
    public void mingling() {
        new Xiaoer().saodi();
    }
}

The code for the test class is as follows:

public class Test {

    public static void main(String[] args) {
        Laowang laowang = new Laowang();
        laowang.mingling();
    }

}

The Mingling method of the Laowang class uses the new keyword to create an object of the Xiaoer class, which is highly coupled and costly to maintain. Why?

One day, the land of the Damo courtyard was dirty again. Lao Wang thought of the second monk, but the second monk went to practice Yijin Jing and let anyone sweep the floor. Lao Wang thought of the third monk, so Laowang had to make a new order, so the code became like this:

public class Xiaosan {
    public void saodi() {
        System.out.println("Junior Three, I'm sweeping the courtyard of Damo";
    }
}
public class Laowang {
    public void mingling() {
        new Xiaoer().saodi();
    }

    public void mingling1() {
        new Xiaosan().saodi();
    }
}

If the third monk had gone to fetch water, Lao Wang might have ordered the fourth monk to sweep the courtyard of Damo. If this goes on, the Laowang class will go crazy.

Lao Wang felt uncomfortable that his next order to sweep the floor was so troublesome because he was a great monk.

02. Control Inversion

We have to do something for Lao Wang, right?

Let’s leave the sweeping job to Lao Wang’s brother and teacher, Lao Fang. Lao Fang is in charge of calling the second monk or the third monk or the fourth monk to carry out the orders presided over by Lao Wang. The code can be implemented in this way.

Define an interface for a sweeping monk. The code is as follows:

public interface Heshang {
    void saodi();
}

The code modifications for the small two categories are as follows:

public class Xiaoer implements Heshang {

    @Override
    public void saodi() {
        System.out.println("Sophomore I'm in the courtyard of Saddamo";"Sophomore I'm in the courtyard of Saddamo";"Sophomore I'm in the courtyard of Saddamo."
    }

    public boolean isYijinjing() {
        // On Wednesday, the second-year monk had to practice Yi Jin Jing.
        return false;
    }
}

The code modifications for the small three categories are as follows:

public class Xiaosan implements Heshang {

    @Override
    public void saodi() {
        System.out.println("Junior Three I'm sweeping the courtyard of Damo";"
    }
}

The code of the old square class is as follows:

public class Laofang {
    public static Heshang getSaodiseng() {
        Xiaoer xiaoer = new Xiaoer();
        if (xiaoer.isYijinjing()) {
            return new Xiaosan();
        }
        return xiaoer;
    }
}

If the old side confirms that the second monk is practicing Yi Jin Jing, it is called the third monk.

The code modification of Lao Wang class is as follows:

public class Laowang {
    public void mingling() {
        Laofang.getSaodiseng().saodi();
    }
}

The code of the test class remains unchanged, as follows:

public class Test {

    public static void main(String[] args) {
        Laowang laowang = new Laowang();
        laowang.mingling();
    }

}

Lao Wang is much more careful now. He just orders who should be called to sweep the courtyard of Damo. His teachers and brothers, Lao Fang, are responsible for it.

The way we think for Lao Wang is called Inversion of Control (IoC), which is not a technology, but an idea that guides us to design loosely coupled programs.

Controlling inversion can be divided into “control” and “inversion” in terms of meaning. When it comes to control, we must find out the subject and object, who controls whom; when it comes to inversion, we must know what positive inversion is.

You see, under the tight coupling situation, Lao Wang will create the dependent objects by using the new keyword when he gives the order (second or third monks); and after the control reversal, Lao Wang will find the sweeping monk who is in charge of his teachers and brothers, Lao Fang, that is to say, the control is handed over to Lao Fang, is it reversed?

03. Dependency Injection

Dependency Injection (DI) is the main way to control inversion: in the process of creating instances of class A, dependent B objects are created, and different objects are injected into different attributes by type or name. There are probably three specific forms of implementation:

1) Based on constructor. Constructors that implement specific parameters pass in objects of the type they depend on when they are newly created.

The code modification of Lao Wang class is as follows:

public class Laowang {
    private Heshang saodiseng;

    public Laowang(Heshang saodiseng) {
        this.saodiseng = saodiseng;
    }
    public void mingling() {
       this.saodiseng.saodi();
    }
}

The code modification of the test class is as follows:

public class Test {

    public static void main(String[] args) {
        Laowang laowang = new Laowang(new Xiaosan());
        laowang.mingling();
    }

}

At this time, the control is in the hands of the test class. It decides whether to send the second monk or the third monk to carry out Lao Wang’s sweeping orders.

2) Set-based method. Implement a public set method for a specific property that allows external containers to call objects of the type they depend on.

The code modification of Lao Wang class is as follows:

public class Laowang {
    private Heshang saodiseng;

    public Heshang getSaodiseng() {
        return saodiseng;
    }

    public void setSaodiseng(Heshang saodiseng) {
        this.saodiseng = saodiseng;
    }

    public void mingling() {
       this.getSaodiseng().saodi();
    }
}

The code modification of the test class is as follows:

public class Test {

    public static void main(String[] args) {
        Laowang laowang = new Laowang();
        Xiaosan xiaosan = new Xiaosan();
        laowang.setSaodiseng(xiaosan);
        laowang.mingling();
    }

}

At this time, the control is still in the hands of the test class. It decides to send the second or third monks to carry out Lao Wang’s sweeping orders.

3) Interface-based. Implementing specific interfaces to inject dependent types of objects into external containers is more complex than constructors and set methods, which are skipped here.

Some people may equate control inversion with dependency injection, but they are essentially different: control inversion is an idea, and dependency injection is a form of control inversion.

04. Spring Framework

Once we understand the concepts of inversion of control and dependency injection, we can take a look at the well-known Spring framework by the way. Controlling inversion is the core of Spring framework and runs through the whole process. There are two implementations of dependency injection in Spring: set mode (value passing mode) and constructor mode (reference mode).

First, we need to add Spring’s dependencies to the pom. XML file. The code is as follows:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>4.3.2.RELEASE</version>
</dependency>

Secondly, we modify the Laowang class as follows:

public class Laowang {
    private Heshang saodiseng;

    public Laowang(Heshang saodiseng) {
        this.saodiseng = saodiseng;
    }
    public void mingling() {
       this.saodiseng.saodi();
    }
}

Then, we create a Spring configuration file, application. xml, as follows:

<?xml version="1.0" encoding="UTF-8"?>
<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="laowang" class="com.cmower.java_demo.ioc.Laowang">
    <constructor-arg ref="saodiseng" />
  </bean>

  <bean id="saodiseng" class="com.cmower.java_demo.ioc.Xiaosan" />

</beans>

Two objects are allocated through elements, one is presided over by Lao Wang and the other is a small three monk. The elements are used to take the small three monks as the structural parameters of Lao Wang’s presiding over.

After the preparation is completed, let’s test it. The code example is as follows:

public class Test {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        Laowang laowang = (Laowang) context.getBean("laowang");
        laowang.mingling();
    }

}

You see, we handed over control to the IoC framework Spring, which also perfectly solves the problem of tight code coupling.

05. Finally

To sum up:

1) Controlling inversion is a decoupling idea in software engineering, which transfers control to a third party. At the time of operation, the third party decides to “inject” the specific dependent object into the object of the calling class.

2) Dependency injection can be used as an implementation way to control inversion, passing instance variables into an object.

3) Through the IoC framework, the strong coupling relationship between class A and class B can be established at runtime through containers, that is to say, the work of creating B instances can be handed over to containers, and class A can be used only.