SpringBoot Web layered decoupling

Directory

  • Layered decoupling
    • Three-tier architecture
      • introduce
      • code splitting
    • Layered decoupling
      • coupling problem
      • Decoupling ideas
    • IOC (Inversion of Control) & DI (Dependency Injection)
      • Getting Started with IOC &DI
      • Detailed explanation of IOC
        • bean declaration
        • Component scan
      • Detailed explanation of DI

In the case of SpringBoot Web Request Response, it is mentioned that parsing XML data, code for obtaining data, code for processing data logic, and code for responding to the page are all piled together, and they are all written in the controller method. .


The business logic of the current program is relatively simple. If the business logic is a little more complicated, we will see that the amount of code in the Controller method will be very large.

When we want to modify the code for operating data, we need to modify the Controller
When we want to improve the logic processing part of the code, we need to change the Controller
When we need to modify the data response code, we still need to modify the Controller

In this way, the reusability of our entire project code will be relatively poor, and the code will be difficult to maintain. So how to solve this problem?
In fact, in current development, there is a very mature solution, which is layered development.

Hierarchical decoupling

Three-tier architecture

Introduction

Single Responsibility Principle: A class or a method only does one thing and only takes care of one function.
This can make classes, interfaces, and methods less complex, more readable, more scalable, and more cost-effective for later maintenance.

The processing logic of the above case can be divided into three parts in terms of composition:

Data access: Responsible for the maintenance operations of business data, including addition, deletion, modification, query and other operations.
Logic processing: Code responsible for business logic processing.
Request processing, response data: Responsible for receiving page requests and responding to the page with data.

According to the above three components, in our project development, the code can be divided into three layers:

Controller: control layer. Receive requests sent by the front end, process the requests, and respond with data.
Service: business logic layer. Handle specific business logic.
Dao: Data Access Object, also known as persistence layer. Responsible for data access operations, including data
Add, delete, modify, check.

Program execution process based on three-tier architecture:

1. The request initiated by the front end is received by the Controller layer (the Controller responds with data to the front end)
2. The Controller layer calls the Service layer for logical processing (after the Service layer completes processing, the processing results are returned to the Controller layer)
3. The Serivce layer calls the Dao layer (some data needed during logical processing must be obtained from the Dao layer)
4. The Dao layer operates the data in the file (the data obtained by Dao will be returned to the Service layer)

Benefits of three-tier architecture:

1. Strong reusability
2. Easy to maintain
3. Conducive to expansion

Code splitting

Use the three-tier architecture idea to transform the previous program.

Control layer package name: xxxx.controller
Business logic layer package name: xxxx.service
Data access layer package name: xxxx.dao

Control layer controller: Receive requests sent by the front end, process the requests, and respond to the data

@RestController
public class EmpController {<!-- -->
    //Business layer object
    private EmpService empService = new EmpServiceA();

    @RequestMapping("/listEmp")
    public Result list() {<!-- -->
        //1. Call the service layer to obtain data
        List<Emp> empList = empService.listEmp();
        //3. Response data
        return Result.success(empList);
    }
}

Business logic layer service: handles specific business logic

Business interface

//Business logic interface (formulate business standards)
public interface EmpService {<!-- -->
    //Get employee list
    public List<Emp> listEmp();
}

Business implementation class

//Business logic implementation class (implemented according to business standards)
public class EmpServiceA implements EmpService {<!-- -->
    //dao layer object
    private EmpDao empDao = new EmpDaoA();

