Skip to content

Custom Constraint Validator not work with native image #29823

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
burl21 opened this issue Jan 15, 2023 · 4 comments
Closed

Custom Constraint Validator not work with native image #29823

burl21 opened this issue Jan 15, 2023 · 4 comments
Assignees
Labels
theme: aot An issue related to Ahead-of-time processing type: enhancement A general enhancement
Milestone

Comments

@burl21
Copy link

burl21 commented Jan 15, 2023

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

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Jan 15, 2023
@mhalbritter
Copy link
Contributor

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

@bclozel bclozel transferred this issue from spring-projects/spring-boot Jan 16, 2023
@sdeleuze sdeleuze self-assigned this Jan 16, 2023
@sdeleuze sdeleuze added theme: aot An issue related to Ahead-of-time processing type: enhancement A general enhancement and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Jan 16, 2023
@sdeleuze sdeleuze added this to the 6.0.5 milestone Jan 16, 2023
@sdeleuze
Copy link
Contributor

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.

@sdeleuze
Copy link
Contributor

sdeleuze commented Jan 16, 2023

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.

jhoeller added a commit that referenced this issue Jan 19, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
…ean(Class)

Closes gh-29855
See gh-29823
sdeleuze added a commit to sdeleuze/spring-framework that referenced this issue Jan 20, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
@sdeleuze
Copy link
Contributor

Draft commit I need to test and review a bit more next week before merging it : sdeleuze@gh-29823.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
theme: aot An issue related to Ahead-of-time processing type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

4 participants