Skip to content

ConstraintDeclarationException in GrpcClientConstructorInjectionBeanFactoryPostProcessor with Hibernate Validator 8 and Spring AOT #1182

@martinsjavacode

Description

@martinsjavacode

The context

I'm using grpc-spring-boot-starter together with Spring Boot 3.4.0 and trying to build the project with Spring AOT enabled (e.g., for native image support or build-time optimizations). My goal is to make the application AOT-compliant with Hibernate Validator 8, which is used by default in Spring Framework 6+.

The bug

When building the application with AOT enabled, I get a ConstraintDeclarationException caused by the class GrpcClientConstructorInjectionBeanFactoryPostProcessor redefining parameter constraints on an overridden method (postProcessBeanFactory(...)). According to the Bean Validation specification and Hibernate Validator 8, this is not allowed.

What I expect to happen:
The library should not define parameter-level constraints (@NotNull, etc.) on methods that override interface methods without those same constraints.

Stacktrace and logs

jakarta.validation.ConstraintDeclarationException: HV000151: A method overriding another method must not redefine the parameter constraint configuration, but method GrpcClientConstructorInjectionBeanFactoryPostProcessor#postProcessBeanFactory(ConfigurableListableBeanFactory) redefines the configuration of BeanFactoryPostProcessor#postProcessBeanFactory(ConfigurableListableBeanFactory).
at org.hibernate.validator.internal.metadata.aggregated.rule.OverridingMethodMustNotAlterParameterConstraints.apply(OverridingMethodMustNotAlterParameterConstraints.java:24) ~[hibernate-validator-8.0.1.Final.jar:8.0.1.Final]
at org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData$Builder.assertCorrectnessOfConfiguration(ExecutableMetaData.java:462) ~[hibernate-validator-8.0.1.Final.jar:8.0.1.Final]
at org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData$Builder.build(ExecutableMetaData.java:380) ~[hibernate-validator-8.0.1.Final.jar:8.0.1.Final]
at org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataBuilder$BuilderDelegate.build(BeanMetaDataBuilder.java:260) ~[hibernate-validator-8.0.1.Final.jar:8.0.1.Final]
at org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataBuilder.build(BeanMetaDataBuilder.java:133) ~[hibernate-validator-8.0.1.Final.jar:8.0.1.Final]
at org.hibernate.validator.internal.metadata.BeanMetaDataManagerImpl.createBeanMetaData(BeanMetaDataManagerImpl.java:206) ~[hibernate-validator-8.0.1.Final.jar:8.0.1.Final]
at org.hibernate.validator.internal.metadata.BeanMetaDataManagerImpl.getBeanMetaData(BeanMetaDataManagerImpl.java:165) ~[hibernate-validator-8.0.1.Final.jar:8.0.1.Final]
at org.hibernate.validator.internal.engine.ValidatorImpl.getConstraintsForClass(ValidatorImpl.java:316) ~[hibernate-validator-8.0.1.Final.jar:8.0.1.Final]
at org.springframework.validation.beanvalidation.BeanValidationBeanRegistrationAotProcessor$BeanValidationDelegate.processAheadOfTime(BeanValidationBeanRegistrationAotProcessor.java:125) ~[spring-context-6.2.0.jar:6.2.0]
at org.springframework.validation.beanvalidation.BeanValidationBeanRegistrationAotProcessor$BeanValidationDelegate.processAheadOfTime(BeanValidationBeanRegistrationAotProcessor.java:110) ~[spring-context-6.2.0.jar:6.2.0]
at org.springframework.validation.beanvalidation.BeanValidationBeanRegistrationAotProcessor.processAheadOfTime(BeanValidationBeanRegistrationAotProcessor.java:75) ~[spring-context-6.2.0.jar:6.2.0]
at org.springframework.beans.factory.aot.BeanDefinitionMethodGeneratorFactory.getAotContributions(BeanDefinitionMethodGeneratorFactory.java:155) ~[spring-beans-6.2.0.jar:6.2.0]
at org.springframework.beans.factory.aot.BeanDefinitionMethodGeneratorFactory.getBeanDefinitionMethodGenerator(BeanDefinitionMethodGeneratorFactory.java:99) ~[spring-beans-6.2.0.jar:6.2.0]
at org.springframework.beans.factory.aot.BeanDefinitionMethodGeneratorFactory.getBeanDefinitionMethodGenerator(BeanDefinitionMethodGeneratorFactory.java:115) ~[spring-beans-6.2.0.jar:6.2.0]
at org.springframework.beans.factory.aot.BeanRegistrationsAotProcessor.processAheadOfTime(BeanRegistrationsAotProcessor.java:49) ~[spring-beans-6.2.0.jar:6.2.0]
at org.springframework.beans.factory.aot.BeanRegistrationsAotProcessor.processAheadOfTime(BeanRegistrationsAotProcessor.java:37) ~[spring-beans-6.2.0.jar:6.2.0]
at org.springframework.context.aot.BeanFactoryInitializationAotContributions.processAheadOfTime(BeanFactoryInitializationAotContributions.java:82) ~[spring-context-6.2.0.jar:6.2.0]
at org.springframework.context.aot.BeanFactoryInitializationAotContributions.getContributions(BeanFactoryInitializationAotContributions.java:69) ~[spring-context-6.2.0.jar:6.2.0]
at org.springframework.context.aot.BeanFactoryInitializationAotContributions.(BeanFactoryInitializationAotContributions.java:52) ~[spring-context-6.2.0.jar:6.2.0]
at org.springframework.context.aot.BeanFactoryInitializationAotContributions.(BeanFactoryInitializationAotContributions.java:47) ~[spring-context-6.2.0.jar:6.2.0]
at org.springframework.context.aot.ApplicationContextAotGenerator.lambda$processAheadOfTime$0(ApplicationContextAotGenerator.java:58) ~[spring-context-6.2.0.jar:6.2.0]
at org.springframework.context.aot.ApplicationContextAotGenerator.withCglibClassHandler(ApplicationContextAotGenerator.java:67) ~[spring-context-6.2.0.jar:6.2.0]
at org.springframework.context.aot.ApplicationContextAotGenerator.processAheadOfTime(ApplicationContextAotGenerator.java:53) ~[spring-context-6.2.0.jar:6.2.0]
at org.springframework.context.aot.ContextAotProcessor.performAotProcessing(ContextAotProcessor.java:106) ~[spring-context-6.2.0.jar:6.2.0]
at org.springframework.context.aot.ContextAotProcessor.doProcess(ContextAotProcessor.java:84) ~[spring-context-6.2.0.jar:6.2.0]
at org.springframework.context.aot.ContextAotProcessor.doProcess(ContextAotProcessor.java:49) ~[spring-context-6.2.0.jar:6.2.0]
at org.springframework.context.aot.AbstractAotProcessor.process(AbstractAotProcessor.java:82) ~[spring-context-6.2.0.jar:6.2.0]
at org.springframework.boot.SpringApplicationAotProcessor.main(SpringApplicationAotProcessor.java:80) ~[spring-boot-3.4.0.jar:3.4.0]

