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.
<code>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);
}
}
}</code>The corresponding HandlerMethodArgumentResolver checks for a custom @Pack annotation and delegates the conversion to the message converter.
<code>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);
}
}</code>Configuration registers the custom converter (with media type application/fm ) and adds the resolver to Spring MVC.
<code>@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);
}
}</code>The controller uses the @Pack annotation on a Users parameter to trigger the custom resolver.
<code>@PostMapping("/resolver")
public Object resolver(@Pack Users user) {
System.out.println("自定义参数解析器处理结果:" + user);
return user;
}</code>A simple @Pack annotation is defined with runtime retention and parameter target.
<code>@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Pack { }</code>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.
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.