javax.validation.ValidationException: HV000183: 无法加载 'javax.el.ExpressionFactory'



我尝试使用休眠验证器编写非常简单的应用程序:

我的步骤:

在pom.xml中添加了以下依赖项:

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>5.1.1.Final</version>
</dependency>

编写了以下代码:

class Configuration {
    Range(min=1,max=100)
    int threadNumber;
    //...
    
    public static void main(String[] args) {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        Validator validator = factory.getValidator();
        
        Configuration configuration = new Configuration();
        configuration.threadNumber = 12;
            //...
        
        Set<ConstraintViolation<Configuration>> constraintViolations = validator.validate(configuration);
        System.out.println(constraintViolations);
    }
}

我得到以下堆栈跟踪:

Exception in thread "main" javax.validation.ValidationException: Unable to instantiate Configuration.
    at javax.validation.Validation$GenericBootstrapImpl.configure(Validation.java:279)
    at javax.validation.Validation.buildDefaultValidatorFactory(Validation.java:110)
    ...
    at org.hibernate.validator.internal.engine.ConfigurationImpl.<init>(ConfigurationImpl.java:110)
    at org.hibernate.validator.internal.engine.ConfigurationImpl.<init>(ConfigurationImpl.java:86)
    at org.hibernate.validator.HibernateValidator.createGenericConfiguration(HibernateValidator.java:41)
    at javax.validation.Validation$GenericBootstrapImpl.configure(Validation.java:276)
    ... 2 more

我做错了什么?

它在添加到以下依赖项后pom.xml工作:

<dependency>
   <groupId>javax.el</groupId>
   <artifactId>javax.el-api</artifactId>
   <version>2.2.4</version>
</dependency>
<dependency>
   <groupId>org.glassfish.web</groupId>
   <artifactId>javax.el</artifactId>
   <version>2.2.4</version>
</dependency>

Hibernate验证器入门:

