Spring Detailed Explanation - The Difference Between IOC and DI

1. Preface to this chapter

As long as we mention the word Spring, people who have a brief understanding of Spring will basically blurt out the concepts of IoC, DI and AOP. But for beginners, it is quite troublesome to understand the concepts of IoC and DI at once. For example, when I just learned Spring, I only knew that IoC can help us create objects, and we no longer need to create them ourselves. At that time, IoC and DI were stupidly unclear, let alone the concept of AOP. So this time we must understand well.

Note: The concepts of IoC and AOP were not proposed by Spring. They existed before Spring came out, but they were more theoretical, and no products were well implemented until the Spring framework carried out these concepts for a long time. Good implementation.

2. What is IoC (Inversion of Control)

IoC (Inversion of Control) means "inversion of control", which is the core point of Spring and runs through it all. IoC is not a technology, but a design idea. It is the Spring IoC container that implements inversion of control in the Spring framework. Specifically, the container controls the life cycle of objects and the dependencies between business objects, rather than direct control by code in the traditional way (new object). All objects in the program will be registered in the Spring IoC container, tell the container what you are and what you need, and then the IoC container will actively give you the objects you want when the system is running properly, and at the same time give you Other objects that need you. That is to say, it is no longer the object that references it that controls the life cycle of the object, but the Spring IoC container controls the creation and destruction of all objects. For a specific object, it used to control other objects, but now all objects are controlled by the Spring IoC container, so this is called inversion of control.

The most intuitive expression of inversion of control is that the IoC container allows the creation of objects without going to new, but is automatically produced by Spring, using the reflection mechanism of java, dynamically creating and managing objects according to the configuration file at runtime, and calling method of the object. The essence of inversion of control is that the control right is transferred from the application code to the external container (IoC container), and the transfer of control right is the so-called inversion. The benefit of the transfer of control rights is to reduce the dependence between business objects, that is, to achieve decoupling. Now that inversion is mentioned in inversion of control, there must be forward rotation. What is the difference between forward rotation and reverse rotation? I once saw on a blog that someone was asked about Spring IoC knowledge points during an interview: what is reverse and forward?

  • Positive: If we want to use an object, we need to be responsible for the creation of the object.
  • Inversion: If you want to use an object, you only need to obtain the object you need to use from the Spring container, and don't care about the object creation process, that is, the control of creating the object is reversed to the Spring framework.

3. What is DI (Dependency Injection)

DI (Dependency Injection) means "Dependency Injection", which is an alias for IoC (Inversion of Control) . In the early years, the godfather of software development Martin·Fowlermentioned in an article that IoC was renamed DI. This is the original address: https://martinfowler.com/articles/injection.html. There is such a passage, as shown in the following figure:

image

Meaning: He thinks a more specific name needs to be given to this pattern (IoC). Because inversion of control is such an overly general term, people get confused. He had a lot of discussions with IoC advocates, and they settled on the name Dependency Injection. That's when the word DI (Dependency Injection) became known to everyone. I also mentioned in the first chapter that IoC and DI are actually the same concept, but they are described from different perspectives (IoC is an idea, while DI is a specific technical implementation method).

This is a sentence we saw in other places. This sentence is really enlightening. In one sentence, it makes a lot of difficult words for others clear: IoC is the purpose (its purpose is to create objects) , DI is the means (by what means to obtain external objects). So at this point, let's stop being stupid and unclear about IoC and DI.

Dependency injection: That is, the application relies on the IoC container to dynamically inject the external resources required by the object at runtime. In dependency injection, "who depends on whom, why does it need to depend, who injects whom, and what is injected", let's analyze it in depth:

●Who depends on whom: Of course, the application depends on the IoC container;

●Why do you need to depend: the application needs the IoC container to provide the external resources required by the object;

●Who injects whom: It is obvious that the IoC container injects an object of the application, the object that the application depends on;

●What is injected: It is to inject the external resources (including objects, resources, and constant data) required by an object.

Based on the above, we can sum it up in one sentence: the so-called Spring IoC/DI means that the Spring container is responsible for the life cycle of objects and the dependencies between objects.

4. Understanding of Spring IoC

The basic concepts of IoC and DI have been introduced in detail above. In order to better understand them, let's use an example in life to deepen the understanding. Before giving an example, let’s make it clear that there are two ways to handle dependencies:

  • Actively create objects
  • Create objects passively

① Actively create objects

