Skip to content
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

3.2.0-RC1: No primary or single unique constructor found for interface java.util.Set #31488

Closed
burl21 opened this issue Oct 24, 2023 · 3 comments
Assignees
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) type: regression A bug that is also a regression
Milestone

Comments

@burl21
Copy link

burl21 commented Oct 24, 2023

After upgrading to Spring Boot 3.2.0-RC1 from 3.1.5 request params binding is not working.

@RestController
@RequestMapping
static class HelloController {
    enum Tag {RED, GREEN}

    record HelloArgs(String name, Set<Tag> tags) {
        HelloArgs(@RequestParam String name, @RequestParam Set<Tag> tags) {
            this.name = name;
            this.tags = tags == null ? Set.of() : tags;
        }
    }

    @GetMapping
    String hello(HelloArgs args) {
        return "Hello %s %s".formatted(args.name(), args.tags.stream().map(Tag::name).collect(Collectors.joining()));
    }
}

With request:
http GET http://localhost:8080\?name\=Hulk

ERROR 54976 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.IllegalStateException: No primary or single unique constructor found for interface java.util.Set] with root cause

java.lang.IllegalStateException: No primary or single unique constructor found for interface java.util.Set
	at org.springframework.beans.BeanUtils.getResolvableConstructor(BeanUtils.java:266) ~[spring-beans-6.1.0-RC1.jar:6.1.0-RC1]
	at org.springframework.validation.DataBinder.createObject(DataBinder.java:923) ~[spring-context-6.1.0-RC1.jar:6.1.0-RC1]
	at org.springframework.validation.DataBinder.createObject(DataBinder.java:952) ~[spring-context-6.1.0-RC1.jar:6.1.0-RC1]
	at org.springframework.validation.DataBinder.construct(DataBinder.java:902) ~[spring-context-6.1.0-RC1.jar:6.1.0-RC1]
	at org.springframework.web.bind.ServletRequestDataBinder.construct(ServletRequestDataBinder.java:112) ~[spring-web-6.1.0-RC1.jar:6.1.0-RC1]
	at org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor.constructAttribute(ServletModelAttributeMethodProcessor.java:156) ~[spring-webmvc-6.1.0-RC1.jar:6.1.0-RC1]
	at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:148) ~[spring-web-6.1.0-RC1.jar:6.1.0-RC1]
	at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:122) ~[spring-web-6.1.0-RC1.jar:6.1.0-RC1]
	at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:217) ~[spring-web-6.1.0-RC1.jar:6.1.0-RC1]
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:170) ~[spring-web-6.1.0-RC1.jar:6.1.0-RC1]
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) ~[spring-webmvc-6.1.0-RC1.jar:6.1.0-RC1]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:918) ~[spring-webmvc-6.1.0-RC1.jar:6.1.0-RC1]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:830) ~[spring-webmvc-6.1.0-RC1.jar:6.1.0-RC1]
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-6.1.0-RC1.jar:6.1.0-RC1]
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) ~[spring-webmvc-6.1.0-RC1.jar:6.1.0-RC1]
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) ~[spring-webmvc-6.1.0-RC1.jar:6.1.0-RC1]
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1011) ~[spring-webmvc-6.1.0-RC1.jar:6.1.0-RC1]
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) ~[spring-webmvc-6.1.0-RC1.jar:6.1.0-RC1]
	at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) ~[tomcat-embed-core-10.1.15.jar:6.0]
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) ~[spring-webmvc-6.1.0-RC1.jar:6.1.0-RC1]
	at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) ~[tomcat-embed-core-10.1.15.jar:6.0]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) ~[tomcat-embed-websocket-10.1.15.jar:10.1.15]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-6.1.0-RC1.jar:6.1.0-RC1]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.0-RC1.jar:6.1.0-RC1]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-6.1.0-RC1.jar:6.1.0-RC1]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.0-RC1.jar:6.1.0-RC1]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-6.1.0-RC1.jar:6.1.0-RC1]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.0-RC1.jar:6.1.0-RC1]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:340) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:896) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1744) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-10.1.15.jar:10.1.15]
	at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]

While with: http GET http://localhost:8080\?name\=Hulk\&tags\=RED,GREEN everything is okay.

@burl21 burl21 changed the title No primary or single unique constructor found for interface java.util.Set Spring boot 3.2.0-RC1: No primary or single unique constructor found for interface java.util.Set Oct 24, 2023
@burl21 burl21 changed the title Spring boot 3.2.0-RC1: No primary or single unique constructor found for interface java.util.Set 3.2.0-RC1: No primary or single unique constructor found for interface java.util.Set Oct 24, 2023
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Oct 24, 2023
@snicoll snicoll transferred this issue from spring-projects/spring-boot Oct 24, 2023
@rstoyanchev
Copy link
Contributor

Thanks for giving the RC2 a try.

Both Spring Framework 6.0.x (Boot 3.1.x) and 6.1 (Boot 3.2) do not support constructor binding to a Collection. In the above example, 6.0.x passes null for the second parameter. 6.1 supports nested data constructors, and fails because it's trying to do more. We can make 6.1 ignore collection parameters, and pass null too, but I just confirm what your expectations are. Even in 6.0.x the tags parameter is not supported, it just happens to be more lenient by passing null.

As an aside, the @RequestParam annotations don't play any role at that level, unless you have some custom support for them. They're only checked when declared on controller method parameters.

@rstoyanchev rstoyanchev added the status: waiting-for-feedback We need additional information before we can continue label Oct 24, 2023
@burl21
Copy link
Author

burl21 commented Oct 24, 2023

Thanks for the tip @RequestParam.
If 6.1 is more strict, how should tags be declared?

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Oct 24, 2023
@rstoyanchev
Copy link
Contributor

It's not more strict intentionally. We can adjust the behavior to be equally lenient and pass null. I just wasn't sure what role tags plays in the first place, it's not very useful if it's always null.

@rstoyanchev rstoyanchev self-assigned this Oct 24, 2023
@rstoyanchev rstoyanchev added type: regression A bug that is also a regression and removed status: waiting-for-triage An issue we've not yet triaged or decided on status: feedback-provided Feedback has been provided labels Oct 24, 2023
@rstoyanchev rstoyanchev added this to the 6.1.0-RC2 milestone Oct 24, 2023
@rstoyanchev rstoyanchev added the in: web Issues in web modules (web, webmvc, webflux, websocket) label Oct 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) type: regression A bug that is also a regression
Projects
None yet
Development

No branches or pull requests

3 participants