SpringBoot’s request response and its layered decoupling

SpringBoot’s request response and its layered decoupling

  • 1. Request
    • 1.1 Simple parameters
    • 1.2 Entity parameters
    • 1.3 Array parameters
    • 1.4 Collection parameters
    • 1.5 Date parameters
    • 1.6 JSON parameters
    • 1.7 Path parameters
  • 2. Response
  • 3. Layered decoupling

1. Request

1.1 Simple parameters

Simple parameters mean that the requested parameters are passed to the server in the form of ordinary parameter definitions, similar to String name, int age.... Please note that the names and order of the actual parameters passed must be consistent with those in the method. The formal parameters are exactly the same, otherwise an error will be reported

@RestController
public class HelloController {<!-- -->
    // simple parameters
    @RequestMapping("/SimpleParameter")
    public String SimpleParameter(String name,int age){<!-- -->
        System.out.println(name + "==>" + age);
        return "ok";
    }
}

1.2 Entity parameters

  • simple entity class

Create an entity class in the project to store the passed parameters. The names and order of the actual parameters must be the same as the attributes of the entity class, otherwise the data obtained will be incomplete.

@RestController
public class HelloController {<!-- -->
    @RequestMapping("/userInfo")
    public String userInfo(User user){<!-- -->
        System.out.println(user);
        return "ok";
    }
}
  • Complex entity class

If an entity class object is nested within another entity class object, you must pay attention to the writing method when writing the actual parameters.

1.3 Array parameters

When the actual parameter receives multiple parameters with the same name but different values, the formal parameters in the method can be received in the form of an array. Note that the array name must be the same as the actual parameter name.

@RestController
public class HelloController {<!-- -->
    @RequestMapping("/hobbyArray")
    public String hobbyArray(String hobby[]){<!-- -->
        System.out.println(Arrays.toString(hobby));
        return "ok";
    }
}

1.4 Collection parameters

When receiving multiple parameters with the same name but different values, they can be passed in the form of a collection parameter. Note that the collection name of the formal parameter must be the same as the name of the actual parameter, and the formal parameter of the method must use @RequestParam Annotation modification, the passing form of actual parameters is the same as that of arrays

@RestController
public class HelloController {<!-- -->
    // Collection parameters. Multiple parameters received have the same name but different values. Note that the collection name of the formal parameter must be the same as the actual parameter name.
    // This formal parameter must be modified with @RequestParam annotation
    @RequestMapping("/list")
    public String list(@RequestParam ArrayList<String> hobby){<!-- -->
        System.out.println(hobby);
        return "ok";
    }
}

1.5 Date parameters

When encapsulating date format parameters, be sure to call the annotation @DateTimeFormat(pattern = "date format") at the front of the formal parameter list. If the incoming date data does not match the specified date format, The same will result in an error

@RestController
public class HelloController {<!-- -->
    // When encapsulating the date format, be sure to call the @DateTimeFormat(pattern = "date format") annotation at the front of the formal parameter list
    @RequestMapping("/date")
    public String date(@DateTimeFormat(pattern = "yyyy-MM-dd")LocalDate localDate ){<!-- -->
        System.out.println(localDate);
        return "ok";
    }
}

1.6 JSON parameters

To transfer data in JSON format, you must use post to send the request and use an entity class to receive it. The attribute name of the actual parameter must be the same as the attribute name in the entity class. Note that you must add in front of the method parameter. @RequestBody annotation reflects the value of the actual parameter into the entity class object

@RestController
public class HelloController {<!-- -->
    // To transfer data in json format, you must use post to send the request. The attribute name of the actual parameter must be the same as the attribute name in the entity class.
    // And be sure to add the @RequestBody annotation in front of the method parameters to reflect the values of the actual parameters into the entity class object.
    @RequestMapping("/json")
    public String json(@RequestBody Student student){<!-- -->
        System.out.println(student);
        return "ok";
    }
}

1.7 Path parameters

The path parameter is to put the data to be transmitted directly in the path, without defining the name of the parameter. When the backend receives, it must define {xxx} in the interface path to receive the parameters, and when defining the method parameters, how many parameters are there? How many @PathVariable annotations should be used to obtain parameters for the path?

@RestController
public class HelloController {<!-- -->
    // path parameters
    @RequestMapping("/path/{id}/{name}")
    public String path(@PathVariable Integer id,@PathVariable String name){<!-- -->
        System.out.println(id + "==>" + name);
        return "ok";
    }
}

2. Response

When we respond to data from the back-end to the front-end, the data types of the response are often different, which makes it much more difficult for the front-end to parse the data, and requires a lot of repetitive work. Then if we respond to the front-end in different data formats If it is the same, data analysis can be completed using only one method, which will greatly improve our development efficiency, so we need to uniformly encapsulate the response data

We now create a class to encapsulate data. Generally, the encapsulation format we respond to the front end includes status code code, response message msg and response data data code>

public class Result {<!-- -->
    private Integer code ;//1 success, 0 failure
    private String msg; //Prompt message
    private Object data; //data data

    public Result() {<!-- -->
    }
    public Result(Integer code, String msg, Object data) {<!-- -->
        this.code = code;
        this.msg = msg;
        this.data = data;
    }
    public Integer getCode() {<!-- -->
        return code;
    }
    public void setCode(Integer code) {<!-- -->
        this.code = code;
    }
    public String getMsg() {<!-- -->
        return msg;
    }
    public void setMsg(String msg) {<!-- -->
        this.msg = msg;
    }
    public Object getData() {<!-- -->
        return data;
    }
    public void setData(Object data) {<!-- -->
        this.data = data;
    }