We know that in a traditional Java project, if you need to call another object's method internally in one object, the most commonly used method is to use it in the main class new 对象. Of course, we can also use the simple factory pattern to achieve, that is, in the simple factory pattern, our dependent class is created by a factory method, the dependent subject first calls the factory method of the dependent object, and then actively accesses the dependent object based on the factory, but There is still a problem with this method, that is, there is a coupling between the dependent subject and the factory of the dependent object. The program idea diagram of actively creating objects is as follows:

image

Example: This is a chestnut I saw in the book "Java EE Internet Lightweight Framework Integrated Development" I bought. I think the author's chestnut is easy to understand because it comes from life. For example, if we usually want to drink a glass of lemon juice, without going to the beverage store to buy it, then what we want to get a glass of orange juice is this: buy a juicer, buy oranges, buy a cup, and then prepare water. These are all processes that you "actively" complete, which means that a glass of orange juice needs to be created by yourself. As shown below:

image

②, Passively create objects

Since it is difficult to avoid coupling problems in the way of actively creating objects, some people use containers to manage objects in a unified way through thinking and summarizing, which gradually attracted everyone's attention, and then opened the trend of thought of passively creating objects. It is precisely because of the introduction of containers that applications no longer need to actively create objects. It can be seen that the process of obtaining objects has been reversed, from active acquisition to passive acceptance, which is also a process of inversion of control. The program idea diagram of passive object creation is as follows:

image

Example: Today when beverage stores are so popular, no one will make beverages and milk tea at home! So our first choice is definitely to go outside to buy or take away. At this time, we only need to describe what kind of drink we need (ignoring adding ice and hot sugar), and we don't need to care about how our drink is made. And these are the processes that are "passively" completed by others, that is to say, a drink needs to be passively created by others. As shown below:

image

Through the example in the picture above, we can find that the glass of orange juice we got was not created by ourselves "actively", but created through the beverage store. However, it fully meets your requirements, even better than what you created.

The above example can only show that we don't need to create objects ourselves, so what if it depends on other objects? So what about calling each other between objects? How do we understand it? The following is an example.

If the merchant in this beverage store is a profiteer, in order to save costs, they add additives to the beverage, as shown in the following figure for example:

image

When the main object depends on other objects, the mutual calls between objects are completed by injection, so we will introduce three injection methods in IOC below.

So far, the understanding of Spring IOC/DI has been fully introduced. I don’t know if you understand it or not, or my understanding is wrong. Please give me your advice! ! !

5. Three injection methods of IoC

The most authoritative summary and explanation of the IoC model should be Martin Fowlerthe article "Inversion of Control Containers and the Dependency Injection pattern" by the godfather of software development. The link has been given above, and I will say it again here: https://martinfowler.com /articles/injection.html. Three methods of dependency injection are mentioned in this article, namely constructor injection, setter injection and interface injection.

image

So let's take a closer look at the characteristics of these three methods and the differences between them:

5.1. Constructor injection

Constructor injection, as the name implies, means that the injected object can let the outside (usually the IoC container) know which dependent objects it needs by declaring the parameter list of the dependent object in its construction method.

The IoC Service Provider will check the construction method of the injected object, obtain the list of dependent objects it needs, and then inject the corresponding object into it. It is impossible for the same object to be constructed twice. Therefore, the construction of the injected object and its entire life cycle should be managed by the IoC Service Provider.

The injection method of the construction method is relatively intuitive. After the object is constructed, it enters the ready state and can be used immediately. It's like when you just entered the door of the bar, the waiter has already put your favorite beer on the table. Sit down and enjoy a cool and comfortable immediately.

5.2, set method injection

For JavaBean objects, the corresponding properties are usually accessed through the setXXX() and getXXX() methods. These setXXX() methods are collectively called setter methods, and getXXX() is of course called a getter method. Through the setter method, the corresponding object property can be changed, and through the getter method, the state of the corresponding property can be obtained. Therefore, as long as the current object adds a setter method to the attribute corresponding to its dependent object, the corresponding dependent object can be set to the injected object through the setter method.

Although the setter method injection is not like the construction method injection, which allows the object to be used after the construction is completed, it is relatively looser and can be injected after the object construction is completed. This is like you can sit down at a bar and decide what beer you want. You can order Budweiser or Daxue, which is more optional. If you are not in a hurry to drink, this method is of course the most suitable for you.

5.3. Interface injection