Hibernate Validator 还需要统一表达式语言 (JSR 341( 的实现,以评估约束冲突消息中的动态表达式。当应用程序在 Java EE 容器(如 WildFly(中运行时,容器已经提供了 EL 实现。但是,在 Java SE 环境中,您必须将实现作为依赖项添加到 POM 文件中。例如,您可以添加以下两个依赖项以使用 JSR 341 参考实现:

<dependency>
   <groupId>javax.el</groupId>
   <artifactId>javax.el-api</artifactId>
   <version>2.2.4</version>
</dependency>
<dependency>
   <groupId>org.glassfish.web</groupId>
   <artifactId>javax.el</artifactId>
   <version>2.2.4</version>
</dependency>

只做

<dependency>
   <groupId>javax.el</groupId>
   <artifactId>javax.el-api</artifactId>
   <version>2.2.4</version>
</dependency>

如果您不需要javax.el(例如在JavaSE应用程序中(,请使用Hibernate验证器的ParameterMessageInterpolator。Hibernate验证器是一个独立的组件,可以在没有Hibernate本身的情况下使用。

依赖于休眠验证器

<dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-validator</artifactId>
   <version>6.0.16.Final</version>
</dependency>

使用参数消息插值器

import javax.validation.Validation;
import javax.validation.Validator;
import org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator;
private static final Validator VALIDATOR =
  Validation.byDefaultProvider()
    .configure()
    .messageInterpolator(new ParameterMessageInterpolator())
    .buildValidatorFactory()
    .getValidator();
如果您使用 tomcat

作为服务器运行时,并且在测试中遇到此错误(因为 tomcat 运行时在测试期间不可用(,那么包含 tomcat el 运行时而不是来自 glassfish 的运行时是有意义的(。这将是:

    <dependency>
        <groupId>org.apache.tomcat</groupId>
        <artifactId>tomcat-el-api</artifactId>
        <version>8.5.14</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.tomcat</groupId>
        <artifactId>tomcat-jasper-el</artifactId>
        <version>8.5.14</version>
        <scope>test</scope>
    </dependency>

如果你在初学者中使用 spring boot,这个依赖关系会同时添加tomcat-embed-elhibernate-validator依赖关系:

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

关于Hibernate验证器文档页面,您必须定义对JSR-341实现的依赖关系:

<dependency>
   <groupId>org.glassfish</groupId>
   <artifactId>javax.el</artifactId>
   <version>3.0.1-b11</version>
</dependency>

Hibernate验证器需要(但不包括(表达式语言(EL(实现。 添加一个对一个意志的依赖将解决问题。

<dependency>
   <groupId>org.glassfish</groupId>
   <artifactId>jakarta.el</artifactId>
   <version>3.0.3</version>
</dependency>

此要求记录在 Hibernate 验证程序入门文档中。 在 Java EE 环境中,它将由容器提供。 在像您这样的独立应用程序中,需要提供它。

Hibernate Validator 还需要统一表达式语言 (JSR 341( 的实现,以评估约束冲突消息中的动态表达式。

当应用程序在 Java EE 容器(如 WildFly(中运行时,容器已经提供了 EL 实现。

但是,在 Java SE 环境中,您必须将实现作为依赖项添加到 POM 文件中。例如,您可以添加以下依赖项以使用 JSR 341 参考实现:

<dependency>
  <groupId>org.glassfish</groupId>
  <artifactId>jakarta.el</artifactId>
  <version>${version.jakarta.el-api}</version>
</dependency>

表达式语言实现

存在几种 EL 实现。 一个是文档中提到的Jakarta EE Glassfish参考实现。 另一个是嵌入式Tomcat,默认情况下由当前版本的Spring Boot使用。 该版本的 EL 可以按如下方式使用:

<dependency>
   <groupId>org.apache.tomcat.embed</groupId>
   <artifactId>tomcat-embed-el</artifactId>
   <version>9.0.48</version>
</dependency>

如本注释中所述,必须选择表达式语言的兼容版本。 Glassfish实现被指定为Hibernate验证器的提供范围依赖项,因此在那里指定的版本应该可以正常工作。 特别是,Hibernate Validator 7使用Glassfish EL实现的版本4,Hibernate 6使用版本3。

弹簧启动

在Spring Boot项目中,通常会使用spring-boot-starter-validation依赖项,而不是直接指定Hibernate验证器和EL库。 该依赖关系包括org.hibernate.validator:hibernate-validatortomcat-embed-el

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

雅加达命名空间

作为从Oracle移交给Eclipse基金会的一部分,Java EE将更名为Jakarta EE。在Jakarta EE 9中,Java包名称从javax.*更改为jakarta.*

M. Justin的回答对雅加达是正确的。我添加了这个答案以提供更多解释和具体示例。

接口与实现

Jakarta Bean Validation 是 Java 中 API 的规范。此规范的二进制库仅包含接口,不包含可执行代码。所以我们还需要这些接口的实现。

我知道Jakarta Bean Validation版本2和3规范只有一个实现:Hibernate验证器版本6和7(分别(。

桌面和控制台应用

对于 Web 应用程序,符合 Jakarta 标准的 Web 容器将提供执行 Bean 验证所需的接口和实现。

对于桌面和控制台应用程序,我们没有此类符合 Jakarta 标准的 Web 容器。因此,您必须将接口 jar 和实现 jar 与应用捆绑在一起。

您可以使用 MavenGradle 或 Ivy 等依赖管理工具下载并捆绑接口和实现 jar。

雅加达表达语言

要运行 Jakarta Bean Validation,我们还需要另一个 Jakarta 工具:Jakarta Expression Language,一种用于嵌入和评估表达式的特殊用途编程语言。雅加达表达式语言也称为EL。

Jakarta 表达式语言由 Jakarta EE 定义为必须为其下载一罐接口的规范。您还需要在另一个 jar 中获取这些接口的实现。

您可以选择实现。截至 2021-03 年,我知道 Eclipse 基金会的 Eclipse Glassfish 提供了一个实现,作为一个单独的库,我们可以免费下载。可能还有其他实现,例如 IBM 公司的 Open Liberty。货比三家,寻找适合您需求的实现方案。

Maven POM 依赖项

将所有

这些信息放在一起,您需要四个jar:两个项目(Jakarta Bean ValidationJakarta Expression Language(中的一对接口和实现jar。

  • 雅加达豆验证
    • 接口
    • 实现
  • 雅加达表达语言
    • 接口
    • 实现

以下是您需要添加到 Maven POM 文件中的四个依赖项,如果 Maven 是您选择的工具。

如上所述,您也许可以找到EL的另一个实现来替代我在这里使用的Glassfish库。

<!--********| Jakarta Bean Validation  |********-->
<!-- Interface -->
<!-- https://mvnrepository.com/artifact/jakarta.validation/jakarta.validation-api -->
<dependency>
    <groupId>jakarta.validation</groupId>
    <artifactId>jakarta.validation-api</artifactId>
    <version>3.0.0</version>
</dependency>
<!-- Implementation -->
<!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator -->
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>7.0.1.Final</version>
</dependency>
<!-- Jakarta Expression Language -->
<!-- Interface -->
<!-- https://mvnrepository.com/artifact/jakarta.el/jakarta.el-api -->
<dependency>
    <groupId>jakarta.el</groupId>
    <artifactId>jakarta.el-api</artifactId>
    <version>4.0.0</version>
</dependency>
<!-- Implementation -->
<!-- https://mvnrepository.com/artifact/org.glassfish/jakarta.el -->
<dependency>
    <groupId>org.glassfish</groupId>
    <artifactId>jakarta.el</artifactId>
    <version>4.0.1</version>
</dependency>

这应该消除javax.validation.ValidationException: HV000183: Unable to load 'javax.el.ExpressionFactory'错误。

示例用法

您可以使用以下简单类 Car 来测试您的设置。我们对三个成员字段中的每一个都有验证。

package work.basil.example.beanval;
import jakarta.validation.constraints.*;
public class Car
{
    // ---------------|  Member fields  |----------------------------
    @NotNull
    private String manufacturer;
    @NotNull
    @Size ( min = 2, max = 14 )
    private String licensePlate;
    @Min ( 2 )
    private int seatCount;
    // ---------------|  Constructors  |----------------------------
    public Car ( String manufacturer , String licensePlate , int seatCount )
    {
        this.manufacturer = manufacturer;
        this.licensePlate = licensePlate;
        this.seatCount = seatCount;
    }
    // ---------------|  Object overrides  |----------------------------
    @Override
    public String toString ( )
    {
        return "Car{ " +
                "manufacturer='" + manufacturer + ''' +
                " | licensePlate='" + licensePlate + ''' +
                " | seatCount=" + seatCount +
                " }";
    }
}

或者,如果使用 Java 16 及更高版本,请改用更简短的record

package work.basil.example.beanval;
import jakarta.validation.constraints.*;
public record Car (
        @NotNull
        String manufacturer ,
        @NotNull
        @Size ( min = 2, max = 14 )
        String licensePlate ,
        @Min ( 2 )
        int seatCount
)
{
}

运行验证。首先,我们使用成功配置的 Car 对象运行。然后,我们实例化第二个有缺陷的Car对象,违反了三个字段中每个字段的一个约束。

package work.basil.example.beanval;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import java.util.Set;
public class App
{
    public static void main ( String[] args )
    {
        App app = new App();
        app.demo();
    }
    private void demo ( )
    {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        Validator validator = factory.getValidator();
        // No violations.
        {
            Car car = new Car( "Honda" , "ABC-789" , 4 );
            System.out.println( "car = " + car );
            Set < ConstraintViolation < Car > > violations = validator.validate( car );
            System.out.format( "INFO - Found %d violations.n" , violations.size() );
        }
        // 3 violations.
        {
            Car car = new Car( null , "X" , 1 );
            System.out.println( "car = " + car );
            Set < ConstraintViolation < Car > > violations = validator.validate( car );
            System.out.format( "INFO - Found %d violations.n" , violations.size() );
            violations.forEach( carConstraintViolation -> System.out.println( carConstraintViolation.getMessage() ) );
        }
    }
}

运行时。

car = Car{ manufacturer='Honda' | licensePlate='ABC-789' | seatCount=4 }
INFO - Found 0 violations.
car = Car{ manufacturer='null' | licensePlate='X' | seatCount=1 }
INFO - Found 3 violations.
must be greater than or equal to 2
must not be null
size must be between 2 and 14

如果使用 Spring Boot,这很好用。即使有春天反应性的蒙戈。

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

和验证配置:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.core.mapping.event.ValidatingMongoEventListener;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
@Configuration
public class MongoValidationConfig {
    @Bean
    public ValidatingMongoEventListener validatingMongoEventListener() {
        return new ValidatingMongoEventListener(validator());
    }
    @Bean
    public LocalValidatorFactoryBean validator() {
        return new LocalValidatorFactoryBean();
    }
}

对于 SBT,请使用以下版本

val glassfishEl = "org.glassfish" % "javax.el" % "3.0.1-b09"
val hibernateValidator = "org.hibernate.validator" % "hibernate-validator" % "6.0.17.Final"
val hibernateValidatorCdi = "org.hibernate.validator" % "hibernate-validator-cdi" % "6.0.17.Final"

我遇到了同样的问题,上面的答案没有帮助。我需要调试并找到它。

 <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-common</artifactId>
        <version>2.6.0-cdh5.13.1</version>
        <exclusions>
            <exclusion>
                <artifactId>jsp-api</artifactId>
                <groupId>javax.servlet.jsp</groupId>
            </exclusion>
        </exclusions>
    </dependency>

排除 jsp-api 后,它对我有用。

对于使用 Hibernate Validator 7 (org.hibernate.validator:hibernate-validator:7.0.0.Final( 作为 Jakarta Bean Validation 3.0 实现的人,应该使用以下依赖项:

<dependency>
    <groupId>org.glassfish</groupId>
    <artifactId>jakarta.el</artifactId>
    <version>4.0.0</version>
</dependency>

如Hibernate验证器文档中所述

我被困在旧技术上,所以我不得不添加以下内容:

    <dependency>
        <groupId>javax.el</groupId>
        <artifactId>javax.el-api</artifactId>
        <version>3.0.0</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.glassfish</groupId>
        <artifactId>javax.el</artifactId>
        <version>3.0.0</version>
        <scope>test</scope>
    </dependency>

其他答案报告相同的依赖项,我只更新了版本。

对于使用Hibernate验证器8的任何人,你需要使用

<dependency>
    <groupId>org.glassfish.expressly</groupId>
    <artifactId>expressly</artifactId>
    <version>5.0.0</version>
 </dependency>

for gradle :

compile 'javax.el:javax.el-api:2.2.4'

如果您的服务器是 websphere 并且您使用了 spring-boot-starter-validation ,请排除tomcat-embed-el

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
    <exclusions>
         <exclusion>
              <groupId>org.apache.tomcat.embed</groupId>
              <artifactId>tomcat-embed-el</artifactId>
         </exclusion>
    </exclusions>
</dependency>

最新更新