专业的编程技术博客社区

网站首页 > 博客文章 正文

Springboot接口返回值自定义处理规则你肯定不知道

baijin 2024-08-20 10:13:59 博客文章 10 ℃ 0 评论

环境:Springboot2.4.11


工作原理

先来看看Controller接口的返回值是如何做输出的,以如下接口为例:

@GetMapping("/rv")
public Users us() {
  return new Users() ;
}

实际方法的执行是在HandlerAdapter中执行,默认实现是
RequestMappingHandlerAdapter

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean {
  protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
    // ...
    // 为方法的执行设置返回值处理句柄
    // this.returnValueHandlers = HandlerMethodReturnValueHandlerComposite
    // 在HandlerMethodReturnValueHandlerComposite中保存了当前容器中的所有HandlerMethodReturnValueHandler集合
    // 值是在afterPropertiesSet中设置
    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(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
    // 遍历所有的HandlerMethodReturnValueHandler,找出合适的对象
    HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
    // ...
    // 调用HandlerMethodReturnValueHandler#handleReturnValue方法处理返回结果信息
    handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
  }
  // 上面的selectHandler方法查找合适的HandlerMethodReturnValueHandler方法。
  private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
    boolean isAsyncValue = isAsyncReturnValue(value, returnType);
    for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
      // ...
      // 调用每一个Handler的supportsReturnType判断是否支持当前接口的返回值对象
      if (handler.supportsReturnType(returnType)) {
        return handler;
      }
    }
    return null;
  }
}

向客户端响应数据调用
HandlerMethodReturnValueHandler#handleReturnValue方法,根据上面的示例确定RequestResponseBodyMethodProcessor处理返回值。

public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
  public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
    mavContainer.setRequestHandled(true);
    ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
    ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
    // 该方法中会对返回值进行转换(HttpMessageConverter),然后对内容进行输出到客户端(默认就是JSON)
    writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
  } 
}

到这里知道了原理输出的内容,如何进行输出是由
HandlerMethodReturnValueHandler对象决定的那么接下来就可以自定义HandlerMethodReturnValueHandler对象进行输出了。

自定义HandlerMethodReturnValueHandler

自定义

public class CustomHandlerMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
  @Override
  public boolean supportsReturnType(MethodParameter returnType) {
    // 如果返回值类型为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() ;
  }
}

注册自定义的
HandlerMethodReturnValueHandler对象

@Configuration
public class WebConfig implements WebMvcConfigurer {
  @Override
  public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
    handlers.add(new CustomHandlerMethodReturnValueHandler()) ;
  }
}

测试

发现并没有生效,按照原来的内容进行了输出。

分析原因

先看自定义的是否生效被注册了

通过调试发现自定义的确实被注册了,但是呢
RequestResponseBodyMethodProcessor系统默认的这个处理器在前,所以被它处理了。

通过查看源码知道了HandlerAdapter是如何定义这些系统默认的处理器和自定义处理器的

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean {
  public void afterPropertiesSet() {
    // ...    
    if (this.returnValueHandlers == null) {
      // 在这个getDefaultReturnValueHandlers方法中处理
      List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
      this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
    }
  }      
  private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
		List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(20);
    // ...
    handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.contentNegotiationManager, this.requestResponseBodyAdvice));
    // ...
		// 自定义返回值类型处理器
    if (getCustomReturnValueHandlers() != null) {
      handlers.addAll(getCustomReturnValueHandlers());
    }
    handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
    return handlers;
  }
}

通过源码查看 本身自定义的就会排在
RequestResponseBodyMethodProcessor后面,同时


HandlerMethodReturnValueHandler也没有实现Ordered接口,我们也没法进行排序。

接下来我们可以通过如下的方式来使其生效

重新设置
HandlerMethodReturnValueHandler,把自定义的添加到最前面。

测试

生效,成功了!!!

完毕!!!

求个关注+转发

公众:Springboot实战案例锦集

Spring MVC 异步请求方式

Spring MVC 异常处理方式

SpringMVC参数统一验证方法

Spring MVC高级知识点自定义请求匹配路径

SpringMVC内嵌Tomcat零配置

SpringMVC自定义注解实现接口调用 SpringMVC请求原理源码分析

使用Spring Boot Admin实时监控你的系统

Spring Boot Security防重登录及在线总数

SpringBoot多数据源配置详解 SpringBoot项目查看线上日志

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表