[Spring] (1) The core idea of Spring design


1. Getting to know Spring for the first time

1.1 What is Spring

Spring is an open source, lightweight Java framework designed to simplify the development of Java applications. It provides a comprehensive programming and configuration model for building various types of applications, including enterprise applications and stand-alone Java applications. Spring's design philosophy is based on Dependency Injection and Aspect-Oriented Programming, making application development more modular, flexible and easy to maintain .

Summarizing Spring in one sentence is: Spring 是一个包含了众多工具的 IoC 容器.

1.2 What is a container

The so-called container is a vessel that can hold things, just like a water cup. And Spring is a container, its function is to store and manage each component (Bean) in the application, and then take out the required Bean object from Spring when using it.

1.3 What is IoC

IoC, Inversion of Control (Inversion of Control), is one of the core concepts of the Spring framework . It reverses the control of the application from the developer's hands, and the Spring container is responsible for managing the life cycle of objects and dependencies. In traditional programming, developers need to be responsible for manually creating and managing objects, but in the IoC container, developers only need to define the configuration metadata of components (Bean), and the container is responsible for instantiating, assembling and managing these components .

2. In-depth understanding of IoC

If you understand IoC for the first time, you may find it difficult. You can use the following example to help us understand the role of IoC.

If, now you need to use a program to simulate a simple car manufacturing process, the implementation idea is as follows:

  • If you want to build a car, you first need to have a body Framework;
  • If you need to build a body framework, you first need to have a site Bottom;
  • If you need to build a site Bottom, you first need to have wheels Tire.

In the above process, it is not difficult to find that there is a dependency chain relationship. First, through the traditional program development method, let's feel the existing problems.

2.1 Problems in traditional program development methods

Here's the code written in the traditional way:

class Tire {
    
    
    private Integer size = 17;

    public void init(){
    
    
        System.out.println("do tire: size = " + size);
    }
}

class Bottom {
    
    
    private Tire tire;
    public Bottom(){
    
    
        this.tire = new Tire();
    }

    public void init(){
    
    
        tire.init();
        System.out.println("do bottom");
    }
}

class Framework {
    
    
    private Bottom bottom;
    public Framework(){
    
    
        bottom = new Bottom();
    }

    public void init(){
    
    
        bottom.init();
        System.out.println("do framework");
    }
}

 class Car {
    
    
    private Framework framework;
    public Car(){
    
    
        framework = new Framework();
    }

    public void init(){
    
    
        framework.init();
        System.out.println("do car");
    }
}


public class Tradition {
    
    
    public static void main(String[] args) {
    
    
        Car car = new Car();
        car.init();
    }
}

In the traditional way of writing above, it can be found that the coupling between each class is very high, and each class needs to manage the objects it depends on, that is, it has control over the objects it depends on. At this time, if the user's needs change, the Tire wheel used is no longer a fixed size, but the size needs to be input by the user himself. At this time, the code of the Tire class needs to be changed to:

class Tire {
    
    
    private Integer size = 17;

    public Tire(int size){
    
    
        this.size = size;
    }

    public void init(){
    
    
        System.out.println("do tire: size = " + size);
    }
}

At this time, I found that it doesn’t matter if I don’t change it, but once I change it, the problem will come. Due to the high coupling between the various classes, the code that relies on the former needs to be modified later.


class Bottom {
    
    
    private Tire tire;
    public Bottom(int size){
    
    
        this.tire = new Tire(size);
    }

    public void init(){
    
    
        tire.init();
        System.out.println("do bottom");
    }
}

class Framework {
    
    
    private Bottom bottom;
    public Framework(int size){
    
    
        bottom = new Bottom(size);
    }

    public void init(){
    
    
        bottom.init();
        System.out.println("do framework");
    }
}

 class Car {
    
    
    private Framework framework;
    public Car(int size){
    
    
        framework = new Framework(size);
    }

    public void init(){
    
    
        framework.init();
        System.out.println("do car");
    }
}

If you want to continue to add attributes of the Tire class, such as color, you need to modify it from scratch. So how to solve this defect?

The root cause of this defect in the last code is that the current class takes control of the class it depends on in its own hands, that is, the constructor of the dependent class called inside its own class . Therefore, the solution to this problem is also very simple, that is, not to create the dependent object in its own internal code, but to obtain the dependent object through parameter passing. This is the idea of ​​control transfer, that is, IoC.

2.2 Development of Inversion of Control Program

The following is the code writing method for transferring control, that is, the object that the current class depends on is passed in as a parameter:

class Tire {
    
    
    private Integer size = 17;

    public Tire(int size) {
    
    
        this.size = size;
    }

    public void init() {
    
    
        System.out.println("do tire: size = " + size);
    }
}


class Bottom {
    
    
    private Tire tire;

    public Bottom(Tire tire) {
    
    
        this.tire = tire;
    }

    public void init() {
    
    
        tire.init();
        System.out.println("do bottom");
    }
}

class Framework {
    
    
    private Bottom bottom;

    public Framework(Bottom bottom) {
    
    
        this.bottom = bottom;
    }

    public void init() {
    
    
        bottom.init();
        System.out.println("do framework");
    }
}

class Car {
    
    
    private Framework framework;

