Hibernate-Validator 5.1.0.Final 无法解析自定义占位符的问题

本文介绍了一个关于Hibernate Validator在使用过程中的异常问题及其解决方案。问题出现在ResourceBundleMessageInterpolator类中的interpolateExpression方法,具体为javax.el.ExpressionFactory无法实例化。通过引入el-impl jar包成功解决了此问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在做一个小项目,想使用Hibernate-validator来做后台数据的验证,在pom中:

<!-- hibernate -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>${hibernate.version}</version>
        </dependency>

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>${hibernate.validator.version}</version>
        </dependency>

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>${hibernate.version}</version>
        </dependency>

结果尝试了N多边都是失败的,在网上搜了很多相关资料,发现使用这个最新版本的jar文章微乎其微。今早,跟踪源代码进去找原因,发现每次在ResourceBundleMessageInterpolator.java类中的interpolateExpression方法[341行InterpolationTerm expression = new InterpolationTerm( term, locale );]中抛出异常——javax.el.ExpressionFactory无法实例化,继续跟踪代码进去,发现InterpolationTerm类的构造方法很简单

public InterpolationTerm(String expression, Locale locale) {
        this.locale = locale;
        this.expression = expression;
        if ( expression.startsWith( EL_DESIGNATION_CHARACTER ) ) {
            this.type = InterpolationTermType.EL;
        }
        else {
            this.type = InterpolationTermType.PARAMETER;
        }
    }

开始认为这里不可能抛出异常,又尝试了很多边,发现问题就出现InterpolationTerm实例化过程中,既然构造方法没问题,那么肯定在其他的static块或者方法中存在问题,继续找相关代码。编译器已经提示存在问题了

static {
        expressionFactory = ExpressionFactory.newInstance();
    }

问题在这里,ExpressionFactory工厂类里未定义newInstance方法。

至此,问题原因找到了,是由于ExpressionFactory工厂类出错了。

 

既然找到了错误,那么这个类是在哪个jar包里呢?

结果发现这个类是在jsp-api.jar包里,那么也就是说hibernate-validator.5.1.0.Final不能使用jsp-api.jar的ExpressionFacotry类,那使用哪个呢?

找到了原因,距离问题解决就不远了,继续在网上找答案……

ExpressionFactory这个类是属于el-api.jar,解决办法就是引入下面的jar文件(会把el-api.jar导入)

<dependency>
    <groupId>org.glassfish.web</groupId>
    <artifactId>el-impl</artifactId>
    <version>2.2</version>
</dependency>

 

至此问题解决了。

 

备注:Tomcat6.x下问题依然存在,主要原因是servlet/jsp的api版本低;换成tomcat7.x及以上就没问题了

 

转载于:https://www.cnblogs.com/linxyz/p/3672937.html

