手把手寫一個基於Spring Boot框架下的參數校驗組件(JSR-303)

前言  

        以前參與的新開放平臺研發的過程當中,因爲不一樣的接口須要對不一樣的入參進行校驗,這就涉及到通用參數的校驗封裝,若是不進行封裝,那麼寫出來的校驗代碼將會風格不統1、校驗工具類不一致、維護風險高等其它因素,因而我對其公共的校驗作了一個封裝,達到了經過註解的方式便可實現參數統一校驗。java

遇到的問題

                      在封裝的時候就發現了一個問題,咱們是開放平臺,返回的報文都必須是統一風格,也就是相似於{code:999,msg:"參數校驗失敗",data:null} 這種,可是原生的JSR303並不支持自定義的字段,因此須要自定義校驗註解。針對這個問題我參考一些JSR303的資料,對其進行了一個定製擴展,以達到開發人員不須要關注捕捉和封裝返回信息。ide

  

傳統的校驗作法 

        以下校驗若是一個實體裏面上百個字段須要校驗的話,對於維護起來是一個很麻煩的事情,並且不少校驗能夠經過jsr-303的註解方式統一處理,無需寫一大堆if和else工具

 if(name == null) {
     //返回錯誤信息
 }else if(age == null) { //返回錯誤信息 }

 基於jsr-303定製後的校驗

  1. 定義一個自定義非空註解
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = { CorpNotEmptyValidator.class }) public @interface CorpNotEmpty { //自定義字段 String field() default "";    //返回錯誤碼 String code() default "0"; //錯誤消息 String message() default "{javax.validation.constraints.NotNull.message}"; Class<?>[] groups() default { }; Class<? extends Payload>[] payload() default { }; @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER }) @Retention(RUNTIME) @Documented @interface List { CorpNotEmpty[] value(); } }

   2.定義非空註解對應的校驗器, initialize和isValid做用描述以下:測試

  •   initialize方法主要是初始化ReturnCodeModel,用於當校驗參數不經過後返回,ReturnCodeModel裏面主要是封裝了返回體,如 code,message等
  •        isValid主要是自定義校驗器的校驗規則,以下判斷是否爲空使用 StringUtils.isEmpty方法,若是校驗不經過則set flag爲false,而後調用基類的isValid方法,該基類方法會判斷flag是否爲false,若是是false說明不經過
public class CorpNotEmptyValidator extends BaseCorpValidator<CorpNotEmpty,String> {
    @Override
    public void initialize(CorpNotEmpty constraintAnnotation) { model = new ReturnCodeModel(); model.setCode(constraintAnnotation.code()); model.setErrorMsg(constraintAnnotation.message()); model.setField(constraintAnnotation.field()); } @Override public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) { System.out.println("1"); if(StringUtils.isEmpty(s)){ model.setFlag(false); }else{ model.setFlag(true); } return super.isValid(s,constraintValidatorContext); } }

  3.定義一個基類,實現 ConstraintValidator,主要是由於須要把isValid這個方法定義成抽象方法提供給不一樣的校驗器使用,避免其它校驗器寫重複的代碼ui

public abstract class BaseCorpValidator<A extends Annotation,B> implements ConstraintValidator<A ,B> {
    protected ReturnCodeModel model = null; @Override public boolean isValid(B value, ConstraintValidatorContext context) { if(!model.getFlag()){ context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate(JSON.toJSONString(model)).addConstraintViolation(); return false; } return true; } }

   4.測試類this

public class TestV1 {
    static ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory(); static Validator validator = validatorFactory.getValidator(); public static void main(String[] args) { UserModel userModel = new UserModel(); userModel.setName("aa"); userModel.setDate("2011"); Set<ConstraintViolation<UserModel>> constraintViolations = validator.validate(userModel); //判斷constraintViolations是否爲空,不爲空說明校驗不經過,拿到ReturnCodeModel裏面的錯誤信息後返回給客戶端 if(!constraintViolations.isEmpty()){ for (ConstraintViolation<?> item : constraintViolations) { ReturnCodeModel codeModel = JSON.parseObject(item.getMessage(), ReturnCodeModel.class); System.out.println(JSON.toJSONString(codeModel)); } } } }

 

畫外音:場景考慮

  1.好比name這個字段,要知足既不能爲空又只能爲數字這2個狀況,若是把2個校驗方法都寫在同一個校驗器,則其餘開發使用的時候也會影響到,因此須要有2個註解的方式,一個是校驗爲空,一個是校驗是否位數字,分析完後那麼就存在一個前後順序問題(由於本身在本地測試出現有可能會先執行校驗是否位數字的校驗器,這時候就會出現空指針異常), 因此針對這個場景須要自定義一個順序註解。spa

  以下代碼,在須要校驗的model實體上加入@GroupSequence註解,這樣校驗器底層會幫咱們按照順序依次處理指針

//順序註解
@GroupSequence({
    First.class,
    Two.class,
    Three.class,
    UserModel.class
})
public class UserModel {
	@CorpMustNumber(code="-2",message="必須數字",groups=Two.class)//在執行數字校驗
	@CorpNotEmpty(code="-1",message="姓名必填",groups=First.class)//先執行非空
	private String name;
	@CorpNotEmpty(code="-1",message="日期必填")
	private String date;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getDate() {
		return date;
	}
	public void setDate(String date) {
		this.date = date;
	}
	
	
}

  

First
Two

總結

    以上就是本篇博客涉及到技術點的全部代碼,經過定製本身的校驗器以知足公司業務場景,對於開發來講統一了規範,統一風格,對之後維護仍是擴展都很是方便。 若是博文對你有幫助麻煩點個關注或者贊,謝謝!code

相關文章
相關標籤/搜索