Programming

Resolve Expression Variables in Hibernate Validator 8 Custom Violations

Learn how to properly configure Hibernate Validator 8 expression variables in Spring Boot 3 custom constraint violations. Fix #{expMessage} not resolving with our step-by-step guide.

5 answers 1 view

How to resolve expression variables in Hibernate Validator 8 custom constraint violations? My addExpressionVariable('expMessage', value) is returning the literal #{expMessage} instead of the variable value, even after enabling Expression Language features. What configuration is needed for custom violations to properly resolve EL expressions in Spring Boot 3?

Expression variables in Hibernate Validator 8 custom constraint violations require proper configuration of both the validator’s expression language feature levels and Spring Boot’s auto-configuration setup. The issue with addExpressionVariable('expMessage', value) returning the literal #{expMessage} instead of the resolved value typically occurs when the custom violation expression language feature level isn’t properly enabled, or when Spring Boot’s validation configuration isn’t correctly ordered.


Contents


Understanding Hibernate Validator Expression Language in Spring Boot 3

Hibernate Validator, the reference implementation of Jakarta Bean Validation, provides powerful expression language support for dynamic constraint violation messages. In Spring Boot 3 applications, this functionality becomes even more valuable as it allows for context-aware validation that can reference application-specific data in violation messages.

The expression language feature in Hibernate Validator allows you to use variables within violation templates using the #{variableName} syntax. This is particularly useful when you want to include dynamic information in your validation messages that isn’t directly available in the annotated object.

For example, instead of a static message like “Password must be at least 8 characters long”, you could have a dynamic message like “Password must be at least #{minLength} characters long” where minLength is determined by your application configuration.

Spring Boot’s auto-configuration for validation creates a LocalValidatorFactoryBean that integrates seamlessly with Hibernate Validator, but it requires proper configuration to fully leverage expression language features in custom constraint violations.

Common Issues with Expression Variables in Custom Constraint Violations

When working with expression variables in custom constraint violations, developers frequently encounter several issues that prevent proper resolution of #{variableName} expressions:

  1. Literal Expression Output: The most common issue is when addExpressionVariable('expMessage', value) results in the literal text #{expMessage} appearing in the violation message instead of the resolved value. This typically indicates that the expression language evaluator isn’t configured to process variables for custom violations.

  2. Inconsistent Behavior: Some developers find that expression variables work for constraint annotations but fail to resolve in programmatically created violations. This inconsistency points to a configuration gap between different expression language contexts.

  3. Missing Dependencies: Spring Boot 3’s transition to Jakarta EE means that Jakarta EL implementations like org.glassfish.expressly:expressly must be explicitly included on the classpath for expression language features to function properly.

  4. Configuration Order Issues: When custom validation configurations aren’t properly ordered relative to Spring Boot’s ValidationAutoConfiguration, expression language settings may be overridden or not applied correctly.

  5. Message Interpolator Problems: If a custom MessageInterpolator is implemented without proper initialization with an explicit ExpressionFactory, expression variables won’t be resolved correctly.

These issues stem from Hibernate Validator’s separation between constraint annotation evaluation and custom violation evaluation, each requiring separate configuration of expression language feature levels.

Proper Configuration for Expression Language in Hibernate Validator 8

Resolving expression variables in Hibernate Validator 8 requires understanding and configuring two distinct expression language feature levels:

  1. Constraint Expression Language Feature Level: This controls expression evaluation for constraint annotations defined on your model classes. It’s typically set to ExpressionLanguageFeatureLevel.VARIABLES by default.

  2. Custom Violation Expression Language Feature Level: This is specifically for programmatically created violations via HibernateConstraintValidatorContext.addExpressionVariable(). This setting must be explicitly configured to ExpressionLanguageFeatureLevel.VARIABLES for your issue to be resolved.

For Spring Boot 3 applications, the configuration must be implemented as follows:

