springdoc 3.0 + boot 4.0 breaks native image support
Thx 4 providing Springdoc 3.0 so fast after boot 4.0 Unfortunately now native image support ist broken, this did not happen in the RC.
`Caused by: org.graalvm.nativeimage.MissingReflectionRegistrationError: Cannot reflectively read or write field 'public static java.lang.Object org.springdoc.core.providers.SpringWebProvider$$SpringCGLIB$$0.CGLIB$FACTORY_DATA'. To allow this operation, add the following to the 'reflection' section of 'reachability-metadata.json' and rebuild the native image:
{ "type": "org.springdoc.core.providers.SpringWebProvider$$SpringCGLIB$$0", "fields": [ { "name": "CGLIB$FACTORY_DATA" } ] }`
As a workound adding this RuntimeRegistrar seems to fix it for now
static class AppRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
hints.reflection().registerType(
TypeReference.of("org.springdoc.core.providers.SpringWebProvider$$SpringCGLIB$$0"),
builder -> builder.withField("CGLIB$FACTORY_DATA"));
hints.reflection().registerType(
TypeReference.of("org.springdoc.core.providers.SpringWebProvider$$SpringCGLIB$$0"),
builder -> builder.withField("CGLIB$CALLBACK_FILTER"));
hints.reflection().registerType(
TypeReference.of("org.springdoc.core.providers.SpringWebProvider"),
builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_METHODS));
}
}
I tried an example using Api-Versioning, and my native test still fail with:
.UnsatisfiedDependencyException: Error creating bean with name 'springDocProviders': Unsatisfied dependency expressed through method 'springDocProviders' parameter 5: Error creating bean with name 'springWebProvider': Instantiation of supplied bean failed
org.springframework.beans.factory.aot.BeanInstanceSupplier.resolveAutowiredArgument(BeanInstanceSupplier.java:341)
org.springframework.beans.factory.aot.BeanInstanceSupplier.resolveArguments(BeanInstanceSupplier.java:262)
org.springframework.beans.factory.aot.BeanInstanceSupplier.get(BeanInstanceSupplier.java:198)
[...]
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'springWebProvider': Instantiation of supplied bean failed
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1252)
[...]
Caused by: java.lang.NullPointerException
org.springdoc.webmvc.core.providers.SpringWebMvcProvider.lambda$new$0(SpringWebMvcProvider.java:84)
[email protected]/java.util.Optional.ifPresent(Optional.java:178)
org.springdoc.webmvc.core.providers.SpringWebMvcProvider.<init>(SpringWebMvcProvider.java:77)
Unfortunately I have trouble compiling with debug info atm, so I can't really see what wrong there.
I needed:
hints.reflection().registerType(
TypeReference.of(DefaultApiVersionStrategy.class),
builder -> builder.withField("versionResolvers"));
hints.reflection().registerType(
TypeReference.of(HeaderApiVersionResolver.class),
builder -> builder.withField("headerName"));
@eiswind,
Could you please create a PR that incorporates the hints you identified?
@bnasslahsen I will take another look at it tomorrow, as I think there will be other hints needed for the different strategies available.
I see that the proxy reflection metadata gets generated for example for OpenApiWebMvcResource by spring aot. So my guess for the moment would be that the hints from https://github.com/springdoc/springdoc-openapi/issues/3155#issuecomment-3564108135 should be created by spring aot.
The other hints of course need a PR.
In order to create a proper reproducer for the spring team, I'd like to understand the reason why there is a cglib proxy for SpringWebProvider at all. From looking at the definitions, I just don't see it. Can you help me with that?
I found that you used @Lazy in the SwaggerWelcome https://github.com/springdoc/springdoc-openapi/blob/c833910f9396fa04208a4ee707a7821827168f54/springdoc-openapi-starter-webmvc-ui/src/main/java/org/springdoc/webmvc/ui/SwaggerConfig.java#L86 definition to resolve a circular dependency which causes the proxy to get created.
According to https://github.com/spring-projects/spring-framework/issues/30985 this will not work properly in AOT mode.
Using an ObjectProvider here could be a solution, but would mean a larger code change. Another approach would mean using an ObjectProvider for the ApiVersionStrategy https://github.com/springdoc/springdoc-openapi/blob/c833910f9396fa04208a4ee707a7821827168f54/springdoc-openapi-starter-webmvc-api/src/main/java/org/springdoc/webmvc/core/providers/SpringWebMvcProvider.java#L76