Compared with the first two injection methods, interface injection is not so simple and clear. If the injected object wants IoC ServiceProvider to inject dependent objects into it, it must implement an interface. This interface provides a method for injecting dependent objects into it. The IoC Service Provider finally uses these interfaces to understand what dependent objects should be injected into the injected object.

5.4. Comparison of three injection methods

injection method describe
setter method injection Because methods can be named, setter method injection is more descriptive than constructor method injection. In addition, setter methods can be inherited, allowing default values ​​to be set, and have good IDE support. The disadvantage, of course, is that the object cannot enter the ready state immediately after construction.
constructor injection The advantage of this injection method is that after the object is constructed, it is in a ready state and can be used immediately. The disadvantage is that when there are many dependent objects, the parameter list of the construction method will be relatively long. When constructing objects through reflection, it will be more difficult to deal with parameters of the same type, and it will be more troublesome to maintain and use. And in Java, constructors cannot be inherited and default values ​​cannot be set. For non-essential dependency processing, multiple construction methods may need to be introduced, and changes in the number of parameters may cause inconvenience in maintenance.
interface injection In terms of the use of injection methods, interface injection is a method that is not advocated now, and is basically in a "retired state". Because it forces the injected object to implement unnecessary interfaces, it is intrusive. This is not required for constructor injection and setter method injection.

To sum up, construction method injection and setter method injection are the most commonly used injection methods because they are less intrusive and easy to understand and use, especially setter method injection; and interface injection is more intrusive, basically Has been eliminated.

6. Examples of IoC usage

In the example of IOC, we still use the example of orange juice above. If profiteers use additives in order to save costs, it can be understood that orange juice in beverage stores depends on additives. In actual use, we need to inject additive objects into orange juice objects. middle. Next, I will explain the application of IOC container instances in these ways:

  • original way
  • constructor injection
  • setter method injection
  • interface injection

First, let's create the OrangeJuice class and the Additive class respectively.

Create the OrangeJuice class with the following code:

/**
 * @author tanghaorong
 * @desc 橙汁类
 */
public class OrangeJuice {
    
    
    public void needOrangeJuice(){
    
    
        System.out.println("消费者点了一杯橙汁(无添加剂)...");
    }
}

Create an Additive class with the following code:

/**
 * @author tanghaorong
 * @desc 添加剂类
 */
public class Additive  {
    
    
    public void addAdditive(){
    
    
        System.out.println("奸商在橙汁中添加了添加剂...");
    }
}

6.1, the original way

The most primitive way is that without an IOC container, we need to use new in the main object to obtain the dependent object. Let's take a look at the writing method in the main class, and the additive class remains unchanged:

public class OrangeJuice {
    public void needOrangeJuice(){
        //创建添加剂对象
        Additive additive = new Additive();
        //调用加入添加剂方法
        additive.addAdditive();
        System.out.println("消费者点了一杯橙汁(有添加剂)...");
    }
}

Create a test class:

public class Test {
    public static void main(String[] args) {
        OrangeJuice orangeJuice = new OrangeJuice();
        orangeJuice.needOrangeJuice();
    }
}

The result of the operation is as follows:

image

From the above example, it can be found that the coupling degree of the original method is very high. If the type of additive is changed, the whole glass of orange juice also needs to be changed.

6.2. Constructor injection

Constructor injection, as the name implies, is to inject dependencies through constructors. First, let's look at the spring configuration file:

<?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 definitions here -->

    <!--将指定类都配置给Spring,让Spring创建其对象的实例,一个bean对应一个对象-->
    <bean id="additive" class="com.thr.Additive"></bean>

    <bean id="orangeJuice" class="com.thr.OrangeJuice">
        <!--通过构造函数注入,ref属性表示注入另一个对象-->
        <constructor-arg ref="additive"></constructor-arg>
    </bean>
</beans>

The premise of using constructor injection is to create a constructor in the main class, so let’s take a look at how the constructor expresses dependencies. The code is as follows:

public class OrangeJuice {
    //引入添加剂参数
    private Additive additive;
    //创建有参构造函数
    public OrangeJuice(Additive additive) {
        this.additive = additive;
    }

    public void needOrangeJuice(){
        //调用加入添加剂方法
        additive.addAdditive();
        System.out.println("消费者点了一杯橙汁(有添加剂)...");
    }
}

Create a test class:

