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

137

我尝试使用Hibernate Validator编写非常简单的应用程序:

我的步骤:

在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

我做错了什么?


1
将 hibernate-validator 升级到 5.2.4.Final 对我解决了问题。 - fracz
2
@fracz 我的 hibernate-validator 版本是 5.2.4.Final,但异常仍然存在。 - Alfonso Nishikawa
一个更新的版本6.0.16.Final适合我。 - Sankalp
16个回答

177

在将以下依赖项添加到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 Validator

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>

6
Bean Validation 1.1 要求在类路径中具有表达式语言的依赖项。参见 http://hibernate.org/validator/documentation/getting-started/。 - Hardy
1
<dependency> <groupId>org.glassfish.web</groupId> <artifactId>javax.el</artifactId> <version>2.2.4</version> <scope>runtime</scope> </dependency> 这就足够了,因为Hibernate Validator已经依赖于javax.el-api。 - mvera
9
对我来说<dependency><groupId>javax.el</groupId><artifactId>javax.el-api</artifactId><version>2.3.1</version></dependency>就足够了。 - Sled
1
<dependency> <groupId>javax.el</groupId> <artifactId>el-api</artifactId> <version>2.2</version> </dependency> 解决了我的问题。 - zhy2002
3
在 SE 环境下,看起来他们在 GitHub 页面上都推荐使用这两个:https://github.com/hibernate/hibernate-validator。不过对我来说,顶部那个已经足够了。 - riddle_me_this
显示剩余4条评论

60

只是去做

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

17
为什么 hibernate-validator 不依赖于那个依赖项? - thomas.mc.work
1
我不知道为什么,但这会更好。 - Bruno Lee
@thomas.mc.work 我认为避免传递依赖问题是很重要的。 - gstackoverflow
5
实际上,在pom文件中它被标记为依赖项,但具有Maven提供的范围。这意味着如果您的JavaEE服务器没有为您提供一个,您需要自己添加它。 - real_paul
这个解决方案对我不起作用,我正在使用hibernate validator 6.0.4和3.0.0版本的java.el,并且正在使用WebLogic。 请问有人可以帮帮我吗?非常感谢你们提前帮助。 - Kushwaha

42

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

依赖于hibernate-validator

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

使用ParameterMessageInterpolator

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();

2
是的,我不想增加更多的依赖。干得好。 - nokieng
1
哦,这对我来说确实是一个关于库项目的好答案。 - Jin Kwon
3
这应该是被选中的答案。 - anataliocs
这是最好的答案,因为您不必依赖其他依赖项。 - Philippe Gioseffi
这是给定问题的正确答案。 - Karol Król

24

如果你在使用Tomcat作为服务器运行时,而在测试中出现了这个错误(因为在测试期间Tomcat运行时不可用),那么包括Tomcat EL运行时而不是Glassfish的EL运行时是有道理的。这将是:

    <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>

1
仍然爱你。看起来你只需要包含 tomcat-jasper-el 依赖项,因为它似乎会传递包含 tomcat-el-api 依赖项。 - xdhmoore

16

如果您正在使用Spring Boot的Starter,那么这个依赖项会同时添加tomcat-embed-elhibernate-validator依赖项:

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

14
关于Hibernate验证器文档页面,您需要定义一个依赖关系到JSR-341实现:
<dependency>
   <groupId>org.glassfish</groupId>
   <artifactId>javax.el</artifactId>
   <version>3.0.1-b11</version>
</dependency>

13
Hibernate验证器需要但不包含表达式语言(EL)实现。添加一个依赖项将会解决这个问题。
<dependency>
   <groupId>org.glassfish</groupId>
   <artifactId>jakarta.el</artifactId>
   <version>3.0.3</version>
</dependency>

这个要求在开始使用Hibernate Validator文档中有记录。在Java EE环境中,它将由容器提供。在像您的独立应用程序这样的独立应用程序中,需要提供它。

Hibernate Validator also requires an implementation of the Unified Expression Language (JSR 341) for evaluating dynamic expressions in constraint violation messages.

When your application runs in a Java EE container such as WildFly, an EL implementation is already provided by the container.

In a Java SE environment, however, you have to add an implementation as dependency to your POM file. For instance, you can add the following dependency to use the JSR 341 reference implementation:

<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>

此评论所述,必须选择兼容的表达式语言版本。Hibernate Validator的Glassfish实现被指定为提供范围依赖项,因此在那里指定的版本应该可以无问题地工作。特别是,Hibernate Validator 7使用Glassfish EL实现的第4版,Hibernate 6使用第3版。

Spring Boot

在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>

2
注意版本兼容性:我刚发现 Hibernate Validator 6.1 无法与 org.glassfish:jakarta.el:4.0.1 兼容,我需要坚持使用 org.glassfish:jakarta.el:3.0.3(直到我升级 Hibernate Validator)。 - Étienne Miret
@EtienneMiret Hibernate 7 文档 使用的是 4.0.0,因此更新到那个版本确实可以让您使用 EL 4 实现。另一方面,Hibernate 6 文档 使用的是 3.0.3。 - M. Justin
1
@EtienneMiret 我已经更新了答案,提到了版本兼容性问题,并介绍了一种确保选择兼容版本的方法。 - M. Justin

10

Jakarta 命名空间

作为从 OracleEclipse Foundation 的移交的一部分,Java EE 被重命名为 Jakarta EE。在 Jakarta EE 9 中,Java 包名称已从 javax.* 更改为 jakarta.*

M. Justin 的回答 关于 Jakarta 是正确的。我添加了这个回答以提供更多的解释和具体的例子。

接口与实现

Jakarta Bean Validation 是 Java API 的一个规范。该规范的二进制库仅包含接口,没有可执行代码。因此,我们还需要这些接口的实现。

我只知道一个实现Jakarta Bean Validation第2和3版本规范的方法:Hibernate Validator第6和7版本(分别)。

桌面和控制台应用程序

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

对于桌面和控制台应用程序,我们没有这样的符合Jakarta的Web容器。因此,您必须将接口jar和实现jar捆绑到您的应用程序中。

您可以使用依赖管理工具,如Maven, Gradle, 或 Ivy来下载并捆绑接口和实现jars。

Jakarta表达式语言

为了运行Jakarta Bean Validation,我们还需要另一个Jakarta工具:Jakarta Expression Language,一种用于嵌入和评估表达式特定目的编程语言Jakarta Expression Language也被简称为ELJakarta Expression Language由Jakarta EE定义为规范,您需要下载接口的jar包,并获取另一个jar包中这些接口的实现。
您可以选择不同的实现。截至2021-03,我知道Eclipse Foundation提供了一个名为Eclipse Glassfish的实现作为可免费下载的单独库。可能还有其他实现,例如IBM Corporation的Open Liberty。寻找适合您需求的实现。

Maven POM依赖项

将所有信息汇总起来,您需要四个JAR文件:每个项目都需要一对接口和实现的JAR文件,分别是Jakarta Bean ValidationJakarta Expression Language
  • Jakarta Bean Validation
    • 接口
    • 实现
  • Jakarta Expression Language
    • 接口
    • 实现

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

如上所述,您可能能够找到另一个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

4

如果使用Spring Boot,这个功能非常好用。即使使用Spring Reactive Mongo也是如此。

<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();
    }
}

2
我遇到了同样的问题,上面的答案没有帮助。我需要进行调试并找到原因。
 <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之后,这个对我起作用了。

这个在使用Hadoop时有效。对于使用gradle的任何人:implementation(group: 'org.apache.hadoop', name: 'hadoop-common', version: '3.3.6') { exclude module: 'jsp-api' } - ChamodyaDias

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接