java
import org.hibernate.validator.cfg.ConstraintMapping;
import org.hibernate.validator.cfg.defs.AssertFalseDef;
import org.hibernate.validator.cfg.defs.AssertTrueDef;
import org.hibernate.validator.cfg.defs.DecimalMaxDef;
import org.hibernate.validator.cfg.defs.DecimalMinDef;
import org.hibernate.validator.cfg.defs.DigitsDef;
import org.hibernate.validator.cfg.defs.EmailDef;
import org.hibernate.validator.cfg.defs.FutureDef;
import org.hibernate.validator.cfg.defs.LengthDef;
import org.hibernate.validator.cfg.defs.MaxDef;
import org.hibernate.validator.cfg.defs.MinDef;
import org.hibernate.validator.cfg.defs.NotBlankDef;
import org.hibernate.validator.cfg.defs.NotEmptyDef;
import org.hibernate.validator.cfg.defs.NotNullDef;
import org.hibernate.validator.cfg.defs.NullDef;
import org.hibernate.validator.cfg.defs.PastDef;
import org.hibernate.validator.cfg.defs.PatternDef;
import org.hibernate.validator.cfg.defs.SizeDef;
import org.hibernate.validator.messageinterpolation.ExpressionLanguageFeatureLevel;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurationBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;

@AutoConfiguration(before = ValidationAutoConfiguration.class)
public class CustomValidationAutoConfiguration {

 @Bean
 @ConditionalOnMissingBean
 public LocalValidatorFactoryBean validatorFactory() {
 LocalValidatorFactoryBean validatorFactory = new LocalValidatorFactoryBean();
 
 // Configure constraint expression language feature level
 validatorFactory.getConstraintMapping()
 .constraintExpressionLanguageFeatureLevel(ExpressionLanguageFeatureLevel.VARIABLES);
 
 // Configure custom violation expression language feature level
 validatorFactory.getConstraintMapping()
 .customViolationExpressionLanguageFeatureLevel(ExpressionLanguageFeatureLevel.VARIABLES);
 
 return validatorFactory;
 }
}

For Spring Boot 3, this configuration must be registered in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports rather than the traditional META-INF/spring.factories file used in earlier versions.

Additionally, ensure you have a Jakarta EL implementation on your classpath. For Maven:

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

For Gradle:

groovy
implementation 'org.glassfish.expressly:expressly:5.0.0'

Step-by-Step Solution: Enabling Expression Variables in Custom Violations

Here’s a comprehensive step-by-step solution to resolve expression variables in Hibernate Validator 8 custom constraint violations:

Step 1: Create a Custom Validation Configuration

First, create a custom auto-configuration class that loads before Spring Boot’s validation auto-configuration:

java
import org.hibernate.validator.cfg.ConstraintMapping;
import org.hibernate.validator.messageinterpolation.ExpressionLanguageFeatureLevel;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurationBefore;
import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;

@AutoConfiguration(before = ValidationAutoConfiguration.class)
public class CustomValidatorAutoConfiguration {

 @Bean
 public LocalValidatorFactoryBean validatorFactory() {
 LocalValidatorFactoryBean validatorFactory = new LocalValidatorFactoryBean();
 
 // Configure both expression language feature levels
 ConstraintMapping constraintMapping = validatorFactory.getConstraintMapping();
 constraintMapping.constraintExpressionLanguageFeatureLevel(ExpressionLanguageFeatureLevel.VARIABLES);
 constraintMapping.customViolationExpressionLanguageFeatureLevel(ExpressionLanguageFeatureLevel.VARIABLES);
 
 return validatorFactory;
 }
}

Step 2: Register the Configuration

For Spring Boot 3.x, create the file META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports with the following content:

com.yourpackage.CustomValidatorAutoConfiguration

Replace com.yourpackage with your actual package name.

Step 3: Add Jakarta EL Dependency

Ensure you have a Jakarta EL implementation in your dependencies. For Maven:

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

