(四)數(shù)據(jù)綁定流程

一 概要流程

流程

  1. Spring MVC 主框架將 ServletRequest 對(duì)象及目標(biāo)方法的入?yún)?shí)例傳遞給WebDataBinderFactory實(shí)例,以創(chuàng)建DataBinder實(shí)例對(duì)象
  2. DataBinder 調(diào)用裝配在 Spring MVC 上下文中的 ConversionService 組件進(jìn)行數(shù)據(jù)類型轉(zhuǎn)換、數(shù)據(jù)格式化工作。將 Servlet 中的請(qǐng)求信息填充到入?yún)?duì)象中
  3. 調(diào)用 Validator組件對(duì)已經(jīng)綁定了請(qǐng)求消息的入?yún)?duì)象進(jìn)行數(shù)據(jù)合法性校驗(yàn),并最終生成數(shù)據(jù)綁定結(jié)果BindingData 對(duì)象
  4. Spring MVC 抽取 BindingResult中的入?yún)?duì)象校驗(yàn)錯(cuò)誤對(duì)象,將它們賦給處理方法的響應(yīng)入?yún)?/li>

Spring MVC 通過(guò)反射機(jī)制對(duì)目標(biāo)處理方法進(jìn)行解析,將請(qǐng)求消息綁定到處理方法的入?yún)⒅?。?shù)據(jù)綁定的核心部件是 DataBinder,運(yùn)行機(jī)制如下:

image.png

原理

  1. DataBinder負(fù)責(zé)將請(qǐng)求的參數(shù)綁定到目標(biāo)方法的參數(shù)中去(有些是pojo)
  2. DataBinder中有幾個(gè)組件負(fù)責(zé)完成這幾項(xiàng)工作;
  3. ConversionService負(fù)責(zé)進(jìn)行類型轉(zhuǎn)換以及格式化工作
  4. 如果有數(shù)據(jù)校驗(yàn)使用校驗(yàn)器進(jìn)行 validators,是一個(gè)集合??梢杂卸鄠€(gè)校驗(yàn)器
  5. 處理校驗(yàn)成功還是失敗信息?BindingResult負(fù)責(zé)處理校驗(yàn)錯(cuò)誤的信息

二 擴(kuò)展

自定義的類型轉(zhuǎn)換器

對(duì)一些時(shí)間類型,或者特殊的POJO,我們可以定義自己的類型轉(zhuǎn)換器,然后放到ConversionService 中。
Spring 定義了 3 種類型的轉(zhuǎn)換器接口,實(shí)現(xiàn)任意一個(gè)轉(zhuǎn)換器接口都可以作為自定義轉(zhuǎn)換器注冊(cè)到 ConversionServiceFactoryBean 中:

  • Converter<S,T>:將 S 類型對(duì)象轉(zhuǎn)為 T 類型對(duì)象
  • ConverterFactory:將相同系列多個(gè) “同質(zhì)” Converter 封裝在一起。如果希望將一種類型的對(duì)象轉(zhuǎn)換為另一種類型及其子類的對(duì)象(例如將 String 轉(zhuǎn)換為 Number 及 Number 子類(Integer、Long、Double 等)對(duì)象)可使用該轉(zhuǎn)換器工廠類
  • GenericConverter:會(huì)根據(jù)源類對(duì)象及目標(biāo)類對(duì)象所在的宿主類中的上下文信息進(jìn)行類型轉(zhuǎn)換

數(shù)據(jù)校驗(yàn)

helloworld

@Controller
@RequestMapping("/valid")
public class ValidController {


    @RequestMapping("/v1")
    @ResponseBody
    public String v1(@Valid Student student, BindingResult result){
        System.out.println("v1");
        if(result.hasErrors()){
            for (ObjectError error : result.getAllErrors()) {
                System.out.println(error.getDefaultMessage());
            }
        }

        return "v1";

    }

}

實(shí)體類

public class Student {

    private int id;

    @NotNull
    @Length(min=2,max=4,message="姓名格式不正確")
    private String name;