    public Car(Framework framework) {
    
    
        this.framework = framework;
    }

    public void init() {
    
    
        framework.init();
        System.out.println("do car");
    }
}

public class IoC {
    
    
    public static void main(String[] args) {
    
    
        Tire tire = new Tire(18, "red");
        Bottom bottom = new Bottom(tire);
        Framework framework = new Framework(bottom);
        Car car = new Car(framework);
        car.init();
    }
}

At this time, if the user's needs change again, the color of the tire is required to be selected by oneself. The code to be changed at this time is as follows:

class Tire {
    
    
    private Integer size = 17;
    private String color;

    public Tire(int size, String color) {
    
    
        this.size = size;
        this.color = color;
    }

    public void init() {
    
    
        System.out.println("do tire: size = " + size + ", color = " + color);
    }
}

At this point, only the Tire class and the code to create the Tire object need to be modified, and no other code needs to be modified. It can be found that even the change of the underlying class will not affect the change of the entire call chain, so that the decoupling of the code is realized , thereby realizing a more flexible and general programming method.

2.3 Comparative summary

The following is the calling process of traditional code and IoC form code:

It is not difficult to find out by comparing the implementation of the two codes:

  1. The order of class creation in the two codes is reversed:
    • The creation order of the traditional code is from the top-level Car class to the bottom-level Tire class, namely: Car -> Framework -> Bottom -> Tire;
    • The creation sequence of the IoC form code is from the bottom Tire class to the top Car class, namely: Tire -> Bottom -> Frame -> Car.
  2. In traditional code, the current class controls the objects it depends on, and the coupling degree is very high. Once the underlying code is modified, the code of the entire call chain will be modified .
  3. In the code of IoC form, the current class will reverse the control of the dependent object through the method of parameter passing, that is, it no longer has its own control. At this time, no matter how the underlying code is modified, it will not affect the entire call chain. Code is affected .

3. Understanding of Spring IoC

The understanding of the phrase "Spring is an IoC container that includes many tools" is very important, because it reveals the core functions and advantages of the Spring framework. Here, we can compare the Spring IoC container to a large container for storing various objects (Bean) in the application. This container is not only responsible for the management of the object life cycle, but also implements dependency injection (DI) between objects, thus realizing inversion of control.

Since Spring is a container, its core functions can be divided into two:

1. Store the object in the container

  • Programmers first need to create various objects (Bean) in the application, these objects represent different components and functional modules in the application.
  • Then tell the Spring IoC container how to create these objects and the dependencies between them through configuration files (such as XML, annotations, or Java configuration classes).
  • According to the configuration information, the Spring IoC container is responsible for instantiating these objects and storing them in the container when the application starts , thereby completing the object creation and assembly process.

2. Remove the object from the container

  • Once objects are stored in the Spring IoC container, they can be retrieved and used in other parts of the application. Programmers do not need to explicitly create objects, but directly obtain the created objects from the Spring container for use.
  • Through dependency injection, the Spring IoC container will automatically inject dependent objects where they are needed when appropriate.

In general, the Spring IoC container is responsible for managing object creation, assembly, and dependencies, and developers only need to focus on object definition and configuration . By storing objects in the container and obtaining objects from the container, Spring realizes the inversion of control of the object, making the development of the application program more concise, flexible and easy to maintain . This is also one of the cores of the Spring framework, providing developers with a powerful and highly customizable development platform.

4. The concept of DI

4.1 What is DI

Dependency Injection (Dependency Injection, DI) is a specific technology to achieve IoC, it is a form of expression of the IoC container . In DI, the dependency relationship between objects is no longer created and managed by the class itself, but an external container (such as Spring) is responsible for injecting the dependent objects. Simply put, DI is to hand over the dependencies of an object to an external container for processing, so as to achieve decoupling between objects.

In DI, there are usually three injection methods:

  1. Constructor Injection : Receive dependent objects through constructors, which is the most common way of injection.
  2. Setter method injection (Setter Injection) : Set dependent objects through the Setter method.
  3. Field Injection : Inject dependent objects through attributes.

DI makes object dependencies out of code, configurable and flexible. By using DI, we can achieve loose coupling between different parts of the application, improving the testability, maintainability and scalability of the code.

4.2 Relationship between DI and IoC

DI (Dependency Injection) and IoC (Inversion of Control) are closely related concepts and are usually mentioned at the same time . The relationship between them can be summarized as:

  • IoC is a design idea that transfers control of an application from within the code to an external container. With IoC, the creation of objects and the management of dependencies are reversed, leaving the responsibility of the container instead of the explicit control of the code.
  • DI is a specific technical means to realize IoC, and it is a form of expression of IoC. Through DI, the dependencies between objects are injected by external containers, rather than the objects themselves creating and managing dependencies.

Therefore, DI is a part of IoC, and it is an important means to realize IoC. The Spring framework takes IoC and DI as the core, and provides a powerful IoC container and dependency injection mechanism, thereby realizing various functions, such as dependency management, AOP, transaction management, etc. Through IoC and DI, the Spring Framework enables loosely coupled, configurable, and extensible application development.

Guess you like

Origin blog.csdn.net/qq_61635026/article/details/132051958