spring--自定义ReturnValueHandler

本文介绍如何在 Spring MVC 中实现自定义返回值处理器,通过编写 ResultBeanReturnValueHandler 类来统一封装 Controller 的返回结果。文章还分析了处理流程及源码,帮助理解自定义处理器的工作机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

由于某些时候需要对controller的返回对象作统一的封装,例如一个业务系统中统一的返回格式。这里可以使用到ReturnValueHandler,当然也可以使用ResponseBody、Convertor或者View等。

  1. 编写类ResultBeanReturnValueHandler实现接口HandlerMethodReturnValueHandler;
  2. 将handler类注册到mvc中;


1.编写类ResultBeanReturnValueHandler实现接口HandlerMethodReturnValueHandler;

/**
 * 结果封装类
 */
public class ResultBeanReturnValueHandler implements HandlerMethodReturnValueHandler {

     /**
      * 类似ResponseBody,仅有添加ResultBeanResponseBody注解的method才会触发
      */
@Override
public boolean supportsReturnType(MethodParameter returnType) {
    return (AnnotationUtils.findAnnotation(returnType.getContainingClass(), ResultBeanResponseBody.class) != null ||
    returnType.getMethodAnnotation(ResultBeanResponseBody.class) != null);
}

/**
 * 使用统一的结果封装类ResultInfo,并序列化成json
 */
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
    ResultInfo resultInfo = new ResultInfo<>(ResultInfo.OK, "success", returnValue);
    HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
    response.addHeader("Content-Type", MediaType.APPLICATION_JSON_UTF8_VALUE);
    response.getWriter().append(JSON.toJSONString(resultInfo));
}

}

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public@interfaceResultBeanResponseBody{
}



2.将handler类注册到mvc中;
可以使用mvc标签;
<mvc:annotation-driven>
    <mvc:return-value-handlers>
        <bean class="com.netease.vcloud.statics.dqs.system.returnhandler.ResultBeanReturnValueHandler" />
    </mvc:return-value-handlers>
    <mvc:message-converters>
        <bean class="org.springframework.http.converter.StringHttpMessageConverter">
            <property name="supportedMediaTypes">
                <list>
                    <value>text/plain;charset=UTF-8</value>
                    <value>text/html;charset=UTF-8</value>
                </list>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>



也可以直接在RequestMappingHandlerAdapter中注入;
这里直接上源码看吧 <--  太懒了
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
    implements BeanFactoryAware, InitializingBean {

    /**
     * 这里可以看到只要 注册到customArgumentResolvers里就可以了
     */
    private List<HandlerMethodArgumentResolver> customArgumentResolvers;

    /**
     * Provide resolvers for custom argument types. Custom resolvers are ordered
     * after built-in ones. To override the built-in support for argument
     * resolution use {@link #setArgumentResolvers} instead.
     */
    public void setCustomArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        this.customArgumentResolvers = argumentResolvers;
    }
    /**
     * Return the custom argument resolvers, or {@code null}.
     */
    public List<HandlerMethodArgumentResolver> getCustomArgumentResolvers() {
        return this.customArgumentResolvers;
    }

    /**
    * Return the list of return value handlers to use including built-in and
    * custom handlers provided via {@link #setReturnValueHandlers}.
    */
    private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
       List<HandlerMethodReturnValueHandler> handlers = new ArrayList<HandlerMethodReturnValueHandler>();
    
       // Single-purpose return value types
       handlers.add(new ModelAndViewMethodReturnValueHandler());
       handlers.add(new ModelMethodProcessor());
       handlers.add(new ViewMethodReturnValueHandler());
       handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters()));
       handlers.add(new StreamingResponseBodyReturnValueHandler());
       handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
             this.contentNegotiationManager, this.requestResponseBodyAdvice));
       handlers.add(new HttpHeadersReturnValueHandler());
       handlers.add(new CallableMethodReturnValueHandler());
       handlers.add(new DeferredResultMethodReturnValueHandler());
       handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));
       handlers.add(new ListenableFutureReturnValueHandler());
       if (completionStagePresent) {
          handlers.add(new CompletionStageReturnValueHandler());
       }
    
       // Annotation-based return value types
       handlers.add(new ModelAttributeMethodProcessor(false));
       handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
             this.contentNegotiationManager, this.requestResponseBodyAdvice));
    
       // Multi-purpose return value types
       handlers.add(new ViewNameMethodReturnValueHandler());
       handlers.add(new MapMethodProcessor());
    
       // Custom return value types
       if (getCustomReturnValueHandlers() != null) {
          handlers.addAll(getCustomReturnValueHandlers());
       }
    
       // Catch-all
       if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
          handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
       }
       else {
          handlers.add(new ModelAttributeMethodProcessor(true));
       }
    
       return handlers;
    }
}




