Mastering Spring Boot Custom Parameter Resolvers: A Step‑by‑Step Guide
This guide explains how to create a Spring Boot custom HTTP message converter and argument resolver, register them, and use a @Pack annotation to parse a simple 'name:张三,age:20' request body into a Users object, enhancing backend flexibility.
Environment: Spring Boot 2.7.12
Overview
Spring's custom argument resolver allows developers to define their own parameter parsing logic, handling non‑standard types, complex structures, or specific formats. In Spring MVC it is a separate component that Spring iterates over for each controller method invocation.
Custom Resolver
The example processes a request body formatted as name:张三,age:20. A custom HttpMessageConverter parses the string, splits by commas and colons, and populates a Users object via reflection.
public class CustomHttpMessageConverter extends AbstractHttpMessageConverter<Object> {
private static Logger logger = LoggerFactory.getLogger(CustomHttpMessageConverter.class);
@Override
protected boolean supports(Class<?> clazz) {
return Users.class == clazz;
}
@Override
protected Object readInternal(Class<? extends Object> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
String content = inToString(inputMessage.getBody());
String[] keys = content.split(",");
Users instance = null;
try {
instance = (Users) clazz.newInstance();
} catch (Exception e1) {
e1.printStackTrace();
}
for (String key : keys) {
String[] vk = key.split(":");
try {
Field[] fields = clazz.getDeclaredFields();
for (Field f : fields) {
if (f.getName().equals(vk[0])) {
f.setAccessible(true);
Class<?> type = f.getType();
if (String.class == type) {
f.set(instance, vk[1]);
} else if (Integer.class == type) {
f.set(instance, Integer.parseInt(vk[1]));
}
break;
}
}
} catch (Exception e) {
logger.error("错误:{}", e);
}
}
return instance;
}
// writeInternal and other methods omitted for brevity
private String inToString(InputStream is) {
byte[] buf = new byte[10 * 1024];
int leng = -1;
StringBuilder sb = new StringBuilder();
try {
while ((leng = is.read(buf)) != -1) {
sb.append(new String(buf, 0, leng));
}
return sb.toString();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}The corresponding HandlerMethodArgumentResolver checks for a custom @Pack annotation and delegates the conversion to the message converter.
public class CustomHandlerMethodParameterResolver implements HandlerMethodArgumentResolver {
private CustomHttpMessageConverter messageConverter;
public CustomHttpMessageConverter getMessageConverter() { return messageConverter; }
public void setMessageConverter(CustomHttpMessageConverter messageConverter) { this.messageConverter = messageConverter; }
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(Pack.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(request);
return messageConverter.read((Class<?>)parameter.getNestedGenericParameterType(), inputMessage);
}
}Configuration registers the custom converter (with media type application/fm) and adds the resolver to Spring MVC.
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public CustomHttpMessageConverter customHttpMessageConverter() {
return new CustomHttpMessageConverter();
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
CustomHttpMessageConverter messageConvert = customHttpMessageConverter();
List<MediaType> supportedMediaTypes = new ArrayList<>();
supportedMediaTypes.add(new MediaType("application", "fm"));
messageConvert.setSupportedMediaTypes(supportedMediaTypes);
converters.add(messageConvert);
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
CustomHandlerMethodArgumentResolver customResolver = new CustomHandlerMethodArgumentResolver();
customResolver.setMessageConverter(customHttpMessageConverter());
resolvers.add(customResolver);
}
}The controller uses the @Pack annotation on a Users parameter to trigger the custom resolver.
@PostMapping("/resolver")
public Object resolver(@Pack Users user) {
System.out.println("自定义参数解析器处理结果:" + user);
return user;
}A simple @Pack annotation is defined with runtime retention and parameter target.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Pack { }Testing shows that the request body name:张三,age:20 is correctly mapped to a Users object, demonstrating the flexibility and maintainability of custom parameter resolvers in Spring applications.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
