-
Notifications
You must be signed in to change notification settings - Fork 866
Description
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
- Create a Spring Boot 3.4.0 project with
grpc-spring-boot-starter(v3.1.0.RELEASE) - Enable AOT processing (e.g., using
spring.aot.enabled=trueor building native image) - Run the AOT processing or native image build
- 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 usinggrpc-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