Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 亚洲国产一区二区三区精品,91成人在线免费观看,色综合第一页

          整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          如何正確使用 Bean Validation 進(jìn)行數(shù)

          如何正確使用 Bean Validation 進(jìn)行數(shù)據(jù)校驗(yàn)

          背景

          在前后端開發(fā)過程中,數(shù)據(jù)校驗(yàn)是一項(xiàng)必須且常見的事,從展示層、業(yè)務(wù)邏輯層到持久層幾乎每層都需要數(shù)據(jù)校驗(yàn)。如果在每一層中手工實(shí)現(xiàn)驗(yàn)證邏輯,既耗時又容易出錯。

          為了避免重復(fù)這些驗(yàn)證,通常的做法是將驗(yàn)證邏輯直接捆綁到領(lǐng)域模型中,通過元數(shù)據(jù)(默認(rèn)是注解)去描述模型, 生成校驗(yàn)代碼,從而使校驗(yàn)從業(yè)務(wù)邏輯中剝離,提升開發(fā)效率,使開發(fā)者更專注業(yè)務(wù)邏輯本身。

          在 Spring 中,目前支持兩種不同的驗(yàn)證方法:Spring Validation 和 JSR-303 Bean Validation,即 @Validated(org . springframework.validation.annotation.Validated)和 @Valid(javax.validation.Valid)。兩者都可以通過定義模型的約束來進(jìn)行數(shù)據(jù)校驗(yàn),雖然兩者使用類似,在很多場景下也可以相互替換,但實(shí)際上卻完全不同,這些差別長久以來對我們?nèi)粘J褂卯a(chǎn)生了較大疑惑,本文主要梳理其中的差別、介紹 Validation 的使用及其實(shí)現(xiàn)原理,幫助大家在實(shí)踐過程中更好使用 Validation 功能。

          Bean Validation簡介

          什么是JSR?

          JSR 是 Java Specification Requests 的縮寫,意思是 Java 規(guī)范提案。是指向 JCP(Java Community Process) 提出新增一個標(biāo)準(zhǔn)化技術(shù)規(guī)范的正式請求,以向 Java 平臺增添新的 API 和服務(wù)。JSR 已成為 Java 界的一個重要標(biāo)準(zhǔn)。

          JSR-303定義的是什么標(biāo)準(zhǔn)?


          JSR-303 是用于 Bean Validation 的 Java API 規(guī)范,該規(guī)范是 Jakarta EE and JavaSE 的一部分,Hibernate Validator 是 Bean Validation 的參考實(shí)現(xiàn)。Hibernate Validator 提供了 JSR 303 規(guī)范中所有內(nèi)置 Constraint 的實(shí)現(xiàn),除此之外還有一些附加的 Constraint。(最新的為 JSR-380 為 Bean Validation 3.0)

          常用的校驗(yàn)注解補(bǔ)充:

          @NotBlank 檢查約束字符串是不是 Null 還有被 Trim 的長度是否大于,只對字符串,且會去掉前后空格。

          @NotEmpty 檢查約束元素是否為 Null 或者是 Empty。

          @Length 被檢查的字符串長度是否在指定的范圍內(nèi)。

          @Email 驗(yàn)證是否是郵件地址,如果為 Null,不進(jìn)行驗(yàn)證,算通過驗(yàn)證。

          @Range 數(shù)值返回校驗(yàn)。

          @IdentityCardNumber 校驗(yàn)身份證信息。

          @UniqueElements 集合唯一性校驗(yàn)。

          @URL 驗(yàn)證是否是一個 URL 地址。

          Spring Validation的產(chǎn)生背景


          上文提到 Spring 支持兩種不同的驗(yàn)證方法:Spring Validation 和 JSR-303 Bean Validation(下文使用@Validated和@Valid替代)。

          • 為什么會同時存在兩種方式?

          Spring 增加 @Validated 是為了支持分組校驗(yàn),即同一個對象在不同的場景下使用不同的校驗(yàn)形式。比如有兩個步驟用于提交用戶資料,后端復(fù)用的是同一個對象,第一步驗(yàn)證姓名,電子郵件等字段,然后在后續(xù)步驟中的其他字段中。這時候分組校驗(yàn)就會發(fā)揮作用。

          • 為什么不合入到 JSR-303 中?

          之所以沒有將它添加到 @Valid 注釋中,是因?yàn)樗鞘褂?Java 社區(qū)過程(JSR-303)標(biāo)準(zhǔn)化的,這需要時間,而 Spring 開發(fā)者想讓人們更快地使用這個功能。

          • @Validated 的內(nèi)置自動化校驗(yàn)

          Spring 增加 @Validated 還有另一層原因,Bean Validation 的標(biāo)準(zhǔn)做法是在程序中手工調(diào)用 Validator 或者 ExecutableValidator 進(jìn)行校驗(yàn),為了實(shí)現(xiàn)自動化,通常通過 AOP、代理等方法攔截技術(shù)來調(diào)用。而 @Validated 注解就是為了配合 Spring 進(jìn)行 AOP 攔截,從而實(shí)現(xiàn) Bean Validation 的自動化執(zhí)行。

          • @Validated 和 @Valid 的區(qū)別

          @Valid 是 JSR 標(biāo)準(zhǔn) API,@Validated 擴(kuò)展了 @Valid 支持分組校驗(yàn)且能作為 SpringBean 的 AOP 注解,在 SpringBean 初始化時實(shí)現(xiàn)方法層面的自動校驗(yàn)。最終還是使用了 JSR API 進(jìn)行約束校驗(yàn)。

          Bean Validation的使用

          引入POM


          // 正常應(yīng)該引入hibernate-validator,是JSR的參考實(shí)現(xiàn)
          
          
          <dependency>
              <groupId>org.hibernate.validator</groupId>
              <artifactId>hibernate-validator</artifactId>
          </dependency>
          // Spring在stark中集成了,所以hibernate-validator可以不用引入
          <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
          </dependency>

          Bean層面校驗(yàn)

          • 變量層面約束
          public class EntryApplicationInfoCmd {
          
              /**
          
               * 用戶ID
          
               */
          
              @NotNull(message="用戶ID不為空")
          
              private Long userId;
          
          
          
          
              /**
          
               *   證件類型
          
               */
          
              @NotEmpty(message="證件類型不為空")
          
              private String certType;
          
          }
          • 屬性層面約束

          主要為了限制 Setter 方法的只讀屬性。屬性的 Getter 方法打注釋,而不是 Setter。

          public class EntryApplicationInfoCmd {
          
              public EntryApplicationInfoCmd(Long userId, String certType) {
          
                      this.userId=userId;
          
                      this.certType=certType;
          
                  }
          
              /**
          
               * 用戶ID
          
               */
          
              private Long userId;
          
          
          
          
              /**
          
               *   證件類型
          
               */
          
              private String certType;
          
              
          
              @NotNull
          
              public String getUserId() {
          
                  return userId;
          
              }
          
              
          
              @NotEmpty
          
              public String getCertType() {
          
                  return userId;
          
              }
          
          }
          • 容器元素約束
          public class EntryApplicationInfoCmd {
          
              ...
          
              List<@NotEmpty Long> categoryList;
          
          }
          • 類層面約束

          @CategoryBrandNotEmptyRecord 是自定義類層面的約束,也可以約束在構(gòu)造函數(shù)上。

          @CategoryBrandNotEmptyRecord
          public class EntryApplicationInfoCmd {
              /**
               * 用戶ID
               */
              @NotNull(message="用戶ID不為空")
              private Long userId;
                 
              List<@NotEmpty Long> categoryList;
          }
          • 嵌套約束

          嵌套對象需要額外使用 @Valid 進(jìn)行標(biāo)注(@Validate 不支持,為什么?請看產(chǎn)生的背景)。

          public class EntryApplicationInfoCmd {
          
              /**
          
               *  主營品牌
          
               */  
          
              @Valid
          
              @NotNull 
          
              private MainBrandImagesCmd mainBrandImage;
          
          }
          
          
          
          
          public class MainBrandImagesCmd {
          
              /**
          
               *  品牌名稱
          
               */  
          
              @NotEmpty 
          
              private String brandName;;
          
          }
          • 手工驗(yàn)證Bean約束
          // 獲取校驗(yàn)器
          
          ValidatorFactory factory=Validation.buildDefaultValidatorFactory();
          
          Validator validator=factory.getValidator();
          
          
          
          
          // 進(jìn)行bean層面校驗(yàn)
          
          Set<ConstraintViolation<User>> violations=validator.validate(EntryApplicationInfoCmd);
          
          // 打印校驗(yàn)信息
          
          for (ConstraintViolation<User> violation : violations) {
          
              log.error(violation.getMessage()); 
          
          }

          方法層面校驗(yàn)

          • 函數(shù)參數(shù)約束
          public class MerchantMainApplyQueryService {
          
              MainApplyDetailResp detail(@NotNull(message="申請單號不能為空") Long id) {
          
                  ...
          
              }
          
          }
          • 函數(shù)返回值約束
          public class MerchantMainApplyQueryService {
          
              @NotNull
          
              @Size(min=1)
          
              public List<@NotNull MainApplyStandDepositResp> getStanderNewDeposit(Long id) {
          
                  //...
          
              }
          
          }
          • 嵌套約束

          嵌套對象需要額外使用 @Valid 進(jìn)行標(biāo)注(@Validate 不支持)。

          public class MerchantMainApplyQueryService {
          
              public NewEntryBrandRuleCheckApiResp brandRuleCheck(@Valid @NotNull NewEntryBrandRuleCheckRequest request) {
          
                  ...
          
              }
          
          }
          
          
          
          
          public class NewEntryBrandRuleCheckRequest {
          
              @NotNull(message="一級類目不能為空")
          
              private Long level1CategoryId;
          
          }
          • 在繼承中方法約束

          Validation 的設(shè)計需要遵循里氏替換原則,無論何時使用類型 T,也可以使用 T 的子類型 S,而不改變程序的行為。即子類不能增加約束也不能減弱約束。

          子類方法參數(shù)的約束與父類行為不一致(錯誤例子):

          // 繼承的方法參數(shù)約束不能改變,否則會導(dǎo)致父類子類行為不一致
          
          public interface Vehicle {
          
          
          
          
              void drive(@Max(75) int speedInMph);
          
          }
          
          
          
          
          public class Car implements Vehicle {
          
          
          
          
              @Override
          
              public void drive(@Max(55) int speedInMph) {
          
                  //...
          
              }
          
          }

          方法的返回值可以增加約束(正確例子):

          // 繼承的方法返回值可以增加約束
          
          public interface Vehicle {
          
          
          
          
              @NotNull
          
              List<Person> getPassengers();
          
          }
          
          
          
          
          public class Car implements Vehicle {
          
          
          
          
              @Override
          
              @Size(min=1)
          
              public List<Person> getPassengers() {
          
                  //...
          
                  return null;
          
              }
          
          }
          • 手工驗(yàn)證方法約束

          方法層面校驗(yàn)使用的是 ExecutableValidator。

          // 獲取校驗(yàn)器
          
          ValidatorFactory factory=Validation.buildDefaultValidatorFactory();
          
          Validator executableValidator=factory.getValidator().forExecutables();
          
          
          
          
          // 進(jìn)行方法層面校驗(yàn)
          
          MerchantMainApplyQueryService service=getService();
          
          Method method=MerchantMainApplyQueryService.class.getMethod( "getStanderNewDeposit", int.class );
          
          Object[] parameterValues={ 80 };
          
          Set<ConstraintViolation<Car>> violations=executableValidator.validateParameters(
          
                  service,
          
                  method,
          
                  parameterValues
          
          );
          
          // 打印校驗(yàn)信息
          
          for (ConstraintViolation<User> violation : violations) {
          
              log.error(violation.getMessage()); 
          
          }

          分組校驗(yàn)

          不同場景復(fù)用一個 Model,采用不一樣的校驗(yàn)方式。

          public class NewEntryMainApplyRequest {
          
              @NotNull(message="一級類目不能為空")
          
              private Long level1CategoryId;
          
              
          
              @NotNull(message="申請單ID不能為空", group=UpdateMerchantMainApplyCmd.class)
          
              private Long applyId;
          
              
          
              @NotEmpty(message="審批人不能為空", group=AddMerchantMainApplyCmd.class)
          
              private String operator;
          
          }
          
          
          
          
          // 校驗(yàn)分組UpdateMerchantMainApplyCmd.class
          
          NewEntryMainApplyRequest request1=new NewEntryMainApplyRequest( 29, null, "aaa");
          
          Set<ConstraintViolation<NewEntryMainApplyRequest>> constraintViolations=validator.validate( request1, UpdateMerchantMainApplyCmd.class );
          
          assertEquals("申請單ID不能為空", constraintViolations.iterator().next().getMessage());
          
          
          
          
          // 校驗(yàn)分組AddMerchantMainApplyCmd.class
          
          NewEntryMainApplyRequest request2=new NewEntryMainApplyRequest( 29, "12345", "");
          
          Set<ConstraintViolation<NewEntryMainApplyRequest>> constraintViolations=validator.validate( request2, AddMerchantMainApplyCmd.class );
          
          assertEquals("審批人不能為空", constraintViolations.iterator().next().getMessage());

          自定義校驗(yàn)

          自定義注解:

          @Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
          @Retention(RetentionPolicy.RUNTIME)
          @Constraint(validatedBy=MyConstraintValidator.class)
          public @interface MyConstraint {
              String message();
          
          
              Class<?>[] groups() default {};
          
          
              Class<? extends Payload>[] payload() default {};
          }
          自定義校驗(yàn)器:
          public class MyConstraintValidator implements ConstraintValidator<MyConstraint, Object> {
          
              @Override
          
              public void initialize(MyConstraint constraintAnnotation) {
          
              
          
              }
          
              
          
              @Override
          
              public isValid isValid(Object value, ConstraintValidatorContext context) {
          
                   String name=(String)value;
          
                   if("xxxx".equals(name)) {
          
                       return true;
          
                   }
          
                   
          
                   return false;
          
              }
          
          }

          使用自定義約束:

          public class Test {
          
              @MyConstraint(message="test")
          
              String name;
          
          }

          Bean Validation自動執(zhí)行以及原理


          上述 2.6 和 3.5 分別實(shí)現(xiàn)了 Bean 和 Method 層面的約束校驗(yàn),但是每次都主動調(diào)用比較繁瑣,因此 Spring 在 @RestController 的 @RequestBody 注解中內(nèi)置了一些自動化校驗(yàn)以及在 Bean 初始化中集成了 AOP 來簡化編碼。

          Validation的常見誤解

          最常見的應(yīng)該就是在 RestController 中,校驗(yàn) @RequestBody 指定參數(shù)的約束,使用 @Validated 或者 @Valid(該場景下兩者等價)進(jìn)行約束校驗(yàn),以至于大部分人理解的 Validation 只要打個注解就可以生效,實(shí)際上這只是一種特例。很多人在使用過程中經(jīng)常遇到約束校驗(yàn)不生效。

          • 約束校驗(yàn)生效

          Spring-mvc 中在 @RequestBody 后面加 @Validated、@Valid 約束即可生效。

          @RestController
          
          @RequestMapping("/biz/merchant/enter")
          
          public class MerchantEnterController {
          
              @PostMapping("/application")
          
              // 使用@Validated
          
              public HttpMessageResult addOrUpdateV1(@RequestBody @Validated MerchantEnterApplicationReq req){
          
                  ...
          
              }
          
              // 使用@Valid
          
              @PostMapping("/application2")
          
              public HttpMessageResult addOrUpdate2(@RequestBody @Valid MerchantEnterApplicationReq req){
          
                  ...
          
              }
          
          }
          • 約束校驗(yàn)不生效

          然而下面這個約束其實(shí)是不生效的,想要生效得在 MerchantEntryServiceImpl 類目加上 @Validated 注解。

          // @Validated 不加不生效
          @Service
          public class MerchantEntryService {
              public Boolean applicationAddOrUpdate(@Validated MerchantEnterApplicationReq req) {
                  ...
              }
              
              public Boolean applicationAddOrUpdate2(@Valid MerchantEnterApplicationReq req) {
                  ...
              }
          }

          那么究竟為什么會出現(xiàn)這種情況呢,這就需要對 Spring Validation 的注解執(zhí)行原理有一定的了解。

          Controller自動執(zhí)行約束校驗(yàn)原理

          在 Spring-mvc 中,有很多攔截器對 Http 請求的出入?yún)⑦M(jìn)行解析和轉(zhuǎn)換,Validation 解析和執(zhí)行也是類似,其中 RequestResponseBodyMethodProcessor 是用于解析 @RequestBody 標(biāo)注的參數(shù)以及處理 @ResponseBody 標(biāo)注方法的返回值的。

          public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
          
          
          
          
              @Override
          
              public boolean supportsParameter(MethodParameter parameter) {
          
                  return parameter.hasParameterAnnotation(RequestBody.class);
          
              }
          
              // 類上或者方法上標(biāo)注了@ResponseBody注解都行
          
              @Override
          
              public boolean supportsReturnType(MethodParameter returnType) {
          
                  return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) || returnType.hasMethodAnnotation(ResponseBody.class));
          
              }
          
              
          
              // 這是處理入?yún)⒎庋b校驗(yàn)的入口
          
              @Override
          
              public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
          
                      NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
          
                  parameter=parameter.nestedIfOptional();
          
                  // 獲取請求的參數(shù)對象
          
                  Object arg=readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
          
                  // 獲取參數(shù)名稱
          
                  String name=Conventions.getVariableNameForParameter(parameter);
          
          
          
          
                  // 只有存在binderFactory才會去完成自動的綁定、校驗(yàn)~
          
                  if (binderFactory !=null) {
          
                      WebDataBinder binder=binderFactory.createBinder(webRequest, arg, name);
          
                      if (arg !=null) {
          
                          // 這里完成數(shù)據(jù)綁定+數(shù)據(jù)校驗(yàn)~~~~~(綁定的錯誤和校驗(yàn)的錯誤都會放進(jìn)Errors里)
          
                          validateIfApplicable(binder, parameter);
          
          
          
          
                          // 若有錯誤消息hasErrors(),并且僅跟著的一個參數(shù)不是Errors類型,Spring MVC會主動給你拋出MethodArgumentNotValidException異常
          
                          if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
          
                              throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
          
                          }
          
                      }
          
                  
          
                      // 把錯誤消息放進(jìn)去 證明已經(jīng)校驗(yàn)出錯誤了~~~
          
                      // 后續(xù)邏輯會判斷MODEL_KEY_PREFIX這個key的~~~~
          
                      if (mavContainer !=null) {
          
                          mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
          
                      }
          
                  }
          
          
          
          
                  return adaptArgumentIfNecessary(arg, parameter);
          
              }
          
              
          
              ...
          
          }

          約束的校驗(yàn)邏輯是在 RequestResponseBodyMethodProcessor.validateIfApplicable 實(shí)現(xiàn)的,這里同時兼容了 @Validated 和 @Valid,所以該場景下兩者是等價的。

          // 校驗(yàn),如果合適的話。使用WebDataBinder,失敗信息最終也都是放在它身上~  
          
          // 入?yún)ⅲ篗ethodParameter parameter
          
          protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
          
              // 拿到標(biāo)注在此參數(shù)上的所有注解們(比如此處有@Valid和@RequestBody兩個注解)
          
              Annotation[] annotations=parameter.getParameterAnnotations();
          
              for (Annotation ann : annotations) {
          
                  // 先看看有木有@Validated
          
                  Validated validatedAnn=AnnotationUtils.getAnnotation(ann, Validated.class);
          
          
          
          
                  // 這個里的判斷是關(guān)鍵:可以看到標(biāo)注了@Validated注解 或者注解名是以Valid打頭的 都會有效哦
          
                  //注意:這里可沒說必須是@Valid注解。實(shí)際上你自定義注解,名稱只要一Valid開頭都成~~~~~
          
                  if (validatedAnn !=null || ann.annotationType().getSimpleName().startsWith("Valid")) {
          
                      // 拿到分組group后,調(diào)用binder的validate()進(jìn)行校驗(yàn)~~~~
          
                      // 可以看到:拿到一個合適的注解后,立馬就break了~~~
          
                      // 所以若你兩個主機(jī)都標(biāo)注@Validated和@Valid,效果是一樣滴~
          
                      Object hints=(validatedAnn !=null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
          
                      Object[] validationHints=(hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
          
                      binder.validate(validationHints);
          
                      break;
          
                  }
          
              }

          binder.validate() 的實(shí)現(xiàn)中使用的 org.springframework.validation.Validator 的接口,該接口的實(shí)現(xiàn)為 SpringValidatorAdapter。

          public void validate(Object... validationHints) {
          
              Object target=getTarget();
          
              Assert.state(target !=null, "No target to validate");
          
              BindingResult bindingResult=getBindingResult();
          
          
          
          
              for (Validator validator : getValidators()) {
          
                 // 使用的org.springframework.validation.Validator,調(diào)用SpringValidatorAdapter.validate
          
                 if (!ObjectUtils.isEmpty(validationHints) && validator instanceof SmartValidator) {
          
                    ((SmartValidator) validator).validate(target, bindingResult, validationHints);
          
                 }
          
                 else if (validator !=null) {
          
                    validator.validate(target, bindingResult);
          
                 }
          
              }
          
          }

          在 ValidatorAdapter.validate 實(shí)現(xiàn)中,最終調(diào)用了 javax.validation.Validator.validate,也就是說最終是調(diào)用 JSR 實(shí)現(xiàn),@Validate 只是外層的包裝,在這個包裝中擴(kuò)展的分組功能。

          public class SpringValidatorAdapter {
          
              ...
          
              private javax.validation.Validator targetValidator;
          
              
          
              @Override
          
              public void validate(Object target, Errors errors) {
          
                  if (this.targetValidator !=null) {
          
                     processConstraintViolations(
          
                         // 最終是調(diào)用JSR實(shí)現(xiàn)
          
                         this.targetValidator.validate(target), errors));
          
                  }
          
              }
          
           }

          targetValidator.validate 就是 javax.validation.Validator.validate 上述 2.6 Bean 層面手工驗(yàn)證一致。

          Service自動執(zhí)行約束校驗(yàn)原理

          非Controller的@RequestBody注解,自動執(zhí)行約束校驗(yàn),是通過 MethodValidationPostProcessor 實(shí)現(xiàn)的,該類繼承。

          BeanPostProcessor, 在 Spring Bean 初始化過程中讀取 @Validated 注解創(chuàng)建 AOP 代理(實(shí)現(xiàn)方式與 @Async 基本一致)。該類開頭文檔注解(JSR 生效必須類層面上打上 @Spring Validated 注解)。

          /**
          
          * <p>Target classes with such annotated methods need to be annotated with Spring's
          
          * {@link Validated} annotation at the type level, for their methods to be searched for
          
          * inline constraint annotations. Validation groups can be specified through {@code @Validated}
          
          * as well. By default, JSR-303 will validate against its default group only.
          
          */
          
          public class MethodValidationPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor
          
                 implements InitializingBean {
          
          
          
          
              private Class<? extends Annotation> validatedAnnotationType=Validated.class;
          
          
          
          
              @Nullable
          
              private Validator validator;
          
              
          
              .....
          
                  
          
              /**
          
               * 設(shè)置Validator
          
               * Set the JSR-303 Validator to delegate to for validating methods.
          
               * <p>Default is the default ValidatorFactory's default Validator.
          
               */
          
              public void setValidator(Validator validator) {
          
                 // Unwrap to the native Validator with forExecutables support
          
                 if (validator instanceof LocalValidatorFactoryBean) {
          
                    this.validator=((LocalValidatorFactoryBean) validator).getValidator();
          
                 }
          
                 else if (validator instanceof SpringValidatorAdapter) {
          
                    this.validator=validator.unwrap(Validator.class);
          
                 }
          
                 else {
          
                    this.validator=validator;
          
                 }
          
              }
          
          
          
          
              /**
          
               * Create AOP advice for method validation purposes, to be applied
          
           * with a pointcut for the specified 'validated' annotation.
          
               * @param validator the JSR-303 Validator to delegate to
          
               * @return the interceptor to use (typically, but not necessarily,
          
               * a {@link MethodValidationInterceptor} or subclass thereof)
          
               * @since 4.2
          
               */
          
              protected Advice createMethodValidationAdvice(@Nullable Validator validator) {
          
                 // 創(chuàng)建了方法調(diào)用時的攔截器
          
                 return (validator !=null ? new MethodValidationInterceptor(validator) : new MethodValidationInterceptor());
          
              }
          
          
          
          
          }

          真正執(zhí)行方法調(diào)用時,會走到 MethodValidationInterceptor.invoke,進(jìn)行約束校驗(yàn)。

          public class MethodValidationInterceptor implements MethodInterceptor {
          
              @Override
          
              public Object invoke(MethodInvocation invocation) throws Throwable {
          
                  // Avoid Validator invocation on FactoryBean.getObjectType/isSingleton
          
                  if (isFactoryBeanMetadataMethod(invocation.getMethod())) {
          
                     return invocation.proceed();
          
                  }
          
              
          
                  // Standard Bean Validation 1.1 API
          
                  ExecutableValidator execVal=this.validator.forExecutables();
          
                  ...
          
              
          
                  try {
          
                     // 執(zhí)行約束校驗(yàn)
          
                     result=execVal.validateParameters(
          
                           invocation.getThis(), methodToValidate, invocation.getArguments(), groups);
          
                  }
          
                  catch (IllegalArgumentException ex) {
          
                     // Probably a generic type mismatch between interface and impl as reported in SPR-12237 / HV-1011
          
                     // Let's try to find the bridged method on the implementation class...
          
                     methodToValidate=BridgeMethodResolver.findBridgedMethod(
          
                           ClassUtils.getMostSpecificMethod(invocation.getMethod(), invocation.getThis().getClass()));
          
                     result=execVal.validateParameters(
          
                           invocation.getThis(), methodToValidate, invocation.getArguments(), groups);
          
                  }
          
                  
          
                  ...
          
              
          
                  return returnValue;
          
              }
          
          }

          execVal.validateParameters 就是 javax.validation.executable.ExecutableValidator.validateParameters 與上述 3.5 方法層面手工驗(yàn)證一致。

          總結(jié)


          參考文章:

          https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single


          作者:洛峰

          來源:微信公眾號:得物技術(shù)

          出處:https://mp.weixin.qq.com/s/Z7bM6vP-lHJzkzeHpN8N0g

          日常開發(fā)中,我們有時候需要判斷用戶輸入的是數(shù)字還是字母。本文將介紹如何用JavaScript實(shí)現(xiàn)這一功能。

          檢查輸入值是否是數(shù)字或字母

          要判斷輸入值是數(shù)字還是字母,我們可以通過JavaScript獲取輸入框的值,然后使用isNaN函數(shù)來檢查輸入值是否為數(shù)字。

          例如,假設(shè)我們有如下表單:

          <form name="myForm">
            年齡: <input type="text" name="age">
            <input type="submit" value="提交">
          </form>

          我們可以通過以下JavaScript代碼來獲取表單,并檢查age字段中是否輸入了數(shù)字:

          const { myForm }=document.forms;
          myForm.addEventListener('submit', (e)=> {
            e.preventDefault();
            const x=myForm.age.value;
            if (isNaN(x)) {
              alert("必須輸入數(shù)字");
            }
          });

          代碼詳解

          1. 獲取表單元素
          const { myForm }=document.forms;

          通過document.forms獲取表單,并使用解構(gòu)賦值的方式獲取我們需要的myForm表單。

          1. 添加提交事件監(jiān)聽器
          myForm.addEventListener('submit', (e)=> {
              e.preventDefault();
          })

          使用addEventListener方法監(jiān)聽表單的submit事件,并在事件觸發(fā)時執(zhí)行回調(diào)函數(shù)?;卣{(diào)函數(shù)中,首先調(diào)用e.preventDefault()來阻止表單的默認(rèn)提交行為。

          1. 獲取輸入框的值
          const x=myForm.age.value;

          從表單中獲取age輸入框的值。

          1. 檢查輸入值是否為數(shù)字
          if (isNaN(x)) {
            alert("必須輸入數(shù)字");
          }

          使用isNaN函數(shù)檢查輸入值是否為數(shù)字。如果isNaN返回true,說明輸入的不是數(shù)字,此時彈出警告框提示用戶“必須輸入數(shù)字”。

          結(jié)束

          通過以上步驟,我們可以輕松地用JavaScript判斷輸入值是數(shù)字還是字母。isNaN函數(shù)在這里起到了關(guān)鍵作用,它能夠有效地幫助我們識別非數(shù)字輸入。在實(shí)際開發(fā)中,這種驗(yàn)證方式能夠提高表單數(shù)據(jù)的準(zhǔn)確性,提升用戶體驗(yàn)。

          希望這篇文章對你有所幫助,趕快試試在你的項(xiàng)目中實(shí)現(xiàn)這個功能吧!如果有任何問題或疑惑,歡迎在評論區(qū)留言討論。

          很多網(wǎng)站注冊的時候,需要我們填寫電話號碼,本文主要介紹了javascript實(shí)現(xiàn)判斷電話號碼實(shí)例,需要的朋友可以參考下:

          效果:

          html:

          span標(biāo)簽里面的內(nèi)容主要是用來寫提示的,比如輸錯了,就會提示您“請輸入正確的手機(jī)號” 如果輸?shù)恼_,就會提示“OK”;

          js代碼:


          主站蜘蛛池模板: 亚洲AV成人一区二区三区在线看 | 国产精品毛片一区二区| 免费无码A片一区二三区| 国产精品视频免费一区二区| 日本一区二区三区高清| 精品国产一区二区三区久久狼 | 亚洲日韩中文字幕一区| 爆乳无码AV一区二区三区| 极品人妻少妇一区二区三区| 国产韩国精品一区二区三区 | 亚洲爆乳精品无码一区二区| 亚洲AV综合色区无码一区| 精品国产一区二区三区AV性色| 日韩一本之道一区中文字幕| 性色AV 一区二区三区| 日韩一区二区免费视频| 国产精品久久久久久一区二区三区 | 在线观看一区二区三区av| 狠狠做深爱婷婷综合一区| 日韩精品一区二区三区在线观看| 无码丰满熟妇浪潮一区二区AV| 国产精品一区二区久久精品| 在线播放一区二区| 日韩人妻一区二区三区免费| 文中字幕一区二区三区视频播放| 精品人妻少妇一区二区| 国产一区二区高清在线播放| 亚洲成av人片一区二区三区| 女人和拘做受全程看视频日本综合a一区二区视频 | 人妻体内射精一区二区三四| 精品久久一区二区| 日本午夜精品一区二区三区电影| 国产一区二区三区91| 亚洲国产老鸭窝一区二区三区| 日本免费电影一区二区| 精品人妻码一区二区三区| 亚洲a∨无码一区二区| 成人区人妻精品一区二区不卡| 国产一区二区三区手机在线观看| 日韩精品无码Av一区二区| 一区二区三区影院|