版本不匹配引发的坑:javax.validation.UnexpectedTypeException_ HV000030_ No validator could be found for constraint

本文由 简悦 SimpRead 转码, 原文地址 www.cnblogs.com

发现一个 API 报了错: https://img2018.cnblogs.com/blog/280044/202001/280044-20200112154816347-1158016752.png

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
javax.validation.UnexpectedTypeException: HV000030: No validator could be found for constraint 'javax.validation.constraints.NotEmpty' validating type 'java.lang.String'. Check configuration for 'cancelReason'
    at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.throwExceptionForNullValidator(ConstraintTree.java:229) ~[hibernate-validator-5.3.6.Final.jar:5.3.6.Final]
    at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.getConstraintValidatorNoUnwrapping(ConstraintTree.java:310) ~[hibernate-validator-5.3.6.Final.jar:5.3.6.Final]
    at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.getConstraintValidatorInstanceForAutomaticUnwrapping(ConstraintTree.java:244) ~[hibernate-validator-5.3.6.Final.jar:5.3.6.Final]
    at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.getInitializedConstraintValidator(ConstraintTree.java:163) ~[hibernate-validator-5.3.6.Final.jar:5.3.6.Final]
    at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateConstraints(ConstraintTree.java:116) ~[hibernate-validator-5.3.6.Final.jar:5.3.6.Final]
    at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateConstraints(ConstraintTree.java:87) ~[hibernate-validator-5.3.6.Final.jar:5.3.6.Final]
    at org.hibernate.validator.internal.metadata.core.MetaConstraint.validateConstraint(MetaConstraint.java:73) ~[hibernate-validator-5.3.6.Final.jar:5.3.6.Final]
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateMetaConstraint(ValidatorImpl.java:621) ~[hibernate-validator-5.3.6.Final.jar:5.3.6.Final]
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraint(ValidatorImpl.java:584) ~[hibernate-validator-5.3.6.Final.jar:5.3.6.Final]
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForSingleDefaultGroupElement(ValidatorImpl.java:528) ~[hibernate-validator-5.3.6.Final.jar:5.3.6.Final]
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForDefaultGroup(ValidatorImpl.java:496) ~[hibernate-validator-5.3.6.Final.jar:5.3.6.Final]
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForCurrentGroup(ValidatorImpl.java:461) ~[hibernate-validator-5.3.6.Final.jar:5.3.6.Final]
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateInContext(ValidatorImpl.java:411) ~[hibernate-validator-5.3.6.Final.jar:5.3.6.Final]
    at org.hibernate.validator.internal.engine.ValidatorImpl.validate(ValidatorImpl.java:208) ~[hibernate-validator-5.3.6.Final.jar:5.3.6.Final]
    at org.springframework.validation.beanvalidation.SpringValidatorAdapter.validate(SpringValidatorAdapter.java:103) ~[spring-context-4.3.17.RELEASE.jar:4.3.17.RELEASE]
    at org.springframework.boot.autoconfigure.web.WebMvcValidator.validate(WebMvcValidator.java:64) ~[spring-boot-autoconfigure-1.5.13.RELEASE.jar:1.5.13.RELEASE]
    at org.springframework.validation.DataBinder.validate(DataBinder.java:895) ~[spring-context-4.3.17.RELEASE.jar:4.3.17.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.validateIfApplicable(AbstractMessageConverterMethodArgumentResolver.java:270) ~[spring-webmvc-4.3.17.RELEASE.jar:4.3.17.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:133) ~[spring-webmvc-4.3.17.RELEASE.jar:4.3.17.RELEASE]
    at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121) ~[spring-web-4.3.17.RELEASE.jar:4.3.17.RELEASE]
    at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:158) ~[spring-web-4.3.17.RELEASE.jar:4.3.17.RELEASE]
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:128) ~[spring-web-4.3.17.RELEASE.jar:4.3.17.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97) ~[spring-webmvc-4.3.17.RELEASE.jar:4.3.17.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827) ~[spring-webmvc-4.3.17.RELEASE.jar:4.3.17.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738) ~[spring-webmvc-4.3.17.RELEASE.jar:4.3.17.RELEASE]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) ~[spring-webmvc-4.3.17.RELEASE.jar:4.3.17.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967) ~[spring-webmvc-4.3.17.RELEASE.jar:4.3.17.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901) ~[spring-webmvc-4.3.17.RELEASE.jar:4.3.17.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) [spring-webmvc-4.3.17.RELEASE.jar:4.3.17.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doPut(FrameworkServlet.java:883) [spring-webmvc-4.3.17.RELEASE.jar:4.3.17.RELEASE]

一、Spring MVC 的参数校验 Spring 的参数校验会用到两个库:validation-api,hibernate-validator validation-api 是一套标准,hibernate-validator 实现了此标准。 JSR-303 是 Java EE 6 中的一项子规范,叫做 BeanValidation,官方参考实现是 hibernate-validator。

二、HV000030: No validator 报这个错,一般有两种情况: (1)约束与对象属性不匹配,譬如在 String 上使用 javax.validation.constraints.Future 就会报这个错 (2)真的是没有相关的 Validator

一下子觉得很奇怪,难道 javax.validation.constraints.NotEmpty 不能用在 java.lang.String?? https://img2018.cnblogs.com/blog/280044/202001/280044-20200112160201164-203757147.png

这有什么错呢!! 真的是没有 Validator?

如果有,会是这样的,以 javax.validation.constraints.Future 为例: https://img2018.cnblogs.com/blog/280044/202001/280044-20200112172750543-2134477547.png

https://img2018.cnblogs.com/blog/280044/202001/280044-20200112155752634-787853954.png

还真没有!!但是 @NotEmpty 是有两个的 看了下线上在跑的的 API,发现使用的是 org.hibernate.validator.constraints.NotEmpty。 刚才也看到了,也是没有对应 Validator 但是有一些与 javax.validation.constraints.NotEmpty 不一样的地方:使用了两个元约束 @NotNull 和 @Size(min=1) https://img2018.cnblogs.com/blog/280044/202001/280044-20200112160743958-1097119539.png https://img2018.cnblogs.com/blog/280044/202001/280044-20200112160757198-937546540.png

也就是一个校验不需要一个专门的 Validator 也是可以的?

  三、ComposingConstraints【组合约束】

打断点看了下, hibernate-validator 在处理约束时,使用组合约束的概念【可扩展性好强】。 具体工作流程如下: (1)先递归校验组合约束。譬如 org.hibernate.validator.constraints.NotEmpty 中的 @NotNull 和 @Size(min=1) (2)校验主约束,如果有则校验;否则,跳过。譬如 org.hibernate.validator.constraints.NotEmpty (3)处理组合约束的校验结果。有三种策略:OR,AND,ALL_FALSE org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree#validateConstraints(org.hibernate.validator.internal.engine.ValidationContext, org.hibernate.validator.internal.engine.ValueContext<?,V>, java.util.Set<javax.validation.ConstraintViolation>) https://img2018.cnblogs.com/blog/280044/202001/280044-20200112173748058-324983844.png

org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree#validateComposingConstraints https://img2018.cnblogs.com/blog/280044/202001/280044-20200112164624151-1205100079.png

四、为什么会有这个一定会报错的 javax.validation.constraints.NotEmpty 呢? 大家都知道 Spring Boot 帮助我们做了一件脏活、累活【感恩】:依赖包的版本管理。难道此处有 bug? 使用 IDEA 中的 Maven Helper 插件看一下: https://img2018.cnblogs.com/blog/280044/202001/280044-20200112172002066-1066604554.png

Jump to Source,原来是 pom.xml 中显式引入 2.0.1.Final 的 validator-api https://img2018.cnblogs.com/blog/280044/202001/280044-20200112172210315-145219696.png

 Spring Boot1.5.13 中使用的是 1.1.0.Final https://img2018.cnblogs.com/blog/280044/202001/280044-20200112172255914-1298208598.png

问了下当时添加此依赖的同学,反馈是从老项目中继承过来的,当时是为了解决 JPA 的一个问题。不过现在已经全部改用 Mybatis,那么这个是可以去掉的。 把项目所有的微服务 check 了一遍,发现只有这个项目有。 去掉并回归了下,相关接口并没有受到影响。OK, 收工

 补充: validation-api,hibernate-validator 依赖及匹配的版本:

https://img2018.cnblogs.com/blog/280044/202001/280044-20200114092529989-2035529255.png

 https://img2018.cnblogs.com/blog/280044/202001/280044-20200114092539229-999904602.png

 https://img2018.cnblogs.com/blog/280044/202001/280044-20200114092549026-1179596176.png

参考: https://www.cnblogs.com/softidea/p/5907423.html