This affects 6.1.x and later. Here's a minimal Boot app that should illustrate the problem:
package com.example.gh_37101;
import java.util.ArrayList;
import java.util.List;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.validation.annotation.Validated;
import org.springframework.validation.beanvalidation.BeanValidationPostProcessor;
import com.example.gh_37101.Gh37101Application.Example.Exclude;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Pattern;
@SpringBootConfiguration
public class Gh37101Application {
@Bean
static BeanValidationPostProcessor beanValidationPostProcess() {
return new BeanValidationPostProcessor();
}
@Bean
Example example() {
Exclude exclude = new Exclude();
exclude.setHttpStatus(List.of("invalid"));
Example example = new Example();
example.setExclude(List.of(exclude));
return example;
}
public static void main(String[] args) {
SpringApplication.run(Gh37101Application.class, args);
}
@Validated
class Example {
@Valid
private List<Exclude> exclude = new ArrayList<>();
public List<Exclude> getExclude() {
return exclude;
}
public void setExclude(List<Exclude> exclude) {
this.exclude = exclude;
}
public static class Exclude {
private List<@Pattern(regexp="^([1-5][x|X]{2}|[1-5][0-9]{2})\\$") String> httpStatus;
public List<String> getHttpStatus() {
return httpStatus;
}
public void setHttpStatus(List<String> httpStatus) {
this.httpStatus = httpStatus;
}
}
}
}
Running it on the JVM will result in a start up failure as the validation fails:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.2.11)
2024-11-04T14:26:29.538Z INFO 59233 --- [ main] c.example.gh_37101.Gh37101Application : Starting Gh37101Application using Java 17.0.12 with PID 59233 (/Users/awilkinson/dev/temp/gh-37101/build/classes/java/main started by awilkinson in /Users/awilkinson/dev/temp/gh-37101)
2024-11-04T14:26:29.540Z INFO 59233 --- [ main] c.example.gh_37101.Gh37101Application : No active profile set, falling back to 1 default profile: "default"
2024-11-04T14:26:29.744Z WARN 59233 --- [ main] s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'example' defined in com.example.gh_37101.Gh37101Application: Bean state is invalid: exclude[0].httpStatus[0].<list element> - must match "^([1-5][x|X]{2}|[1-5][0-9]{2})\$"
2024-11-04T14:26:29.762Z ERROR 59233 --- [ main] o.s.boot.SpringApplication : Application run failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'example' defined in com.example.gh_37101.Gh37101Application: Bean state is invalid: exclude[0].httpStatus[0].<list element> - must match "^([1-5][x|X]{2}|[1-5][0-9]{2})\$"
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:607) ~[spring-beans-6.1.14.jar:6.1.14]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) ~[spring-beans-6.1.14.jar:6.1.14]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337) ~[spring-beans-6.1.14.jar:6.1.14]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.1.14.jar:6.1.14]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335) ~[spring-beans-6.1.14.jar:6.1.14]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.1.14.jar:6.1.14]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:975) ~[spring-beans-6.1.14.jar:6.1.14]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:971) ~[spring-context-6.1.14.jar:6.1.14]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:625) ~[spring-context-6.1.14.jar:6.1.14]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-3.2.11.jar:3.2.11]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) ~[spring-boot-3.2.11.jar:3.2.11]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:335) ~[spring-boot-3.2.11.jar:3.2.11]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1363) ~[spring-boot-3.2.11.jar:3.2.11]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1352) ~[spring-boot-3.2.11.jar:3.2.11]
at com.example.gh_37101.Gh37101Application.main(Gh37101Application.java:35) ~[main/:na]
Caused by: org.springframework.beans.factory.BeanInitializationException: Bean state is invalid: exclude[0].httpStatus[0].<list element> - must match "^([1-5][x|X]{2}|[1-5][0-9]{2})\$"
at org.springframework.validation.beanvalidation.BeanValidationPostProcessor.doValidate(BeanValidationPostProcessor.java:127) ~[spring-context-6.1.14.jar:6.1.14]
at org.springframework.validation.beanvalidation.BeanValidationPostProcessor.postProcessBeforeInitialization(BeanValidationPostProcessor.java:91) ~[spring-context-6.1.14.jar:6.1.14]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:422) ~[spring-beans-6.1.14.jar:6.1.14]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1798) ~[spring-beans-6.1.14.jar:6.1.14]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) ~[spring-beans-6.1.14.jar:6.1.14]
... 14 common frames omitted
Running as a native image will succeed:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.2.11)
2024-11-04T14:28:53.031Z INFO 59751 --- [ main] c.example.gh_37101.Gh37101Application : Starting AOT-processed Gh37101Application using Java 22.0.1 with PID 59751 (/Users/awilkinson/dev/temp/gh-37101/build/native/nativeCompile/gh-37101 started by awilkinson in /Users/awilkinson/dev/temp/gh-37101)
2024-11-04T14:28:53.032Z INFO 59751 --- [ main] c.example.gh_37101.Gh37101Application : No active profile set, falling back to 1 default profile: "default"
2024-11-04T14:28:53.038Z INFO 59751 --- [ main] c.example.gh_37101.Gh37101Application : Started Gh37101Application in 0.023 seconds (process running for 0.035)
Running as a native image succeeds because AOT processing does not generate any metadata for the @Pattern
container element constraint. It's missed both because the cascade on exclude
isn't considered and because container element constraints are not considered.
Comment From: wilkinsona
Thanks for this, @sdeleuze. Given that you consider this to be an enhancement, am I right to assume that it won't be backported to 6.1.x?
Comment From: sdeleuze
Yeah, the change is more involved than expected, so I chose to make it 6.2+ only after discussing with the team.