Step 4: Implement Custom Constraint Validator

Here’s how to properly implement a custom constraint validator that uses expression variables:

java
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext;
import org.hibernate.validator.messageinterpolation.ExpressionLanguageFeatureLevel;

public class CustomPasswordValidator implements ConstraintValidator<ValidPassword, String> {

 @Override
 public boolean isValid(String password, ConstraintValidatorContext context) {
 if (password == null) {
 return true; // Null values are handled by @NotNull
 }
 
 // Get the Hibernate-specific constraint validator context
 HibernateConstraintValidatorContext hibernateContext = context.unwrap(HibernateConstraintValidatorContext.class);
 
 // Add expression variables
 hibernateContext.addExpressionVariable("minLength", 8);
 hibernateContext.addExpressionVariable("maxLength", 20);
 hibernateContext.addExpressionVariable("expMessage", "Password must be between 8 and 20 characters");
 
 // Disable default violation message
 context.disableDefaultConstraintViolation();
 
 // Build custom violation with expression language
 context.buildConstraintViolationWithTemplate(
 "#{expMessage}. Current length: #{password.length()}"
 )
 .addConstraintViolation();
 
 return password.length() >= 8 && password.length() <= 20;
 }
}

Step 5: Create the Custom Constraint Annotation

java
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.*;

@Documented
@Constraint(validatedBy = CustomPasswordValidator.class)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidPassword {
 String message() default "Invalid password";
 Class<?>[] groups() default {};
 Class<? extends Payload>[] payload() default {};
}

Step 6: Use the Custom Constraint

Apply your custom constraint to your model:

java
public class UserRegistrationDto {
 @ValidPassword
 private String password;
 
 // getters and setters
}

Step 7: Test the Implementation

Verify that expression variables are properly resolved:

java
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

import static org.junit.jupiter.api.Assertions.assertEquals;

@SpringBootTest
public class CustomPasswordValidatorTest {

 @Autowired
 private Validator validator;

 @Test
 public void testCustomPasswordValidatorWithExpressionVariables() {
 UserRegistrationDto user = new UserRegistrationDto();
 user.setPassword("123"); // Invalid password
 
 Errors errors = new BeanPropertyBindingResult(user, "user");
 validator.validate(user, errors);
 
 // Check that expression variables are resolved
 String errorMessage = errors.getFieldError("password").getDefaultMessage();
 assertTrue(errorMessage.contains("Password must be between 8 and 20 characters"));
 assertTrue(errorMessage.contains("Current length: 3"));
 }
}

Debugging Expression Language Issues in Hibernate Validator

When expression variables aren’t resolving in Hibernate Validator 8 custom violations, follow these debugging techniques to identify and resolve the issue:

1. Verify Expression Language Feature Level Configuration

Check that both expression language feature levels are properly configured:

java
// In your validator factory configuration
ConstraintMapping constraintMapping = validatorFactory.getConstraintMapping();
constraintMapping.constraintExpressionLanguageFeatureLevel(ExpressionLanguageFeatureLevel.VARIABLES);
constraintMapping.customViolationExpressionLanguageFeatureLevel(ExpressionLanguageFeatureLevel.VARIABLES);

The most common mistake is configuring only the constraint expression language feature level while neglecting the custom violation expression language feature level.

2. Confirm Jakarta EL Implementation is Available

Ensure a Jakarta EL implementation is on your classpath. You can verify this by checking the classpath:

java
import javax.el.ExpressionFactory;
import jakarta.validation.Validation;
import jakarta.validation.ValidatorFactory;

public class ElDependencyCheck {
 public static void main(String[] args) {
 try {
 ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
 ExpressionFactory expressionFactory = factory.getMessageInterpolator().getExpressionFactory();
 System.out.println("EL Implementation: " + expressionFactory.getClass().getName());
 } catch (Exception e) {
 System.out.println("EL implementation not found: " + e.getMessage());
 }
 }
}