jakarta.validation.UnexpectedTypeException: HV000030: No validator could be found for constraint 'jakarta.validation.constraints.NotBlank' validating type 'java.lang.Integer'. Check configuration for 'user_Id' at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.getExceptionForNullValidator(ConstraintTree.java:116) ~[hibernate-validator-8.0.2.Final.jar:8.0.2.Final] at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.getInitializedConstraintValidator(ConstraintTree.java:162) ~[hibernate-validator-8.0.2.Final.jar:8.0.2.Final] at org.hibernate.validator.internal.engine.constraintvalidation.SimpleConstraintTree.validateConstraints(SimpleConstraintTree.java:58) ~[hibernate-validator-8.0.2.Final.jar:8.0.2.Final] at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateConstraints(ConstraintTree.java:75) ~[hibernate-validator-8.0.2.Final.jar:8.0.2.Final] at org.hibernate.validator.internal.metadata.core.MetaConstraint.doValidateConstraint(MetaConstraint.java:130) ~[hibernate-validator-8.0.2.Final.jar:8.0.2.Final] at org.hibernate.validator.internal.metadata.core.MetaConstraint.validateConstraint(MetaConstraint.java:123) ~[hibernate-validator-8.0.2.Final.jar:8.0.2.Final] at org.hibernate.validator.internal.engine.ValidatorImpl.validateMetaConstraint(ValidatorImpl.java:555) ~[hibernate-validator-8.0.2.Final.jar:8.0.2.Final] at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForSingleDefaultGroupElement(ValidatorImpl.java:518) ~[hibernate-validator-8.0.2.Final.jar:8.0.2.Final]怎么解决
最新发布
08-08
<think>我们正在处理一个Jakarta Validation异常:`UnexpectedTypeException: No validator found for NotBlank on Integer user_Id`。 问题分析: 1. `@NotBlank`注解通常用于字符串(String)类型,因为它会检查字符串不为null且长度大于0(即不是空字符串)。 2. 但是,这里我们将`@NotBlank`用在了Integer类型的字段`user_Id`上。 3. Jakarta Validation(Hibernate Validator是其参考实现)要求注解必须适用于被注解的元素类型。由于`@NotBlank`只支持字符串,所以当用于Integer时,就会抛出`UnexpectedTypeException`。 解决方案: 1. 对于非字符串类型的字段(如Integer、Long等),我们应该使用`@NotNull`注解来确保该字段不为null。 2. 如果还需要检查数值不为0(或者某个特定值),我们可以结合其他注解,例如`@Min`、`@Max`等。 但是,这里用户想要的效果可能是:既不能为null,也不能为0(或者空值?)。注意,Integer是对象类型,null表示未赋值,而0是一个有效的数值。所以,我们需要明确需求: - 如果要求user_Id不能为null且不能为0,那么我们可以使用`@NotNull`和`@Min(1)`(假设最小值为1)。 - 如果只是不能为null,那么使用`@NotNull`即可。 因此,修改注解: 将`@NotBlank`替换为`@NotNull`,如果需要进一步限制数值范围,则添加其他约束注解。 示例代码: 原代码可能类似: ```java public class User { @NotBlank private Integer user_Id; // ... } ``` 修改为: ```java public class User { @NotNull @Min(1) // 可选,如果要求最小值为1 private Integer user_Id; // ... } ``` 注意:`@NotBlank`是专门为字符串设计的,它包含三个检查:非null、非空字符串(长度>0)以及非空白字符(如果使用Hibernate Validator的扩展,则还会检查非空白字符串)。对于非字符串类型,这些检查没有意义。 另外,如果确实需要自定义验证逻辑,我们可以创建自定义的验证注解和验证器。 自定义验证器示例(如果需要验证Integer不为0): 1. 创建自定义注解`@NotZero`(或其他名称): ```java import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.RUNTIME; @Documented @Constraint(validatedBy = NotZeroValidator.class) @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) @Retention(RUNTIME) public @interface NotZero { String message() default "must not be zero"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; } ``` 2. 实现验证器: ```java import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; public class NotZeroValidator implements ConstraintValidator<NotZero, Integer> { @Override public boolean isValid(Integer value, ConstraintValidatorContext context) { // 允许为null,因为可能由@NotNull负责,如果同时需要非null,则应该配合@NotNull使用 if (value == null) { return true; } return value != 0; } } ``` 然后,在字段上使用: ```java public class User { @NotNull @NotZero private Integer user_Id; // ... } ``` 但是,对于大多数情况,使用`@NotNull`和`@Min(1)`就足够了,不需要自定义。 总结: 错误原因是将只适用于字符串的`@NotBlank`用在了Integer上。解决方案是使用`@NotNull`替代,并根据需要添加其他数值约束。 相关问题1. Jakarta Validation中常用的约束注解有哪些? 2. 如何自定义验证注解和验证器? 3. 在Jakarta Validation中,如何处理集合或数组元素的验证?</think>### 解决 UnexpectedTypeException: @NotBlank 在 Integer 类型上的问题 当在 Integer 类型字段上使用 `@NotBlank` 注解时,会触发 `UnexpectedTypeException`,因为 **`@NotBlank` 仅适用于字符串类型**,不能用于数值类型(如 Integer)。以下是解决方案和原理分析: --- ### 问题原因 1. **注解类型不匹配** `@NotBlank`(来自 `jakarta.validation.constraints`)设计用于验证字符串: - 检查字符串不为 `null` - 检查字符串长度 `> 0` - 检查字符串包含非空白字符 但 Integer 是数值类型,无法通过字符串规则验证[^1]。 2. **验证器查找机制** Jakarta Validation 在运行时根据字段类型查找匹配的验证器。由于没有为 Integer 定义的 `@NotBlank` 验证器,抛出异常: ```java jakarta.validation.UnexpectedTypeException: No validator found for constraint 'NotBlank' on type 'java.lang.Integer' ``` --- ### 解决方案 #### ✅ 方案 1:使用 `@NotNull` 替代(推荐) ```java public class User { @NotNull(message = "User ID cannot be null") // ✅ 正确用于对象类型 private Integer user_Id; } ``` - **`@NotNull`** 适用于所有对象类型(包括 Integer),检查值非 `null` - **数值范围检查**:若需验证数值有效性(如大于0),添加额外注解: ```java @NotNull @Positive(message = "User ID must be positive") // 确保值 > 0 private Integer user_Id; ``` #### ✅ 方案 2:自定义验证器(复杂场景) 1. 创建自定义注解 `@NotZero`: ```java import jakarta.validation.Constraint; import jakarta.validation.Payload; import java.lang.annotation.*; @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = NotZeroValidator.class) // 关联验证器 public @interface NotZero { String message() default "Value must not be zero"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; } ``` 2. 实现验证逻辑: ```java import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; public class NotZeroValidator implements ConstraintValidator<NotZero, Integer> { @Override public boolean isValid(Integer value, ConstraintValidatorContext context) { // 允许为 null(由 @NotNull 处理),仅检查非零 return value == null || value != 0; } } ``` 3. 在字段上使用: ```java public class User { @NotNull @NotZero // 自定义注解 private Integer user_Id; } ``` --- ### 关键区别:`@NotBlank` vs `@NotNull` | 注解 | 适用类型 | 检查内容 | |--------------|-------------|-----------------------------| | `@NotBlank` | `String` | 非 null + 非空字符串 + 非空白符 | | `@NotNull` | 任意对象 | 非 null | | `@Min`/`@Max`| 数值类型 | 数值范围 | --- ### 最佳实践 1. **对象类型**(Integer/Long/Double): - 用 `@NotNull` 检查 null - 用 `@Positive`、`@Negative`、`@Min`、`@Max` 检查数值有效性 ```java @NotNull @Positive private Integer user_Id; ``` 2. **字符串类型**: ```java @NotBlank // 仅用于 String! private String username; ``` 3. **集合类型**: ```java @NotEmpty // 检查非空集合(非 null 且 size>0) private List<String> items; ``` > 💡 提示:所有 Jakarta Validation 注解需配合 `@Valid` 在 Controller 层触发验证: > ```java > public ResponseEntity<?> createUser(@Valid @RequestBody User user) { ... } > ``` --- ### 相关问题 1. 如何为枚举类型定义自定义验证器? 2. Jakarta Validation 中 `@Valid` 和 `@Validated` 有何区别? 3. 如何全局处理验证失败时的异常消息? 4. 在 Spring Boot 中如何自定义验证错误响应格式? [^1]: Jakarta Bean Validation 规范要求约束注解必须匹配目标类型,`@NotBlank` 仅支持 `CharSequence` 类型。 [^2]: 自定义验证器需实现 `ConstraintValidator` 接口并注册到约束注解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值