In a new project generated by Spring Initializr, I created a RestController with custom ConstraintValidator and it works fine in a JVM setup:

@Validated
@RestController
@RequestMapping("/")
public class HelloController {

    @GetMapping("hello")
    public String hello(@RequestParam @Exists String name) {
        return "Hello %s.".formatted(name);
    }

}
@Component
public class ExistsValidator implements ConstraintValidator<Exists, String> {
    private final DataService service;

    public ExistsValidator(DataService service) {
        this.service = service;
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
        return service.exists(value);
    }
}

However, for the native image it doesn't work with the error message:

ERROR 19797 --- [nio-8080-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.example.nativevalidation.ExistsValidator': Failed to instantiate [com.example.nativevalidation.ExistsValidator]: No default constructor found] with root cause

java.lang.NoSuchMethodException: com.example.nativevalidation.ExistsValidator.<init>()
...

In reflect-config.json you can see the configuration of ExistsValidator:

 {
    "name": "com.example.nativevalidation.ExistsValidator",
    "queriedMethods": [
      {
        "name": "<init>",
        "parameterTypes": [
          "com.example.nativevalidation.DataService"
        ]
      }
    ]
  }

I found no issues with the jakarta.validation.constraints.* annotations. Is it a limitation with native-image to be able to inject a bean into a ConstraintValidator ?

Spring Boot v3.0.1

Comment From: mhalbritter

The reflect-config.json entry only registers the constructor of the custom validator for querying, not for invocation, which is the problem here.

Comment From: sdeleuze

It looks like there are 2 issues for that use case: - At native level: missing hint for constructor invocation as pointed out by @mhalbritter - At AOT level: AutowireCapableBeanFactory#createBean(java.lang.Class<T>) invocation in SpringConstraintValidatorFactory seems not able to autowire contructor arguments with AOT mode (reproducable on the JVM). I may create a dedicated issue for this unrelated problem.

Comment From: sdeleuze

The detailed reason for the AOT level issue inSpringConstraintValidatorFactory is that ExistsValidator() constructor invocation which does not exist is attempted and fails because: - AutowireCapableBeanFactory#createBean(java.lang.Class<T>) does not perform autowiring by itself unlike AutowireCapableBeanFactory#createBean(Class, int, boolean) can do - Without AOT mode, AutowiredAnnotationBeanPostProcessor autowire the bean at runtime - With AOT mode, there is no AutowiredAnnotationBeanPostProcessor at runtime since such processing has been done AOT - @Component annotation on ExistsValidator is not really used since a new prototype bean is created via @Exists -> @Constraint(validatedBy = { ExistsValidator.class }) at least on my repro.

Comment From: sdeleuze

Draft commit I need to test and review a bit more next week before merging it : https://github.com/sdeleuze/spring-framework/commit/gh-29823.