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:
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