Mastering Spring Boot 3 Parameter Conversion: 5 Practical Solutions
This article walks through five comprehensive Spring Boot 3 techniques for converting request parameters—including a custom Converter, PropertyEditor, custom @ConvertUser annotation, argument resolver, and JSON deserializer—showing step‑by‑step implementations, registration details, and runtime screenshots for each approach.
1. Introduction
In Spring Boot development, converting incoming request parameters (strings, JSON, path variables, form data) to strongly‑typed Java objects is a frequent requirement. Mismatches in format or type can cause errors, so Spring Boot offers multiple mechanisms to handle these conversions.
This article presents five universal solutions that cover most everyday scenarios, ranging from simple annotations to advanced argument resolvers.
2. Practical Cases
2.1 Custom Converter<S,T>
Implement the Converter<String, User> interface and register it as a Spring bean. The converter parses a comma‑separated string into a User object.
@Component
public class StringToUserConvert implements Converter<String, User> {
@Override
public User convert(String source) {
if (!StringUtils.hasLength(source)) {
return null;
}
String[] data = source.split(",");
if (data.length != 3) {
return null;
}
return new User(Long.valueOf(data[0]), data[1], Integer.valueOf(data[2]));
}
}When registered globally, this converter works for any controller method that receives a User parameter.
Run result:
If you need the converter only for a specific controller, register it inside @InitBinder:
@RestController
@RequestMapping("/api")
public class ApiController {
@InitBinder
public void init(WebDataBinder binder) {
ConversionService cs = binder.getConversionService();
if (cs instanceof ConfigurableConversionService gcs) {
gcs.addConverter(new StringToUserConvert());
}
}
// ... other endpoints
}2.2 Custom PropertyEditor
Extend PropertyEditorSupport to convert a string into a User object, then register it with WebDataBinder:
@InitBinder
public void init(WebDataBinder binder) {
binder.registerCustomEditor(User.class, new PropertyEditorSupport() {
@Override
public void setAsText(String source) throws IllegalArgumentException {
if (!StringUtils.hasLength(source)) {
setValue(null);
return;
}
String[] data = source.split(",");
if (data.length != 3) {
setValue(null);
return;
}
setValue(new User(Long.valueOf(data[0]), data[1], Integer.valueOf(data[2])));
}
});
}Run result:
Test endpoint:
@GetMapping("/dto")
public DTO convert2(DTO dto) {
return dto;
}
public class DTO {
private User user;
}Run result:
2.3 Custom Annotation @ConvertUser
Spring provides built‑in annotations like @NumberFormat and @DateTimeFormat that rely on AnnotationFormatterFactory. You can define a similar custom annotation to trigger conversion.
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
public @interface ConvertUser {}Formatter factory implementation:
public final class ConvertUserAnnotationFormatterFactory implements AnnotationFormatterFactory<ConvertUser> {
private static final Set<Class<?>> FIELD_TYPES = Set.of(User.class);
@Override
public Set<Class<?>> getFieldTypes() { return FIELD_TYPES; }
@Override
public Printer<User> getPrinter(ConvertUser annotation, Class<?> fieldType) {
return user -> "【id = %s, name = %s, age = %s】".formatted(user.getId(), user.getName(), user.getAge());
}
@Override
public Parser<User> getParser(ConvertUser annotation, Class<?> fieldType) {
return source -> {
if (!StringUtils.hasLength(source)) return null;
String[] data = source.split(",");
if (data.length != 3) return null;
return new User(Long.valueOf(data[0]), data[1], Integer.valueOf(data[2]));
};
}
}Register the formatter:
@Component
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addFormatterForFieldAnnotation(new ConvertUserAnnotationFormatterFactory());
}
}Test endpoint using the annotation:
@GetMapping("/anno")
public User convert2(@ConvertUser User user) {
return user;
}Run result:
2.4 Custom Argument Resolver
For the most flexible handling, implement HandlerMethodArgumentResolver that works with the @ConvertUser annotation.
public class ConvertUserArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(ConvertUser.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
String name = parameter.getParameterName();
String value = webRequest.getParameter(name);
if (!StringUtils.hasLength(value)) return null;
String[] data = value.split(",");
if (data.length != 3) return null;
return new User(Long.valueOf(data[0]), data[1], Integer.valueOf(data[2]));
}
}Register the resolver:
@Component
public class WebConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new ConvertUserArgumentResolver());
}
}Run result (screenshot):
2.5 JSON Property Conversion
When the request body is JSON, define a custom deserializer and attach it to the @ConvertUser annotation.
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
@JacksonAnnotationsInside
@JsonDeserialize(using = UserConverterDeserializer.class)
public @interface ConvertUser {} public class UserConverterDeserializer extends JsonDeserializer<User> {
@Override
public User deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String source = p.getText();
if (!StringUtils.hasLength(source)) return null;
String[] data = source.split(",");
if (data.length != 3) return null;
return new User(Long.valueOf(data[0]), data[1], Integer.valueOf(data[2]));
}
}DTO using the annotation:
public class DTO {
@ConvertUser
private User user;
}
@PostMapping("/body")
public DTO convert3(@RequestBody DTO dto) {
return dto;
}Run result:
3. Conclusion
The article demonstrates five interchangeable strategies for handling parameter conversion in Spring Boot 3.5.0, allowing developers to choose the most suitable approach—global bean converters, PropertyEditor, custom annotations with formatter factories, argument resolvers, or JSON deserializers—based on project complexity and performance considerations.
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.