3. Validate Constraint Validator Context Type

Ensure you’re using the correct context type for adding expression variables:

java
// Correct: Hibernate-specific context
HibernateConstraintValidatorContext hibernateContext = 
 context.unwrap(HibernateConstraintValidatorContext.class);

hibernateContext.addExpressionVariable("varName", varValue);

// Incorrect: Standard context won't have addExpressionVariable method
// context.addExpressionVariable("varName", varValue); // This won't compile

4. Check Message Interpolator Configuration

If you’re using a custom message interpolator, ensure it’s properly initialized with an expression factory:

java
import javax.el.ExpressionFactory;
import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator;

public class CustomMessageInterpolator extends ResourceBundleMessageInterpolator {
 public CustomMessageInterpolator() {
 super(new ExpressionFactory());
 }
}

5. Enable Debug Logging

Add Hibernate Validator debug logging to your application:

properties
# application.properties
logging.level.org.hibernate.validator=DEBUG
logging.level.org.hibernate.validator.internal.engine=TRACE
logging.level.org.hibernate.validator.internal.engine.messageinterpolation=TRACE

This will show you the expression evaluation process and help identify where resolution is failing.

6. Test with Simple Expressions

Start with simple expressions to verify basic functionality:

java
// Simple test expression
hibernateContext.addExpressionVariable("testValue", "Hello World");
context.buildConstraintViolationWithTemplate("Value: #{testValue}")
 .addConstraintViolation();

If this works but more complex expressions don’t, the issue might be with your expression syntax or variable types.

7. Verify Auto-Configuration Order

Ensure your custom validation configuration loads before Spring Boot’s validation auto-configuration:

java
@AutoConfiguration(before = ValidationAutoConfiguration.class)
public class CustomValidatorAutoConfiguration {
 // ...
}

And verify that it’s properly registered in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports.

8. Check for Classpath Conflicts

Look for duplicate validation-related dependencies that might be causing conflicts. Use your build tool’s dependency tree command to identify and resolve conflicts.

For Maven:

bash
mvn dependency:tree

For Gradle:

bash
gradle dependencies

By systematically following these debugging steps, you should be able to identify and resolve the issues preventing expression variables from being properly resolved in your Hibernate Validator 8 custom constraint violations.

Best Practices for Using Expression Variables in Validation

When working with expression variables in Hibernate Validator custom constraint violations, following these best practices will help you avoid common issues and create more maintainable validation code:

1. Keep Expressions Simple and Readable

Complex expressions can be difficult to debug and maintain. Break them down into simpler parts:

java
// Instead of complex nested expressions
hibernateContext.addExpressionVariable("complexResult", 
 someValue > threshold ? value1 : value2);

// Use multiple simple variables
hibernateContext.addExpressionVariable("isOverThreshold", someValue > threshold);
hibernateContext.addExpressionVariable("resultValue", isOverThreshold ? value1 : value2);

2. Use Descriptive Variable Names

Choose meaningful variable names that clearly indicate their purpose:

java
// Good
hibernateContext.addExpressionVariable("minPasswordLength", 8);
hibernateContext.addExpressionVariable("maxPasswordLength", 20);
hibernateContext.addExpressionVariable("passwordPolicyMessage", "Use 8-20 characters");

// Avoid
hibernateContext.addExpressionVariable("min", 8);
hibernateContext.addExpressionVariable("max", 20);
hibernateContext.addExpressionVariable("msg", "Use 8-20 characters");

3. Centralize Expression Variable Definitions

Create a utility class to centralize commonly used expression variables:

java
public class ValidationExpressionVariables {
 public static void addCommonVariables(HibernateConstraintValidatorContext context) {
 context.addExpressionVariable("minLength", 8);
 context.addExpressionVariable("maxLength", 20);
 context.addExpressionVariable("emailPattern", "^[A-Za-z0-9+_.-]+@(.+)$");
 }
 
