Mastering @BindParam in Spring Boot 3: Seamlessly Map Path and Query Parameters
This article explains how Spring Boot 3's new @BindParam annotation lets developers map mismatched path and query parameters to constructor arguments, supports @ModelAttribute, and shows how to create custom name resolvers for advanced parameter binding scenarios.
1. Introduction
Spring provides powerful and flexible support for handling interface parameters in Spring MVC and Spring WebFlux, allowing binding via annotations such as @RequestParam , @PathVariable , @RequestHeader , @RequestBody , etc.
<code>@GetMapping("/{type}/{id}")
public Object bind(Param param) {
return param;
}
public static record Param(Long id, String type) {}
</code>This interface correctly binds the path variables to the Param object.
2. @BindParam Annotation
2.1 Path Parameters
<code>@GetMapping("/{cate}/{id}")
public Object bindData(Param param) {
return param;
}
public static record Param(Long id, @BindParam("cate") String type) {}
</code>Here @BindParam maps the path variable cate to the constructor argument type .
2.2 Query Parameters
<code>@GetMapping("/q")
public ResponseEntity<Object> q(Param param) {
return ResponseEntity.ok(param);
}
public static record Param(@BindParam("pid") Long id,
@BindParam("cate") String type) {}
</code>Access the endpoint with /q?pid=888&cate=xxxxooo to see the binding in action.
2.3 Support for @ModelAttribute
<code>@GetMapping("/{cate}/{id}")
public ResponseEntity<Object> q(@ModelAttribute Param param) {
return ResponseEntity.ok(param);
}
public static record Param(Long id, @BindParam("cate") String type) {}
</code>The @BindParam annotation also works on constructor parameters of a method argument annotated with @ModelAttribute .
2.4 Custom Name Resolver
Define a custom resolver to interpret your own annotation:
<code>public class PackParamNameResolver implements NameResolver {
public String resolveName(MethodParameter parameter) {
PackParam packParam = parameter.getParameterAnnotation(PackParam.class);
if (packParam != null && StringUtils.hasText(packParam.value())) {
return packParam.value();
}
return null;
}
}
</code>Register the resolver (locally in a controller or globally via @ControllerAdvice ):
<code>@InitBinder
public void init(WebDataBinder binder) {
binder.setNameResolver(new PackParamNameResolver());
}
</code>Usage example with a custom annotation:
<code>@GetMapping("/q")
public ResponseEntity<Object> q(Param param) {
return ResponseEntity.ok(param);
}
public static record Param(Long id,
@PackParam("cate") String type) {}
</code>The custom resolver can be extended to support SpEL expressions, e.g., @PackParam("#{${pack.param.name} + '-1'}") .
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.