Steps to Reproduce

  1. Create a Spring Boot 3.4.0 project with grpc-spring-boot-starter (v3.1.0.RELEASE)
  2. Enable AOT processing (e.g., using spring.aot.enabled=true or building native image)
  3. Run the AOT processing or native image build
  4. Observe the validation exception during build time

The application's environment

  • Spring Boot: 3.4.0
  • Spring Framework: 6.2.0
  • grpc-java: 1.63.0 (default used by grpc-spring-boot-starter)
  • grpc-spring-boot-starter: 3.1.0.RELEASE
  • Hibernate Validator: 8.0.1.Final
  • Java: 21 (64-bit)

Additional context

  • Did it ever work before?
    Yes, this did not occur with older Spring Boot versions (e.g., 2.x) or without AOT. It started happening with Hibernate Validator 8 + Spring Boot 3.2+ using AOT mode.

  • Do you have a demo?
    Not currently, but it's easily reproducible by enabling AOT in any Spring Boot 3.4.0 project using grpc-spring-boot-starter.

Suggested fix

The issue could be fixed by removing parameter constraint annotations (such as @NotNull) from the postProcessBeanFactory(ConfigurableListableBeanFactory) method in GrpcClientConstructorInjectionBeanFactoryPostProcessor, since it overrides BeanFactoryPostProcessor#postProcessBeanFactory(...) which is not annotated.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething does not work as expected

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions