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.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Mastering Spring Boot Custom Parameter Resolvers: A Step‑by‑Step Guide

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaSpring BootCustom Argument ResolverHTTP message converter
Spring Full-Stack Practical Cases
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.