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

Что я не прав?

16 ответов

Решение

Работает после добавления в 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

Делать только

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

Если вам не нужен javax.el (например, в приложении JavaSE), используйте ParameterMessageInterpolator от Hibernate.

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>

Если вы используете весеннюю загрузку со стартерами - эта зависимость добавляет оба tomcat-embed-el а также hibernate-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-b08</version>
</dependency>

Согласно документации по началу работы с Hibernate Validator, необходимо предоставить реализацию языка выражений (EL). В среде Java EE он будет предоставляться контейнером. Однако в отдельном приложении, таком как ваше, его необходимо предоставить.

Для Hibernate Validator также требуется реализация Unified Expression Language (JSR 341) для оценки динамических выражений в сообщениях о нарушении ограничений.

Когда ваше приложение выполняется в контейнере Java EE, таком как WildFly, реализация EL уже предоставляется контейнером.

Однако в среде Java SE вы должны добавить реализацию как зависимость к вашему POM-файлу. Например, вы можете добавить следующую зависимость для использования эталонной реализации JSR 341:

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

Пример зависимости в документации немного устарел, так как в 2018 году язык выражений был переведен на проект Jakarta EE. Чтобы использовать версию языка выражений Jakarta EE, добавьте следующую зависимость Eclipse Glassfish EL:

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

Существуют и другие реализации EL, которые можно использовать, кроме Glassfish. Например, Spring Boot по умолчанию использует встроенный Tomcat. Эту версию EL можно использовать следующим образом:

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

Jakarta пространство имен

В рамках передачи из Oracle в Фонд Затмения , Java EE переименовывается в Джакарте EE . В Jakarta EE 9 имена пакетов Java были изменены с к .

Ответ М. Иустина правильно в отношении Джакарты. Я добавил этот ответ, чтобы дать больше объяснений и конкретных примеров.

Интерфейс против реализации

<em>Jakarta Bean Validation</em> - это спецификация API на Java. Бинарная библиотека для этой спецификации содержит только интерфейсы , а не исполняемый код. Так что нам также нужна реализация этих интерфейсов.

Я знаю только одну реализацию спецификаций Jakarta Bean Validation версий 2 и 3: <em>Hibernate Validator</em> версий 6 и 7 (соответственно).

Настольные и консольные приложения

Для веб-приложений совместимый с Джакартой веб-контейнер будет предоставлять как интерфейс, так и реализацию, необходимые для выполнения проверки компонентов.

Для настольных и консольных приложений у нас нет такого Jakarta-совместимого веб-контейнера. Таким образом, вы должны связать и интерфейса, и jar реализации с вашим приложением.

Вы можете использовать инструмент управления зависимостями, такой как <em>Maven</em>, <em>Gradle</em> или Ivy, для загрузки и объединения <em>jar-</em> файлов интерфейса и реализации.

Джакартский язык выражения

Чтобы запустить Jakarta Bean Validation , нам понадобится еще один инструмент Jakarta: <em>Jakarta Expression Language</em>, <em>язык</em> программирования специального назначения для встраивания и оценки выражений . Джакартский язык выражений также известен как EL .

Jakarta Expression Language определяется Jakarta EE как спецификация, для которой вы должны загрузить банку интерфейсов. И вам также необходимо получить реализацию этих интерфейсов в другом банке.

У вас может быть выбор реализаций. По состоянию на 2021-03 гг. Мне известно о Eclipse Glassfish от Eclipse Foundation, предоставляющей реализацию в виде отдельной библиотеки, которую мы можем скачать бесплатно. Могут быть и другие реализации, например Open Liberty от IBM Corporation. Присмотритесь к реализации, которая соответствует вашим потребностям.

Зависимости Maven POM

Собрав всю эту информацию, вам понадобится четыре jar-файла: пара jar-файлов интерфейса и реализации для каждого из двух проектов, Jakarta Bean Validation и Jakarta Expression Language .

  • Проверка Jakarta Bean
    • Интерфейс
    • Выполнение
  • Джакартский язык выражения
    • Интерфейс
    • Выполнение

Ниже приведены четыре зависимости, которые вам нужно добавить в свой файл Maven POM, если Maven является вашим предпочтительным инструментом.

Как упоминалось выше, вы можете найти другую реализацию EL вместо библиотеки Glassfish, которую я использую здесь.

      <!-- Jakarta Bean Validation -->

<!-- Interface -->
<!-- https://search.maven.org/artifact/jakarta.validation/jakarta.validation-api/3.0.0/jar -->
<dependency>
    <groupId>jakarta.validation</groupId>
    <artifactId>jakarta.validation-api</artifactId>
    <version>3.0.0</version>
</dependency>

<!-- Implementation -->
<!-- https://search.maven.org/artifact/org.hibernate.validator/hibernate-validator/7.0.0.Final/jar -->
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>7.0.0.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>

Это должно устранить ошибка.

Пример использования

Вы можете протестировать свою установку с помощью следующего простого класса. У нас есть проверки для каждого из трех полей-членов.

      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 и новее, используйте более короткий <tcodeid="4377449"></tcodeid="4377449"> вместо.

      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
)
{
}

Запустите проверку. Сначала запускаем с успешно настроенным объектом. Затем мы создаем второй объект неисправен, нарушая по одному ограничению для каждого из трех полей.

      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 это работает хорошо. Даже с 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();
    }
}

Я столкнулся с той же проблемой, и приведенные выше ответы не помогли. Мне нужно отладить и найти.

       <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 у меня это сработало.

Для 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"

Для тех, кто использует 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 Validator

для тех, кто использует Hibernate Validator 8, вам нужно использовать

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

Я застрял на старых технологиях, поэтому мне пришлось добавить следующее:

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

Другие ответы сообщают о тех же зависимостях, я только обновил версии.

Для 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>
Другие вопросы по тегам