How to Create and Register a Custom Spring MVC Return Value Handler
This article explains the Spring MVC return‑value handling mechanism, demonstrates how to implement a custom HandlerMethodReturnValueHandler, register it correctly in a WebMvcConfigurer, and troubleshoot why the default processor may override custom handlers.
1. Introduction
In Spring MVC a controller method can return many types such as String, View, ModelAndView, ResponseEntity, etc. When the return type is String, Spring treats it as a view name; other types are processed by specific handlers. The framework uses HandlerMethodReturnValueHandler implementations to convert the controller return value into an HTTP response.
2. Working Principle
For a typical JSON‑producing endpoint:
@GetMapping("/rv")
public Users us() {
return new Users();
}The request is routed to the target controller by RequestMappingHandlerAdapter, which creates a ServletInvocableHandlerMethod. This method invokes the controller, obtains the return value, and then delegates to the configured HandlerMethodReturnValueHandler chain.
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter {
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response,
HandlerMethod handlerMethod) throws Exception {
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
invocableMethod.invokeAndHandle(webRequest, mavContainer);
}
} public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
public void invokeAndHandle(ServletWebRequest webRequest,
ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
this.returnValueHandlers.handleReturnValue(returnValue,
getReturnValueType(returnValue),
mavContainer, webRequest);
}
} public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler {
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
private HandlerMethodReturnValueHandler selectHandler(Object value, MethodParameter returnType) {
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
}3. Custom Return‑Value Handler
public class CustomHandlerMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
@Override
public boolean supportsReturnType(MethodParameter returnType) {
// Match only when the return type is Users
return returnType.getParameterType() == Users.class;
}
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
HttpServletResponse response = (HttpServletResponse) webRequest.getNativeResponse();
PrintWriter out = response.getWriter();
out.println("Custom HandlerMethodReturnValueHandler");
out.flush();
out.close();
}
}Register the custom handler in a configuration class:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
handlers.add(new CustomHandlerMethodReturnValueHandler());
}
}4. Testing & Analysis
Initial tests showed that the custom handler was not invoked; the response still contained the default Users JSON. Debugging confirmed that the custom handler was registered, but the built‑in RequestResponseBodyMethodProcessor appears earlier in the handler chain and processes the return value first.
Source inspection of RequestMappingHandlerAdapter reveals that default handlers are added before custom ones, and the handler list does not implement Ordered, so custom handlers cannot be reordered automatically.
5. Making the Custom Handler Effective
To ensure the custom handler runs before the default processor, we must place it at the beginning of the handler list. One approach is to override the default list entirely or prepend the custom handler manually.
After adjusting the registration order, the custom handler is invoked and the expected output is produced.
6. Conclusion
This article detailed how Spring MVC processes controller return values, how to implement a custom HandlerMethodReturnValueHandler, and how to register it so that it takes precedence over the framework’s default processors, enabling more flexible response handling in Spring Boot 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.