 public static void addPasswordVariables(HibernateConstraintValidatorContext context) {
 context.addExpressionVariable("minPasswordLength", 8);
 context.addExpressionVariable("maxPasswordLength", 20);
 context.addExpressionVariable("passwordPolicy", "Must contain uppercase, lowercase, number, and special character");
 }
}

4. Handle Null Values in Expressions

Be defensive when using variables in expressions that might be null:

java
// Instead of directly accessing potentially null properties
context.buildConstraintViolationWithTemplate("Parent name: #{parent.name}")
 .addConstraintViolation();

// Use safe navigation operator if supported (EL 3.0+)
context.buildConstraintViolationWithTemplate("Parent name: #{parent?.name}")
 .addConstraintViolation();

5. Create Reusable Constraint Validators

Instead of duplicating validation logic, create reusable constraint validators that can be applied to different fields:

java
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = LengthRangeValidator.class)
public @interface LengthRange {
 int min() default 0;
 int max() default Integer.MAX_VALUE;
 String message() default "Length must be between #{min} and #{max}";
 Class<?>[] groups() default {};
 Class<? extends Payload>[] payload() default {};
}

public class LengthRangeValidator implements ConstraintValidator<LengthRange, String> {
 private int min;
 private int max;
 
 @Override
 public void initialize(LengthRange constraintAnnotation) {
 this.min = constraintAnnotation.min();
 this.max = constraintAnnotation.max();
 }
 
 @Override
 public boolean isValid(String value, ConstraintValidatorContext context) {
 if (value == null) return true;
 
 HibernateConstraintValidatorContext hibernateContext = 
 context.unwrap(HibernateConstraintValidatorContext.class);
 
 hibernateContext.addExpressionVariable("min", min);
 hibernateContext.addExpressionVariable("max", max);
 hibernateContext.addExpressionVariable("actual", value.length());
 
 return value.length() >= min && value.length() <= max;
 }
}

6. Use Externalized Validation Messages

For complex validation scenarios, externalize your validation messages to resource bundles:

properties
# ValidationMessages.properties
password.length=Password must be between #{min} and #{max} characters
password.pattern=Password must match pattern: #{pattern}

Then reference them in your constraint:

java
@LengthRange(min = 8, max = 20)
@Pattern(regexp = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\\S+$).{8,20}$")
private String password;

7. Test Expression Variables Thoroughly

Create comprehensive tests for your custom validators that verify expression variable resolution:

java
@Test
void testLengthRangeValidatorWithExpressionVariables() {
 User user = new User();
 user.setPassword("123"); // Too short
 
 Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
 Set<ConstraintViolation<User>> violations = validator.validate(user);
 
 ConstraintViolation<User> violation = violations.iterator().next();
 String message = violation.getMessage();
 
 assertTrue(message.contains("Password must be between 8 and 20 characters"));
 assertTrue(message.contains("actual: 3"));
}

8. Document Expression Variables

When working with a team, document the expression variables used in your validation constraints:

java
/**
 * Custom validator for password strength requirements.
 * 
 * Expression variables:
 * - minLength: Minimum password length (8)
 * - maxLength: Maximum password length (20)
 * - requiredChars: Required character types (uppercase, lowercase, number, special)
 */
public class PasswordStrengthValidator implements ConstraintValidator<StrongPassword, String> {
 // ...
}

9. Consider Performance Implications

Be aware that expression evaluation adds overhead to validation. For performance-critical applications, consider:

  • Pre-compiling complex expressions
  • Caching frequently used validation results
  • Using simpler validation logic for high-frequency validations

10. Follow Spring Boot 3 Migration Guidelines

When migrating to Spring Boot 3, ensure all validation-related configurations are updated:

  • Use META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports instead of spring.factories
  • Replace javax.* imports with jakarta.*
  • Update dependencies to use Jakarta EE 9+ versions