    public static Result success(Object data){<!-- -->
        return new Result(1, "success", data);
    }
    public static Result success(){<!-- -->
        return new Result(1, "success", null);
    }
    public static Result error(String msg){<!-- -->
        return new Result(0, msg, null);
    }
}

3. Layered decoupling

We follow the design principle of 'high cohesion, low coupling' during development. Therefore, in a software system, we should try to ensure the independence of modules. The modules implement single functions and responsibilities, and the simpler the better. This is conducive to system reuse and greatly reduces the dependence between modules. The system has high stability and is easier to maintain. To learn layered decoupling, we must first learn the three-tier architecture of the SpringBoot framework. The so-called three-tier architecture is to divide a business into three parts.

  • Controller layer: responsible for receiving and responding to data
  • Service layer: Responsible for business logic processing and returning processed data to the Controller layer
  • Dao layer: Responsible for returning the data to be processed to the Service layer for processing

A small introductory case (this case is only designed to introduce the three-tier architecture): the front-end page modifies a product, passes in the product number and reduced price, and returns all the products on this page after the back-end process is completed. Information in order to update the entire page, then we analyze the entire business as follows:

  1. The Controller layer receives the data and passes it to the Service layer.
  2. After receiving the parameters, the Service layer notifies the Dao layer to retrieve the data.
  3. The Dao layer retrieves the data and returns it to the Service layer (in actual development, we use a database to store data. In this case, a collection is used to store the corresponding data)
  4. The Service layer receives the data, processes the data according to logic, and returns the data to the Controller layer.
  5. The Controller layer receives the data returned by the Service layer and encapsulates the data before responding to the front-end personnel.

For convenience, only the core code of the three-tier architecture is shown here.

  • Controller layer
@RestController
public class UserController {<!-- -->
    private UserService service = new UserServiceImpl();
    @RequestMapping("/user")
    public Result user(String id, Double price){<!-- -->
        ArrayList<Foods> list = service.changePrice(id,price);
        return Result.ok("Modification successful~",list);
    }
}
  • Service layer
public interface UserDao {<!-- -->
    ArrayList<Foods> changePrice();
}

public class UserServiceImpl implements UserService {<!-- -->
    // Create an object of the Dao layer to notify the Dao layer to retrieve data
    private UserDao dao = new UserDaoImpl();
    @Override
    public ArrayList<Foods> changePrice(String id, Double price) {<!-- -->
        //Notify the Dao layer to retrieve the data
        ArrayList<Foods> list = dao.changePrice();
        for (Foods food : list) {<!-- -->
            if(food.getId().equals(id)){<!-- -->
                food.setPrice(price);
                break;
            }
        }
        return list;
    }
}
  • Dao floor
public interface UserService {<!-- -->
    ArrayList<Foods> changePrice(String id, Double price);
}

public class UserDaoImpl implements UserDao {<!-- -->
    @Override
    public ArrayList<Foods> changePrice() {<!-- -->
        // Get data
        return Foods.list;
    }
}
  • data source
public class Foods {<!-- -->
    private String name;
    private String id;
    private Double price;

    public static ArrayList<Foods> list = new ArrayList<>();

    static {<!-- -->
        list.add(new Foods("Xiaomi 10","001",3999.0));
        list.add(new Foods("Apple 13","002",6888.0));
        list.add(new Foods("Huawei Mate50","003",4500.0));
    }
    //Construction method and set and get methods...
}

Although the writing level is clear in this way, there is a big drawback, that is, the coupling is too large. Once an exception occurs in the program it depends on, the entire program will collapse, and you will fall into endless steps of finding bugs~~

The SpringBoot framework provides a method, which is to hand over the permission to create Service layer and Dao layer objects to Spring management, referred to as IOC, also called inversion of control; and then automatically create its objects through DI dependency injection for use by the corresponding classes.

  1. We only need to add the @Service annotation to the Service layer implementation class and the @Repository annotation to the Dao layer implementation class to hand the class over to Spring for management.
  2. Add the @Autowired annotation when defining the implementation class objects of the Service layer and Dao layer, so that Spring can automatically create the corresponding objects when using them, and give Spring the authority to create objects to complete decoupling.
  • Controller layer
@RestController
public class UserController {<!-- -->
    @Autowired
    private UserService service = new UserServiceImpl();

    @RequestMapping("/user")
    public Result user(String id, Double price){<!-- -->
        ArrayList<Foods> list = service.changePrice(id,price);
        return Result.ok("Modification successful~",list);
    }
}
  • Service layer
public interface UserDao {<!-- -->
    ArrayList<Foods> changePrice();
}

@Service
public class UserServiceImpl implements UserService {<!-- -->

    @Autowired
    // Create an object of the Dao layer to notify the Dao layer to retrieve data
    private UserDao dao;

    @Override
    public ArrayList<Foods> changePrice(String id, Double price) {<!-- -->
        //Notify the Dao layer to retrieve the data
        ArrayList<Foods> list = dao.changePrice();
        for (Foods food : list) {<!-- -->
            if(food.getId().equals(id)){<!-- -->
                food.setPrice(price);
                break;
            }
        }
        return list;
    }
}
  • Dao floor
public interface UserService {<!-- -->
    ArrayList<Foods> changePrice(String id, Double price);
}

@Repository
public class UserDaoImpl implements UserDao {<!-- -->
    @Override
    public ArrayList<Foods> changePrice() {<!-- -->
        //Remove data
        return Foods.list;
    }
}