附赠一个使用过程中的小问题:
@Controller
@RequestMapping("/live/block")
public class LiveBlockController {

    @RequestMapping("/getCdnBlockRateByAreaAndIsp")
    public @ResultBeanResponseBody String getCdnBlockRateByAreaAndIsp(
            @RequestParam("countory") String countory,
            @RequestParam("province") String province,
            @RequestParam("city") String city,
            @RequestParam("isp") String isp){
        return "123123";
    }

}



这里本想可以统一进行处理,但是一直执行不到我的ResultBeanReturnValueHandler类中,于是直接翻看RequestMappingHandlerAdapter类的源码,找到invokeHandlerMethod函数


/**
* Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
* if view resolution is required.
* @since 4.2
* @see #createInvocableHandlerMethod(HandlerMethod)
*/
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
      HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

   ServletWebRequest webRequest = new ServletWebRequest(request, response);

   WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
   ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

   ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
   invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
   invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
   invocableMethod.setDataBinderFactory(binderFactory);
   invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

   ModelAndViewContainer mavContainer = new ModelAndViewContainer();
   mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
   modelFactory.initModel(webRequest, mavContainer, invocableMethod);
   mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

   AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
   asyncWebRequest.setTimeout(this.asyncRequestTimeout);

   WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
   asyncManager.setTaskExecutor(this.taskExecutor);
   asyncManager.setAsyncWebRequest(asyncWebRequest);
   asyncManager.registerCallableInterceptors(this.callableInterceptors);
   asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

   if (asyncManager.hasConcurrentResult()) {
      Object result = asyncManager.getConcurrentResult();
      mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
      asyncManager.clearConcurrentResult();
      if (logger.isDebugEnabled()) {
         logger.debug("Found concurrent result value [" + result + "]");
      }
      invocableMethod = invocableMethod.wrapConcurrentResult(result);
   }

   invocableMethod.invokeAndHandle(webRequest, mavContainer);
   if (asyncManager.isConcurrentHandlingStarted()) {
      return null;
   }

   return getModelAndView(mavContainer, modelFactory, webRequest);
}



大致过下前情,一个请求进来时,DispatcherServlet会找到其匹配的HandlerMapping和HandlerAdapter,然后会执行HandlerAdapter的handle方法->handleInternal方法->invokeHandlerMethod方法。
最后追到invocableMethod.invokeAndHandle -> returnValueHandlers.handleReturnValue函数;
整个过程总结就是对请求进行封装和前置处理,然后执行对应的controller.method方法,最后对结果进行封装。


追踪源码的顺序是HandlerAdapter.handle -> handleInternal -> invokeHandlerMethod -> invocableMethod.invokeAndHandle -> returnValueHandlers.handleReturnValue <-- 这就是目标函数了

函数handleReturnValue下会进行结果处理类ReturnValueHandler的选择和执行。
/**
* Iterate over registered {@link HandlerMethodReturnValueHandler}s and invoke the one that supports it.
* @throws IllegalStateException if no suitable {@link HandlerMethodReturnValueHandler} is found.
*/
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
      ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

   HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
   Assert.notNull(handler, "Unknown return value type [" + returnType.getParameterType().getName() + "]");
   handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}

private HandlerMethodReturnValueHandler selectHandler(Object value, MethodParameter returnType) {
   boolean isAsyncValue = isAsyncReturnValue(value, returnType);
   for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
      if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
         continue;
      }
      if (handler.supportsReturnType(returnType)) {
         return handler;
      }
   }
   return null;
}


在debug的时候发现被ViewNameMethodReturnValueHandler抢先了,因为我的返回值是一个string类型的对象。

public boolean supportsReturnType(MethodParameter returnType) {
   Class<?> paramType = returnType.getParameterType();
   return (void.class == paramType || CharSequence.class.isAssignableFrom(paramType));
}

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值