天天看點

@NotEmpty\@NotBlank等注解用在字元串屬性上提示[The annotation XXX is disallowed for this data type]的問題解決方案.問題再現解決

@NotNull、@NotBlack等校驗注解的詭異問題總結

  • 問題再現
    • 背景
    • 複現
    • 錯誤提示
  • 解決
    • 查找問題過程
    • 解決方案
    • PS

問題再現

背景

公司新成立了一個項目,但在已有項目的支援下引入了一批原始的依賴,就這樣,坑來了…

複現

  1. 通過Maven工具引入java注解相關依賴.

    僅展示與@NotNull和@NotBlank相關的依賴,友善後續作說明.

<!--注解校驗類-->
<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <!--<version>2.0.1.Final</version>-->
</dependency>

<!--以下注解校驗實作類-->
 <dependency>
     <groupId>org.hibernate</groupId>
     <artifactId>hibernate-validator</artifactId>
     <version>5.2.1.Final</version>
 </dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator-annotation-processor</artifactId>
    <version>5.2.1.Final</version>
</dependency>
<!--以上注解校驗實作類-->
           
  1. 代碼實作(涉及公司機密,是以僅展示Demo)

2.1 請求Request

import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;


//校驗實體類
@Data
public class SSS {

    @NotEmpty(message = "名稱不能為空!")
    private String name;

    @NotNull(message = "年齡不能為空!")
    @Min(message = "年齡不能小于123歲!", value = 123)
    private Integer age;

}
           

2.2 請求Controller

@RestController
@RequestMapping("/test")
public class InfoController {

  @PostMapping("/demo")
  public String ss( @Valid @RequestBody SSS request) {
   
      System.out.println(request.toString());
      return "測試成功";
  }
}
           

錯誤提示

  1. java: The annotation @NotEmpty is disallowed for this data type.
    @NotEmpty\@NotBlank等注解用在字元串屬性上提示[The annotation XXX is disallowed for this data type]的問題解決方案.問題再現解決

解決

查找問題過程

通過錯誤提示[is disallowed for this data type.]對代碼的全局搜尋後,發現【hibernate-validator】依賴的【ValidationProcessorMessages.properties】中存在【NOT_SUPPORTED_TYPE=The annotation @{0} is disallowed for this data type.】,再通過引用方式點選進入【TypeCheck.java】類中才發現該依賴會對屬性在編譯時期就進行類型比對的校驗,且其并未完全參照@NotEmpty等注解的支援形式進行類型判斷,是以會提示該錯誤;

例:@NotEmpty與本注解會支援下列屬性

CharSequence (length of character sequence is evaluated)
Collection (collection size is evaluated)
Map (map size is evaluated)
Array (array length is evaluated)
           

上述描述如圖 1.1、1.2所示.

@NotEmpty\@NotBlank等注解用在字元串屬性上提示[The annotation XXX is disallowed for this data type]的問題解決方案.問題再現解決

圖1.1 ValidationProcessorMessages.properties部分消息展示

@NotEmpty\@NotBlank等注解用在字元串屬性上提示[The annotation XXX is disallowed for this data type]的問題解決方案.問題再現解決

圖1.2 TypeCheck.java校驗邏輯

解決方案

既然原有的【hibernate-validator】并不滿足@NotEmpty和@NotBlank等方式編譯期校驗,即可以再尋找替換的方案;而Spring提供的【spring-boot-starter-validation】依賴經測試可以滿足,原因很簡單,就是它實作了上述注解的适用範圍.直接使用【spring-boot-starter-validation】替換maven依賴的【hibernate-validator】和【hibernate-validator-annotation-processor】即可。

替換依賴如下

<dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-validation</artifactId>
              <version>2.3.4.RELEASE</version>
         </dependency>
           

這樣即可解決題目中出現的錯誤了

PS

1.其實springboot的校驗實作基類都是以【HibernateValidatorFactory.java】進行校驗邏輯對象的調用,無非是筆者最先引入的依賴多了一個TypeCheck的校驗罷了。

2.附上筆者親測的@NotEmpty這一系列的注解最終的處理方式。

1.通過BindingResult進行校驗結果處理;(其實可以使用切面進行處理,至于實作就靠各位了.)

@PostMapping("/test")
    public String ss( @RequestAttribute SSS request, BindingResult rs ) {
    	//此處處理校驗結果.
        if(rs.hasErrors()){
        	//每次僅取一次校驗結果傳回.
            return  rs.getFieldError().getDefaultMessage();
        }

        System.out.println(request.toString());
        return "dafds";
    }
           

2.通過@Valid注解抛出校驗異常,再用@RestControllerAdvice結合@ExceptionHandler進行統一異常處理,Demo如下.

@PostMapping("/test")
    public String ss(@Valid @RequestAttribute SSS request) {
    	//此處處理校驗結果.
        if(rs.hasErrors()){
        	//每次僅取一次校驗結果傳回.
            return  rs.getFieldError().getDefaultMessage();
        }

        System.out.println(request.toString());
        return "dafds";
    }
	
	//統一異常處理類
@RestControllerAdvice
public class BaseExceptionHandler {

    /**
     * 業務手動異常
     */
    @ExceptionHandler(value = Exception.class)
    public String exceptionRest(Exception e) {
        return  e.getMessage();
    }

           

先就這樣吧。