SpringBoot custom parameter binding and source code parsing

1. Example demonstration

Pet attributes are represented by cascading attributes in the Person class

<! DOCTYPEhtml>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Custom parameter tests</title>
    </head>
    <body>
        <form action="/saveuser" method="post">
            <! The value of name must be the same as the name in the entity class.Name:<input name="userName" value="zhangsan"/> <br/>Age:<input name="age" value="18"/> <br/>Birthday:<input name="birth" value="2019/12/10"/> <br/>Pet name:<input name="pet.name" value="The cat"/><br/>Pet age:<input name="pet.age" value="5"/>
            <input type="submit" value="Save"/>
        </form>
    </body>
</html>
Copy the code

The custom entity class Person corresponds to the parameter name of the form:

@Data
public class Person {
    private String userName;
    private Integer age;
    private Date birth;
    private Pet pet;
}

Copy the code

Pet class:

@Data
public class Pet {
    private String name;
    private String age;
}
Copy the code

Test: Return the Person object to see if the binding was successful

@RestController
public class TestController {
    /** * Data binding: the request data (GET, POST) submitted by the page can be bound to the object properties *@param person
    *@return* /
    @PostMapping("/saveuser")
    public Person saveuser(Person person) {
        returnperson; }}Copy the code

Result: Visit the home page, click Save, and the result appears successfully. All the attributes are wrapped into the Person POJO

2. Binding principle of user-defined parameters

Let’s start with a brief description of the binding principle of custom parameters:

In SpringMVC’s request parameter parsing, we decide in the following step which parameter parser to use to parse the various parameter types in our request (value attribute values)

After the breakpoint analysis, finally will find our custom parameter types are processed by ServletModelAttributeMethodProcessor, ServletModelAttributeMethodProcessor the parameters parser supports custom binding, It uses the WebDataBinder data binder underneath, and the data binder has a conversionService that registers a number of type converters to help with type converters

Finally, after the convert method is done, the final value is set to the entity class. The convert method is simple

3. Customize Converter

3.1, tests,

@Data
public class Person {
    private String userName;
    private Integer age;
    private Date birth;
    private Pet pet;
}
@Data
public class Pet {
    private String name;
    private String age;
}
Copy the code

Index.html: Here, there is an attribute pet, value separated by comma, preceded by name, followed by age

<form action="save" method="post">Name:<input name="userName" value="admin"/> <br/>Age:<input name="age" value="12"/> <br/>Birthday:<input name="birth" value="2021/01/01"/> <br/>Pet name:<input name="pet" value="cat,18"/><br/>
    <input type="submit" value="Save">
</form>
Copy the code

Controller:

@PostMapping("save")
@ResponseBody
public Person save(Person person){
    return person;
}
Copy the code

Springmvc does not know that the parameters passed in to your form are separated by commas, with name in front and age in the back, so the conversion is abnormal

3.2. Customize Converter

In springboot2, we need to implement the WebMvcConfigurer interface to do some custom work for springmvc components. If you look carefully, you can find the method to register the custom converter:

/**
	 * Add {@link Converter Converters} and {@link Formatter Formatters} in addition to the ones
	 * registered by default.
	 */
default void addFormatters(FormatterRegistry registry) {}Copy the code

Next, we can customize the Converter

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new Converter<String, Pet>(){
            // Cat, 3
            @Override
            public Pet convert(String source) {
                if(StringUtils.isEmpty(source)) return null;
                String[] split = source.split(",");
                Pet pet = new Pet();
                pet.setName(split[0]);
                pet.setAge(split[1]);
                returnpet; }}); }}Copy the code

Testing:

AddFormatters method source code