    @Override
    public List<Emp> listEmp() {<!-- -->
        //1. Call dao and get data
        List<Emp> empList = empDao.listEmp();
        //2. Convert data - gender, job
        empList.stream().forEach(emp -> {<!-- -->
            //Handle gender 1: male, 2: female
            String gender = emp.getGender();
            if ("1".equals(gender)) {<!-- -->
                emp.setGender("male");
            } else if ("2".equals(gender)) {<!-- -->
                emp.setGender("female");
            }
            //Process job - 1: Lecturer, 2: Class Teacher, 3: Employment Guidance
            String job = emp.getJob();
            if ("1".equals(job)) {<!-- -->
                emp.setJob("Lecturer");
            } else if ("2".equals(job)) {<!-- -->
                emp.setJob("Class Teacher");
            } else if ("3".equals(job)) {<!-- -->
                emp.setJob("Employment Guidance");
            }
        });
        return empList;
    }
}

Data access layer dao: Responsible for data access operations, including data addition, deletion, modification, and query

Data access interface

//Data access layer interface (standard setting)
public interface EmpDao {<!-- -->
    //Get employee list data
    public List<Emp> listEmp();
}

Data access implementation class

//Data access implementation class
public class EmpDaoA implements EmpDao {<!-- -->
    @Override
    public List<Emp> listEmp() {<!-- -->
        //1. Load and parse emp.xml
        String file =
                this.getClass().getClassLoader().getResource("emp.xml").getFile();
        System.out.println(file);
        List<Emp> empList = XmlParserUtils.parse(file, Emp.class);
        return empList;
    }
}

Hierarchical decoupling

Learn the idea of decoupling programs.
Decoupling: Decoupling.

Coupling problem

Cohesion: the functional connections within each functional module in the software.
Coupling: Measures the degree of dependence and association between various layers/modules in the software.

Software design principles: high cohesion and low coupling

High cohesion refers to: the closeness of the connection between the various elements in a module. The higher the connection between the various elements (statements, program segments), the higher the cohesion, that is, "high cohesion".
Low coupling refers to: the lower the dependencies between various layers and modules in the software, the better.

Reflection of high cohesion in the program: Only employee-related logic processing code is written in the EmpServiceA class

Reflection of coupling code in the program: When changing the business class to EmpServiceB, you need to modify the code in the controller layer

The purpose of high cohesion and low coupling is to greatly enhance the reusability and portability of program modules:

Decoupling ideas

When we were writing code before, if we needed any object, we could just create a new one. With this approach, the code between layers is coupled. When the implementation of the service layer changes, we also need to modify the code of the controller layer.

So how should we decouple it? First of all, you cannot use the new object in EmpController, which means there is no business layer object (an error will be reported when the program runs). What should I do?

Our solution is:
Provide a container that stores some objects (for example: EmpService objects), and the controller program obtains objects of type EmpService from the container.

If we want to achieve the above decoupling operation, we involve two core concepts in Spring:

Inversion of Control: Inversion Of Control, referred to as IOC. The creation control of the object is transferred from the program itself to the outside (container). This container is called: IOC container or Spring container. This idea is called inversion of control.

Dependency Injection: Dependency Injection, referred to as DI. The container provides the resources that the application depends on during runtime, which is called dependency injection.
When the program needs a certain resource when it is running, the container provides it with this resource.
Example: The EmpController program requires an EmpService object when running, and the Spring container provides and injects the EmpService object.

The objects created and managed in the IOC container are called: bean objects

IOC (Inversion of Control) & DI (Dependency Injection)

Details of Inversion of Control IOC

Introduction to IOC & DI

Task: Complete the code decoupling of the Controller layer, Service layer, and Dao layer

Idea:

1. Delete the code of the new object in the Controller layer and Service layer
2. The implementation classes of the Service layer and Dao layer are handed over to the IOC container for management.
3. Inject runtime dependent objects into Controller and Service
Service layer objects that inject dependencies into the Controller program
Dao layer objects that inject dependencies in the Service program

Step 1: Delete the code of the new object in the Controller layer and Service layer

Step 2: The implementation classes of the Service layer and Dao layer are handed over to the IOC container management
Using the annotation provided by Spring: @Component, you can implement the class and hand it over to the IOC container for management.