By following these best practices, you’ll create more robust, maintainable, and effective expression language usage in your Hibernate Validator custom constraint violations.


Sources

  1. Hibernate Validator Documentation — Expression Language in Hibernate Validator and configuration requirements: https://docs.jboss.org/hibernate/validator/8.0/reference/en-US/html_single/#section-expression-language

  2. Spring Boot Documentation — Validation auto-configuration and Spring Boot 3 migration: https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#validation

  3. Stack Overflow Answer by Uday Chauhan — Detailed solution for Hibernate Validator 8 expression variables not resolving: https://stackoverflow.com/questions/79895837/hibernate-validator-8-addexpressionvariable-with-expmessage-not-being


Conclusion

Expression variables in Hibernate Validator 8 custom constraint violations are a powerful feature that allows for dynamic, context-aware validation messages in Spring Boot 3 applications. The key to resolving the issue where addExpressionVariable('expMessage', value) returns the literal #{expMessage} lies in properly configuring both the constraint expression language feature level and the custom violation expression language feature level to ExpressionLanguageFeatureLevel.VARIABLES.

For Spring Boot 3 applications, the solution involves creating a custom auto-configuration that loads before the default validation auto-configuration, ensuring proper Jakarta EL implementation is on the classpath, and correctly implementing the HibernateConstraintValidatorContext in custom validators. By following the step-by-step guide and best practices outlined above, you can effectively leverage expression language features in your custom constraint violations, creating more informative and user-friendly validation messages in your Spring Boot 3 applications.

U

The issue with Hibernate Validator 8’s addExpressionVariable() not resolving #{expMessage} is due to two separate EL feature level settings that must both be enabled. First, Hibernate Validator has constraintExpressionLanguageFeatureLevel for constraint annotations and customViolationExpressionLanguageFeatureLevel for programmatically built violations. The latter is specifically needed for addExpressionVariable() to work. Second, your Spring Boot configuration must load before ValidationAutoConfiguration using @AutoConfiguration(before = ValidationAutoConfiguration.class). For Spring Boot 3.x, register your configuration in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. Additionally, ensure Jakarta EL implementation like org.glassfish.expressly:expressly is on your classpath, and if using a custom MessageInterpolator, initialize it with an explicit ExpressionFactory.

Hibernate Validator’s expression language support allows for dynamic message resolution in constraint validation. The framework provides two distinct contexts for expression evaluation: one for constraint annotations defined in validation constraints, and another for custom violations created programmatically via the HibernateConstraintValidatorContext. To enable expression variable resolution in custom violations, you need to configure both constraintExpressionLanguageFeatureLevel and customViolationExpressionLanguageFeatureLevel to ExpressionLanguageFeatureLevel.VARIABLES. This ensures that variables added through addExpressionVariable() are properly evaluated during message interpolation.

Spring Boot provides auto-configuration for validation through ValidationAutoConfiguration. This configuration creates a LocalValidatorFactoryBean that integrates with Hibernate Validator. When implementing custom validation configurations, it’s essential to ensure your configuration loads before Spring Boot’s auto-configuration using @AutoConfiguration(before = ValidationAutoConfiguration.class). For Spring Boot 3.x, custom auto-configuration classes should be registered in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports rather than the META-INF/spring.factories file used in earlier versions.

Expression Language in Hibernate Validator supports variable substitution through the #{variableName} syntax. When working with custom constraint violations, variables must be added to the validation context using addExpressionVariable() method on the HibernateConstraintValidatorContext. These variables are then available for use in violation templates. The evaluation of these expressions requires proper configuration of the validator factory with appropriate expression language feature levels, and the presence of a Jakarta EL implementation on the classpath.

Authors
U
Developer
Sources
Stack Overflow / Q&A Platform
Q&A Platform
Documentation Portal
Documentation Portal
Verified by moderation
NeuroAnswers
Moderation
Resolve Expression Variables in Hibernate Validator 8 Custom Violations