    @Length(min=4,max=5,message = "地址長(zhǎng)度錯(cuò)誤")
    private String address;



    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

校驗(yàn)?zāi)J?/h3>
  1. 普通模式(默認(rèn)是這個(gè)模式)
      普通模式(會(huì)校驗(yàn)完所有的屬性,然后返回所有的驗(yàn)證失敗信息)
    2.快速失敗返回模式
      快速失敗返回模式(只要有一個(gè)驗(yàn)證失敗,則返回)

failFast:true 快速失敗返回模式 false 普通模式
兩種配置方式

ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
        .configure()
        .failFast( true )
        .buildValidatorFactory();
Validator validator = validatorFactory.getValidator();


===


ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
        .configure()
        .addProperty( "hibernate.validator.fail_fast", "true" )
        .buildValidatorFactory();
Validator validator = validatorFactory.getValidator();

配置方式

@Configuration
public class ValidatorConfiguration {
    @Bean
    public Validator validator(){
        ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
                .configure()
                .addProperty( "hibernate.validator.fail_fast", "true" )
                .buildValidatorFactory();
        Validator validator = validatorFactory.getValidator();

        return validator;
    }
}

如果有多個(gè)參數(shù)要校驗(yàn),就需要多個(gè)接收多個(gè)BindingResult 對(duì)象

public void test()(@RequestBody @Valid DemoModel demo, BindingResult result)

public void test()(@RequestBody @Valid DemoModel demo, BindingResult result,@RequestBody @Valid DemoModel demo2, BindingResult result2)

校驗(yàn)@RequestParam參數(shù)

使用校驗(yàn)bean的方式,沒(méi)有辦法校驗(yàn)RequestParam的內(nèi)容
使用@Valid注解,對(duì)RequestParam對(duì)應(yīng)的參數(shù)進(jìn)行注解,是無(wú)效的,需要使用@Validated注解來(lái)使得驗(yàn)證生效。如下所示:

  1. 添加MethodValidationPostProcessorbena
@Configuration
public class ValidConbfiguration {

    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
        MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor();
        /**設(shè)置validator模式為快速失敗返回*/
        postProcessor.setValidator(validator());
        return postProcessor;
    }

    @Bean
    public Validator validator(){
        ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
                .configure()
                .addProperty( "hibernate.validator.fail_fast", "true" )
                .buildValidatorFactory();
        Validator validator = validatorFactory.getValidator();

        return validator;
    }


  1. Controller上加注解@Validated
@Controller
@RequestMapping("/valid")
@Validated
public class ValidController {
  1. 可以看到:驗(yàn)證不通過(guò)時(shí),拋出了ConstraintViolationException異常,使用同一捕獲異常處理:
@ControllerAdvice
@Component
public class GlobalExceptionHandler {

    @ExceptionHandler
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public String handle(ValidationException exception) {
        if(exception instanceof ConstraintViolationException){
            ConstraintViolationException exs = (ConstraintViolationException) exception;

            Set<ConstraintViolation<?>> violations = exs.getConstraintViolations();
            for (ConstraintViolation<?> item : violations) {
            /**打印驗(yàn)證不通過(guò)的信息*/
                System.out.println("global: "+item.getMessage());
            }
        }
        return "bad request, " ;
    }
}

model校驗(yàn)

這里我沒(méi)有對(duì)類加@Data

    @Autowired
    private Validator validator;

    @RequestMapping("/demo3")
    @ResponseBody
    public String demo3(String name){
        Student s = new Student();
        s.setName(name);
        Set<ConstraintViolation<Student>> violationSet = validator.validate(s);
        for (ConstraintViolation<Student> model : violationSet) {
            System.out.println(model.getMessage());
        }
        return "demo3";
    }

對(duì)象級(jí)聯(lián)校驗(yàn)

對(duì)級(jí)聯(lián)的屬性加上@Valid

public class Student {

    private int id;

    @NotNull
    @Length(min=2,max=4,message="姓名格式不正確")
    private String name;


    @Length(min=4,max=5,message = "地址長(zhǎng)度錯(cuò)誤")
    private String address;


    @Valid
    private Student friend;

    ...

分組校驗(yàn)

詳見(jiàn)參考引用

參考

  1. https://blog.csdn.net/dp_dp/article/details/82994948
  2. https://www.cnblogs.com/mr-yang-localhost/p/7812038.html#_label5
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容