Step 3: Inject runtime dependent objects into Controller and Service
Using the annotation provided by Spring: @Autowired, you can realize the IOC container to automatically inject the required dependency objects when the program is running.

Run the SpringBoot boot class and you can find that the program runs normally.

For example, when the Service layer changes and we no longer use EmpServiceA and switch to EmpServiceB, we only need to comment out the @Component annotation in EmpServiceA and add @Component to EmpServiceB without changing the code in the Controller layer.

IOC detailed explanation

bean declaration

The objects created by the IOC container are called bean objects. To hand over an object to the IOC container for management, you need to add an annotation to the class: @Component

In order to better identify which layer the bean object belongs to during web application development, the Spring framework provides @Component derived annotations:

@Controller (marked on the control layer class)
@Service (marked on the business layer class)
@Repository (marked on the data access layer class)

Modify the entry case code, change the Service layer implementation class annotation to @Service, and change the Dao layer implementation class annotation to @Repository

Annotation Summary To hand over an object to the IOC container for management, you need to add one of the following annotations to the corresponding class:

Annotation Description Position
@Controller The derived annotation of @Component is marked on the controller class
@Service The derived annotation of @Component is marked on the business class
@Repository The derived annotations of @Component are annotated on the data access class (due to integration with mybatis, more @Mapper is used)
@Component Declare the basic annotation of the bean Use this annotation when it does not belong to the above three categories

Beans can be declared using the above four annotations, but in springboot integrated web development, controller beans can only be declared using @Controller (or its annotated annotations)

@Service and @Repository source code:

In the IOC container, each Bean has its own name. You can specify the bean name through the value attribute of the annotation:

@Service(value = "ServiceA")
//@Service("ServiceA")

If not specified, the default is to lowercase the first letter of the class name:

Please add image description

Component scanning

Question: Will the beans declared using the four annotations learned previously be effective?
Answer: Not necessarily. (Reason: In order for the bean to take effect, it needs to be scanned by the component)

Next, we test whether the bean object is effective by modifying the directory structure of the project project:

After running the program, an error message appears:

Why is the bean object not found? To be effective, beans declared using the four major annotations need to be scanned by the component scanning annotation @ComponentScan. Although the @ComponentScan annotation is not explicitly configured, it is actually included in the bootstrap class declaration annotation.
In @SpringBootApplication, the default scanning scope is the package containing the SpringBoot startup class and its sub-packages.

Solution: Manually add @ComponentScan annotation and specify the package to be scanned (only for understanding, not recommended)

Recommended approach: Place the controller, service, and dao packages we defined under the sub-package of the package where the boot class is located, so that the beans we define will be automatically scanned.

DI detailed explanation

Dependency annotation DI details

Dependency injection means that the IOC container provides the resources that the application depends on during runtime, and resources refer to objects.

In the starter program case, we used the @Autowired annotation to complete the dependency injection operation, and this Autowired is translated as: automatic assembly.

@Autowired annotation, the default is to automatically assemble according to the type (go to the IOC container to find an object of a certain type, and then complete the injection operation)

So what happens if there are multiple bean objects of the same type in the IOC container?

An error will be reported when running the program. How to solve the above problem? Spring provides the following solutions:

@Primary
@Qualifier
@Resource

1. Use the @Primary annotation: When there are multiple Bean injections of the same type, add the @Primary annotation to determine the default implementation.

2. Use the @Qualifier annotation: specify the bean object currently to be injected. In the value attribute of @Qualifier, specify the name of the injected bean.
The @Qualifier annotation cannot be used alone and must be used in conjunction with @Autowired

3. Use the @Resource annotation: inject according to the name of the bean. Specify the name of the bean to be injected through the name attribute.

Interview question: The difference between @Autowird and @Resource
@Autowired is an annotation provided by the spring framework, and @Resource is an annotation provided by JDK
@Autowired defaults to injecting by type, while @Resource injects by name