spring--自定义ReturnValueHandler

来源:互联网 发布:js valueof 比较大小 编辑:程序博客网 时间:2024/06/05 02:15
由于某些时候需要对controller的返回对象作统一的封装,例如一个业务系统中统一的返回格式。这里可以使用到ReturnValueHandler,当然也可以使用ResponseBody、Convertor或者View等。

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


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

/** * 结果封装类 */public class ResultBeanReturnValueHandler implements HandlerMethodReturnValueHandler {     /**      * 类似ResponseBody,仅有添加ResultBeanResponseBody注解的method才会触发      */@Overridepublic boolean supportsReturnType(MethodParameter returnType) {    return (AnnotationUtils.findAnnotation(returnType.getContainingClass(), ResultBeanResponseBody.class) != null ||    returnType.getMethodAnnotation(ResultBeanResponseBody.class) != null);}/** * 使用统一的结果封装类ResultInfo,并序列化成json */@Overridepublic 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)@Documentedpublic@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.*/@Overridepublic 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));}