code https://github.com/xuyixun/demo-graalvm-java spring boot: 3.2.0 gradle: 8.5
public class TestDto{
private String name;
//get and set
}
@RequestMapping("/demo")
public String demo(TestDto dto){
System.out.println(dto.getName());
return "success";
}
Hi Spring Team,I run Demo with gradle bootRun
is fine. Then build Demo with gradle bootBuildImage
is fine. But when run docker image and request 127.0.0.1:8080/demo or http://127.0.0.1:8080/demo?name=test with error causes
java.lang.IllegalStateException: No primary or single unique constructor found for class com.example.demo.dto.TestDto
at org.springframework.beans.BeanUtils.getResolvableConstructor(BeanUtils.java:267) ~[na:na]
at org.springframework.validation.DataBinder.createObject(DataBinder.java:924) ~[com.example.demo.DemoApplication:6.1.1]
at org.springframework.validation.DataBinder.construct(DataBinder.java:903) ~[com.example.demo.DemoApplication:6.1.1]
at org.springframework.web.bind.ServletRequestDataBinder.construct(ServletRequestDataBinder.java:112) ~[com.example.demo.DemoApplication:6.1.1]
at org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor.constructAttribute(ServletModelAttributeMethodProcessor.java:156) ~[na:na]
at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:148) ~[com.example.demo.DemoApplication:6.1.1]
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:122) ~[na:na]
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:218) ~[com.example.demo.DemoApplication:6.1.1]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:171) ~[com.example.demo.DemoApplication:6.1.1]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) ~[com.example.demo.DemoApplication:6.1.1]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:917) ~[com.example.demo.DemoApplication:6.1.1]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:829) ~[com.example.demo.DemoApplication:6.1.1]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[com.example.demo.DemoApplication:6.1.1]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) ~[com.example.demo.DemoApplication:6.1.1]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) ~[com.example.demo.DemoApplication:6.1.1]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) ~[com.example.demo.DemoApplication:6.1.1]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) ~[com.example.demo.DemoApplication:6.1.1]
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) ~[com.example.demo.DemoApplication:6.0]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) ~[com.example.demo.DemoApplication:6.1.1]
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) ~[com.example.demo.DemoApplication:6.0]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205) ~[na:na]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[na:na]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) ~[com.example.demo.DemoApplication:10.1.16]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[na:na]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[na:na]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[com.example.demo.DemoApplication:6.1.1]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[com.example.demo.DemoApplication:6.1.1]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[na:na]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[na:na]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) ~[na:na]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) ~[na:na]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482) ~[com.example.demo.DemoApplication:10.1.16]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115) ~[na:na]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) ~[com.example.demo.DemoApplication:10.1.16]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[na:na]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:340) ~[na:na]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391) ~[na:na]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) ~[com.example.demo.DemoApplication:10.1.16]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:896) ~[na:na]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1744) ~[na:na]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) ~[com.example.demo.DemoApplication:10.1.16]
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[na:na]
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[na:na]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[na:na]
at java.base@21.0.1/java.lang.Thread.runWith(Thread.java:1596) ~[com.example.demo.DemoApplication:na]
at java.base@21.0.1/java.lang.Thread.run(Thread.java:1583) ~[com.example.demo.DemoApplication:na]
at org.graalvm.nativeimage.builder/com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:837) ~[com.example.demo.DemoApplication:na]
at org.graalvm.nativeimage.builder/com.oracle.svm.core.posix.thread.PosixPlatformThreads.pthreadStartRoutine(PosixPlatformThreads.java:211) ~[na:na]
Comment From: quaff
You can reproduce it by gradle nativeRun
, nothing to do with docker.
Comment From: mhalbritter
There's a @RequestBody
annotation missing on your controller method:
@RequestMapping("/demo")
public String demo(@RequestBody TestDto dto){
// ...
Comment From: quaff
There's a
@RequestBody
annotation missing on your controller method:
java @RequestMapping("/demo") public String demo(@RequestBody TestDto dto){ // ...
It's intentional, then parameters from request will be used instead of request body.
Comment From: xuyixun
@mhalbritter I want use get request and url parameters like http://127.0.0.1:8080/demo?name=test not post request. This code run in jvm is fine, But in graalvm is fail
Comment From: quaff
If you change return type from String
to TestDto
like this:
public TestDto demo(TestDto dto){
return dto;
}
It works both with bootRun
and nativeRun
.
Comment From: mhalbritter
Sorry, my bad. Then this looks like a bug to me.
Comment From: wilkinsona
I suspect it's a bug/limitation of Framework's ControllerMappingReflectiveProcessor
.
Comment From: mhalbritter
I agree, this belongs to Spring Framework.
Workaround: Annotate your main class with @RegisterReflectionForBinding(TestDto.class)
.
Comment From: sdeleuze
This is a known and almost "by design" limitation of ControllerMappingReflectiveProcessor
(see #28623 related implementation) since the programming model induced by the catch-all instantiation of ServletModelAttributeMethodProcessor
and ModelAttributeMethodArgumentResolver
with useDefaultResolution
set to true
is not something, as far as I can tell, that we can support in a maintainable way. And our past tries to add reflection binding hints to all web handler method parameters has always been a failure due to a very significant increase of the footprint due to much methods reachable.
As a consequence, I suggest that we recommend and document for GraalVM use case explicit annotation with @ModelAttribute
in order to allow automatic reflection hint generation.
As discussed with @mhalbritter, I suggest we add in the web section of Spring Boot GraalVM wiki:
Optional usage of
@ModelAttribute
as supported by Spring MVC and Spring WebFlux does not allow ahead-of-time inference of related data binding reflection hints. As a consequence, it is recommended to annotate explicitly with@ModelAttribute
method parameters where binding is required when compiling to GraalVM native images.
I will also update those Spring Framework reference pages to document that limitation.
Comment From: mhalbritter
I've updated the wiki page on Spring Boot side.