public class Test {
    public static void main(String[] args) {
        //1.初始化Spring容器,加载配置文件
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2.通过容器获取实例对象,getBean()方法中的参数是bean标签中的id
        OrangeJuice orangeJuice = (OrangeJuice) applicationContext.getBean("orangeJuice");
        //3.调用实例中的方法
        orangeJuice.needOrangeJuice();
    }
}

The result of the operation is as follows:

image

It can be found that the running result is the same as the original method, but after the right to create objects is handed over to Spring, the coupling between orange juice and additives is significantly reduced. At this time, our focus is on the configuration file, and we don’t care about the program itself. Even if the type of additive changes, we only need to modify the configuration file without modifying the program code.

6.3, set method injection

Setter injection is widely used in actual development, because it can be injected after the object is constructed, which is more intuitive and natural. Let's take a look at the spring configuration file:

<?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 definitions here -->

    <!--将指定类都配置给Spring,让Spring创建其对象的实例,一个bean对应一个对象-->
    <bean id="additive" class="com.thr.Additive"></bean>

    <bean id="orangeJuice" class="com.thr.OrangeJuice">
        <!--通过setter注入,ref属性表示注入另一个对象-->
        <property name="additive" ref="additive"></property>
    </bean>
</beans>

Some elements in the configuration file such as , name, ref, etc. will be introduced in detail later.

Then let's take a look at how the setter expresses the dependency relationship:

public class OrangeJuice {
    //引入添加剂参数
    private Additive additive;
    //创建setter方法
    public void setAdditive(Additive additive) {
        this.additive = additive;
    }

    public void needOrangeJuice(){
        //调用加入添加剂方法
        additive.addAdditive();
        System.out.println("消费者点了一杯橙汁(有添加剂)...");
    }
}

The test class and the result of the operation are the same as the method of constructor injection, so it will not be shown here.

6.4. Interface injection

Interface injection means that the main class must implement an injection interface we created, which will pass in the object of the dependent class to complete the injection.

Since Spring's configuration files only support constructor injection and setter injection, configuration files cannot be used here, and it only serves to help us create objects. Spring configuration file:

<?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 definitions here -->

    <!--将指定类都配置给Spring,让Spring创建其对象的实例,一个bean对应一个对象-->
    <bean id="additive" class="com.thr.Additive"></bean>

    <bean id="orangeJuice" class="com.thr.OrangeJuice"></bean>
</beans>

Create an interface as follows:

//创建注入接口
public interface InterfaceInject {
    void injectAdditive(Additive additive);
}
       主体类实现接口并且初始化添加剂参数:

//实现InterfaceInject
public class OrangeJuice implements InterfaceInject {
    //引入添加剂参数
    private Additive additive;
    //实现接口方法,并且初始化参数
    @Override
    public void injectAdditive(Additive additive) {
        this.additive = additive;
    }

    public void needOrangeJuice(){
        //调用加入添加剂方法
        additive.addAdditive();
        System.out.println("消费者点了一杯橙汁(有添加剂)...");
    }
}

Create a test class:

public class Test {
    public static void main(String[] args) {
        //1.初始化Spring容器,加载配置文件
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2.通过容器获取实例对象,getBean()方法中的参数是bean标签中的id
        OrangeJuice orangeJuice = (OrangeJuice) applicationContext.getBean("orangeJuice");
        Additive additive = (Additive) applicationContext.getBean("additive");
        //通过接口注入,调用注入方法并且将Additive对象注入
        orangeJuice.injectAdditive(additive);
        //3.调用实例中的方法
        orangeJuice.needOrangeJuice();
    }
}

Due to the interface injection method, which forces the injected object to implement unnecessary interfaces, it is very intrusive, so this method has been eliminated.

7. Summarize what benefits IoC brings

The core of the idea of ​​IoC is that resources are not managed by the two parties who use the resources, but by a third party who does not use the resources.

First, centralized management of resources to achieve configurable and easy management of resources

Second, the degree of dependence between the two parties using resources is reduced, which is what we call the degree of coupling

In fact, the biggest change that IoC brings to programming is not from the code, but from the ideological point of view, a "master-slave transposition" change has occurred. The application was originally the boss, and it took the initiative to obtain any resources, but in the IoC/DI thinking, the application became passive, passively waiting for the IoC container to create and inject the resources it needs. IoC well embodies one of the object-oriented design rules of Hollywood: "Don't look for us, we look for you"; that is, the IoC container helps the object find the corresponding dependent object and inject it, rather than the object actively looking for it.

Guess you like

Origin blog.csdn.net/wgzblog/article/details/125456579