欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

SpringMVC处理器适配器--源码解读前置知识HandlerMethodArgumentResolver与HandlerMethodReturnV接口的使用

程序员文章站 2022-04-26 14:46:21
在梳理处理器适配器(HandlerAdapter)开始前,我们先梳理其中要用到的一些内容,以便在梳理HandlerAdapter的时候能更关注这个接口本身的逻辑处理部分。同时这样的话,也能将独立的知识点提取出来,更方便理解。以前关于源码的梳理大部分都放在一篇文章中,只是对自己梳理过程的记录,读者看的话,看到一篇文章长篇大论,真的劝退(毕竟我自己看到一些长篇大论的文章也不是很能看下去,特别是一些逻辑梳理) 一、HandlerMethodReturnValueHandler接口的使用......

在梳理处理器适配器(HandlerAdapter)开始前,我们先梳理其中要用到的一些内容,以便在梳理HandlerAdapter的时候能更关注这个接口本身的逻辑处理部分。同时这样的话,也能将独立的知识点提取出来,更方便理解。

一、HandlerMethodArgumentResolver接口的使用与SpringMVC对其的实现

这里简单用一个demo来说明。常见的就是例如我们使用JWT进行token验证,然后对token的内容进行解码,将token解析的内容再赋值到一个对象中,供我们去使用一些信息,为了demo较一的功能描叙,我们就在这里简单使用json,不进行加密的内容。

xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.2.xsd
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
    <context:component-scan base-package="web" />
     <!--开启注解 -->
    <!--<mvc:default-servlet-handler/>-->
    <mvc:annotation-driven/>

    <mvc:annotation-driven>
        <mvc:argument-resolvers>
            <bean class="web.config.HeaderHandlerMethodArgumentResolver"/>
        </mvc:argument-resolvers>
    </mvc:annotation-driven>
</bean>

controller类:

@RestController
public class SpringController{
    @RequestMapping(value = "parameterResolver",method = RequestMethod.GET)
    public String parameterResolver(@HeaderAnnotation StudentVo studentVo)
    {
        return "parameterResolver";
    }
}

自定义的HandlerMethodArgumentResolver

public class HeaderHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver{
    public boolean supportsParameter(MethodParameter methodParameter) {
        if (methodParameter.hasParameterAnnotation(HeaderAnnotation.class))
        {
            return true;
        }
        return false;
    }

    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {

        String studentVoString = nativeWebRequest.getHeader("token-student");
        StudentVo studentVo = JSON.parseObject(studentVoString, StudentVo.class);
        return studentVo;
    }
}

请求:

SpringMVC处理器适配器--源码解读前置知识HandlerMethodArgumentResolver与HandlerMethodReturnV接口的使用

在请求头中加"token-student"就可以了,然后我们就能在方法中获取到赋值后的StudentVo对象了:

SpringMVC处理器适配器--源码解读前置知识HandlerMethodArgumentResolver与HandlerMethodReturnV接口的使用

1、HandlerMethodArgumentResolver接口的分析

HandlerMethodArgumentResolver  接口本身

public interface HandlerMethodArgumentResolver {
    boolean supportsParameter(MethodParameter var1);

    Object resolveArgument(MethodParameter var1, ModelAndViewContainer var2, NativeWebRequest var3, WebDataBinderFactory var4) throws Exception;
}

然后是我们自定义的:

public class HeaderHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver{
    public boolean supportsParameter(MethodParameter methodParameter) {
        if (methodParameter.hasParameterAnnotation(HeaderAnnotation.class))
        {
            return true;
        }
        return false;
    }

    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {

        String studentVoString = nativeWebRequest.getHeader("token-student");
        StudentVo studentVo = JSON.parseObject(studentVoString, StudentVo.class);
        return studentVo;
    }
}

1、MethodParameter类

这个MethodParameter在前面文章关于DispatcherServletinit方法有提到,其就是用来描叙对应方法的参数的:

public class MethodParameter {
    private final Method method;
    private final Constructor<?> constructor;
    private final int parameterIndex;
    private int nestingLevel;
    Map<Integer, Integer> typeIndexesPerLevel;
    private volatile Class<?> containingClass;
    private volatile Class<?> parameterType;
    private volatile Type genericParameterType;
    private volatile Annotation[] parameterAnnotations;
    private volatile ParameterNameDiscoverer parameterNameDiscoverer;
    private volatile String parameterName;

这里的几个参数parameterIndex表示当前参数是在第几个位置(方法能有多个参数),parameterType表示参数的类型,parameterAnnotations表示参数前面有加什么注解。

2、supportsParameter方法

SpringMVC处理器适配器--源码解读前置知识HandlerMethodArgumentResolver与HandlerMethodReturnV接口的使用

所以我们自定义的该方法就是用来判断在该参数前面有没有加@HeaderAnnotation注解,如果有加HeaderAnnotation注解,就返回true表示,表示前面这个参数能用这个HandlerMethodArgumentResolver去解析。

3、resolveArgument方法

下面就是用这个方法去返回转换为目标参数的类型与值:

public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
    String studentVoString = nativeWebRequest.getHeader("token-student");
    StudentVo studentVo = JSON.parseObject(studentVoString, StudentVo.class);
    return studentVo;
}

可以看到这个方法有入参methodParameter、modelAndViewContainer、nativeWebRequest、webDataBinderFactory,就表示你能有这些入参,通过这些入参去完成该参数的赋值。

2、SpringMVC对于这个接口是怎样实现的

现在我们来看下SpringMVC一般是怎样处理HandlerMethodArgumentResolver接口的。

1、几个主要的抽象实现

1、AbstractNamedValueMethodArgumentResolver

public abstract class AbstractNamedValueMethodArgumentResolver implements HandlerMethodArgumentResolver {
   @Nullable
   private final ConfigurableBeanFactory configurableBeanFactory;
   @Nullable
   private final BeanExpressionContext expressionContext;
   private final Map<MethodParameter, NamedValueInfo> namedValueInfoCache = new ConcurrentHashMap<>(256);
     .........

   @Override
   @Nullable
   public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
         NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
      NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
      MethodParameter nestedParameter = parameter.nestedIfOptional();

      Object resolvedName = resolveStringValue(namedValueInfo.name);
        ........
      Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
          ............

      if (binderFactory != null) {
         WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
         try {
            arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
         }
            ...........

      handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
      return arg;
   }

   private NamedValueInfo getNamedValueInfo(MethodParameter parameter) {
      NamedValueInfo namedValueInfo = this.namedValueInfoCache.get(parameter);
      if (namedValueInfo == null) {
         namedValueInfo = createNamedValueInfo(parameter);
         namedValueInfo = updateNamedValueInfo(parameter, namedValueInfo);
         this.namedValueInfoCache.put(parameter, namedValueInfo);
      }
      return namedValueInfo;
   }
   protected void handleResolvedValue(@Nullable Object arg, String name, MethodParameter parameter,
      @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest) {
   }
   ..........
   @Nullable
   private Object resolveStringValue(String value) {
      if (this.configurableBeanFactory == null) {
         return value;
      }
      String placeholdersResolved = this.configurableBeanFactory.resolveEmbeddedValue(value);
      BeanExpressionResolver exprResolver = this.configurableBeanFactory.getBeanExpressionResolver();
      if (exprResolver == null || this.expressionContext == null) {
         return value;
      }
      return exprResolver.evaluate(placeholdersResolved, this.expressionContext);
   }

   @Nullable
   protected abstract Object resolveName(String name, MethodParameter parameter, NativeWebRequest request)
         throws Exception;

      ...........
   protected void handleResolvedValue(@Nullable Object arg, String name, MethodParameter parameter,
         @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest) {
   }

   protected static class NamedValueInfo {

      private final String name;

      private final boolean required;
        ...........
   }
}

可以看到这里其对于resolveArgument方法有初步实现,其getNamedValueInfo方法就是就是将你加的注解里面的内容提取出来设置到NamedValueInfo中,例如@ReqeustBody、@RequestParam使用时,设置的value="xxx",required = true。

获取到name后再调用resolveName方法去解析获取到对应的参数值(下文会用springMVC是怎样解析@PathVariable去具体看值的设置),然后如果这个值是需要进行类型转换的话,就再调用binder.convertIfNecessary进行类型转换。

2、AbstractMessageConverterMethodArgumentResolver

public abstract class AbstractMessageConverterMethodArgumentResolver implements HandlerMethodArgumentResolver {

   private static final Set<HttpMethod> SUPPORTED_METHODS =
         EnumSet.of(HttpMethod.POST, HttpMethod.PUT, HttpMethod.PATCH);

   protected final List<HttpMessageConverter<?>> messageConverters;
   protected final List<MediaType> allSupportedMediaTypes;
   private final RequestResponseBodyAdviceChain advice;

   public AbstractMessageConverterMethodArgumentResolver(List<HttpMessageConverter<?>> converters) {
      this(converters, null);
   }

   public AbstractMessageConverterMethodArgumentResolver(List<HttpMessageConverter<?>> converters,
         @Nullable List<Object> requestResponseBodyAdvice) {
      this.messageConverters = converters;
      this.allSupportedMediaTypes = getAllSupportedMediaTypes(converters);
      this.advice = new RequestResponseBodyAdviceChain(requestResponseBodyAdvice);
   }
     .........
   @Nullable
   protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
         Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

      HttpInputMessage inputMessage = createInputMessage(webRequest);
      return readWithMessageConverters(inputMessage, parameter, paramType);
   }

   @Nullable
   protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
         Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

         ...............
      return body;
   }
    ...........
   private static class EmptyBodyCheckingHttpInputMessage implements HttpInputMessage {
      private final HttpHeaders headers;
      @Nullable
      private final InputStream body;

      public EmptyBodyCheckingHttpInputMessage(HttpInputMessage inputMessage) throws IOException {
         this.headers = inputMessage.getHeaders();
         InputStream inputStream = inputMessage.getBody();
           ...........
      }
      @Override
      public HttpHeaders getHeaders() {
         return this.headers;
      }
      @Override
      public InputStream getBody() {
         return (this.body != null ? this.body : StreamUtils.emptyInput());
      }

      public boolean hasBody() {
         return (this.body != null);
      }
   } 

这个抽象类并没有去简单实现resolveArgument方法,但这里是有实现readWithMessageConverters方法

protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
      Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

   HttpInputMessage inputMessage = createInputMessage(webRequest);
   return readWithMessageConverters(inputMessage, parameter, paramType);
}

这个方法就是通过reqeust去创建HttpInputMessage,我们看下这个接口:

public interface HttpInputMessage extends HttpMessage {
   InputStream getBody() throws IOException;
}
public interface HttpMessage {
   HttpHeaders getHeaders();
} 

这个接口,主要是两个方法:getHeaders、getBody,所以HttpInputMessage就是通过reqeust,获取request的请求体,以及请求头,然后应该就能通过请求头去听取请求体中的内容了。其再调用readWithMessageConverters方法:

@Nullable
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
      Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
   MediaType contentType;
   boolean noContentType = false;
   try {
      contentType = inputMessage.getHeaders().getContentType();
   }
    ..........
   Class<?> contextClass = parameter.getContainingClass();
   Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
   if (targetClass == null) {
      ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
      targetClass = (Class<T>) resolvableType.resolve();
   }
    ........
   EmptyBodyCheckingHttpInputMessage message;
   try {
      message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
      for (HttpMessageConverter<?> converter : this.messageConverters) {
         Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
         GenericHttpMessageConverter<?> genericConverter =
               (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
         if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
               (targetClass != null && converter.canRead(targetClass, contentType))) {
            if (message.hasBody()) {
               HttpInputMessage msgToUse =
                     getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
               body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
                     ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
               body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
            }
            else {
               body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
            }
            break;
         }
      }
   }
    ...........
   return body;
}

这里主要有两个点:首先是获取contentType,然后再遍历HttpMessageConverter,再去判断HttpMessageConverter的子类GenericHttpMessageConverter,再调用其的canRead传入contentType(所以这里如果在头部的content-type不同,就是使用不同的GenericHttpMessageConverter),看是不是使用当前GenericHttpMessageConverter去读取body中的内容,如果是就调用其read方法去读取(与HandlerMethodArgumentResolver的使用类似)body。

这里还有一个点,在调用genericConverter方法之前与之后会调用getAdvice().beforeBodyRead:

@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage request, MethodParameter parameter,
      Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {

   for (RequestBodyAdvice advice : getMatchingAdvice(parameter, RequestBodyAdvice.class)) {
      if (advice.supports(parameter, targetType, converterType)) {
         request = advice.beforeBodyRead(request, parameter, targetType, converterType);
      }
   }
   return request;
}
public interface RequestBodyAdvice {

   boolean supports(MethodParameter methodParameter, Type targetType,
         Class<? extends HttpMessageConverter<?>> converterType);
   HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter,
         Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException;
   Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
         Type targetType, Class<? extends HttpMessageConverter<?>> converterType);
   @Nullable
   Object handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage, MethodParameter parameter,
         Type targetType, Class<? extends HttpMessageConverter<?>> converterType);
}

接口的作用通过我们以前在梳理讲通过BeanFactory获取bean的时候有讲过,就是看这个接口的入参、出参、与调用时机就差不多能猜到其是什么作用,这里就不再具体分析了,可以看下其他博文。

2、这两种抽象实现的差别

通过前面的分析以及源码中的注解来理解:

AbstractNamedValueMethodArgumentResolver体现的请求的方法参数的内容是需要在Request parameters, request headers, and path variables are examples of named(请求头、parameters、请求路径中)这些地方获取的值。而AbstractMessageConverterMethodArgumentResolver 是表示方法参数的值是需要从 from the body of a request(请求体中)。

3、对应抽象类的的几个具体实现

1、PathVariable注解(PathVariableMethodArgumentResolver)

PathVariable注解是用PathVariableMethodArgumentResolver类去处理,,其继承AbstractNamedValueMethodArgumentResolver类,同时这个类还有继承其他的类:

SpringMVC处理器适配器--源码解读前置知识HandlerMethodArgumentResolver与HandlerMethodReturnV接口的使用

这里我们简单来看下主要的两个方法:

supportsParameter方法

@Override
public boolean supportsParameter(MethodParameter parameter) {
   if (!parameter.hasParameterAnnotation(PathVariable.class)) {
      return false;
   }
   if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
      PathVariable pathVariable = parameter.getParameterAnnotation(PathVariable.class);
      return (pathVariable != null && StringUtils.hasText(pathVariable.value()));
   }
   return true;
}

可以看到这个方法与我们前面写的类似(我就是模仿这里对应写的自定义类),主要就是判断参数前面有没有PathVariable注解。

resolveArgument方法

public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
      NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

   NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
   MethodParameter nestedParameter = parameter.nestedIfOptional();

   Object resolvedName = resolveStringValue(namedValueInfo.name);
            .........
   Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
       ..........
   if (binderFactory != null) {
      WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
      try {
         arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
      }
       ........
   handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);

   return arg;
}
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
   Map<String, String> uriTemplateVars = (Map<String, String>) request.getAttribute(
         HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
   return (uriTemplateVars != null ? uriTemplateVars.get(name) : null);
}

可以看到这里就是通过MethodParameter 去构建这个注解的描叙:

SpringMVC处理器适配器--源码解读前置知识HandlerMethodArgumentResolver与HandlerMethodReturnV接口的使用

然后是调用resolveName方法去获取参数的具体值,其在PathVariableMethodArgumentResolver的实现就是去request中获取对应的value:

protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
   Map<String, String> uriTemplateVars = (Map<String, String>) request.getAttribute(
         HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
   return (uriTemplateVars != null ? uriTemplateVars.get(name) : null);
}

(spring在前面进行地址解析的时候就会将该值以URI_TEMPLATE_VARIABLES_ATTRIBUTE为key设置到request中)

然后如果有需要的话再通过convertIfNecessary方法去解析类型转换。

同时SpringMVC对该接口还有很多其他实现,这里就不再一一梳理了

SpringMVC处理器适配器--源码解读前置知识HandlerMethodArgumentResolver与HandlerMethodReturnV接口的使用

2、RequestBody注解(RequestResponseBodyMethodProcessor)

SpringMVC处理器适配器--源码解读前置知识HandlerMethodArgumentResolver与HandlerMethodReturnV接口的使用

supportsParameter方法

@Override
public boolean supportsParameter(MethodParameter parameter) {
   return parameter.hasParameterAnnotation(RequestBody.class);
}

resolveArgument方法

@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
      NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

   parameter = parameter.nestedIfOptional();
   Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
   String name = Conventions.getVariableNameForParameter(parameter);

   if (binderFactory != null) {
      WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
       .........
   }
   return adaptArgumentIfNecessary(arg, parameter);
}

这里调用了readWithMessageConverters去获取参数值

@Override
protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
      Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

   HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
   ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
   Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
   if (arg == null) {
      if (checkRequired(parameter)) {
         throw new HttpMessageNotReadableException("Required request body is missing: " +
               parameter.getExecutable().toGenericString());
      }
   }
   return arg;
}

然后再调用了父类的readWithMessageConverters方法。

3、GenericHttpMessageConverter接口的对应实现

我们再来补充在AbstractMessageConverterMethodArgumentResolver中readWithMessageConverters方法调用的GenericHttpMessageConverter接口的实现。

SpringMVC处理器适配器--源码解读前置知识HandlerMethodArgumentResolver与HandlerMethodReturnV接口的使用

可以看到这里的关键词:Jackson、ResourceRegion,Jackson就是与Json相关的,例如我们的@RequestBody就是将请求体中的json格式内容转换为对应的java对象(context-type是application/json),然后ResourceRegion就是表示的Resource资源:

public class ResourceRegion {
   private final Resource resource;
   private final long position;
   private final long count;
    .........
}

SpringMVC处理器适配器--源码解读前置知识HandlerMethodArgumentResolver与HandlerMethodReturnV接口的使用

我们以RequestBody注解为例,看其获取的是GenericHttpMessageConverter的哪个子类:

@RequestMapping(value = "parameterBody",method = RequestMethod.POST)
public String parameterBody(@RequestBody StudentVo studentVo)
{
    return "parameterResolver";
}

再将{"name":"Fev","age":25}放到请求体中:

SpringMVC处理器适配器--源码解读前置知识HandlerMethodArgumentResolver与HandlerMethodReturnV接口的使用

我们可以看到其canRead方法:


public boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType) {
   if (!canRead(mediaType)) {
      return false;
   }
   JavaType javaType = getJavaType(type, contextClass);
   AtomicReference<Throwable> causeRef = new AtomicReference<>();
   if (this.objectMapper.canDeserialize(javaType, causeRef)) {
      return true;
   }
   return false;
}

首先是判断mediaType,当前请求头是不是用该类解析:

public AbstractJsonHttpMessageConverter() {
   super(MediaType.APPLICATION_JSON, new MediaType("application", "*+json"));
   setDefaultCharset(DEFAULT_CHARSET);
}

如果是就通过objectMapper.canDeserialize看能不能转换,如果能再调用read读取body

public Object read(Type type, Class<?> contextClass, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
    JavaType javaType = this.getJavaType(type, contextClass);
    return this.readJavaType(javaType, inputMessage);
}
private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) {
    ........
      return this.objectMapper.readValue(inputMessage.getBody(), javaType);
     .......
}

SpringMVC处理器适配器--源码解读前置知识HandlerMethodArgumentResolver与HandlerMethodReturnV接口的使用


二、HandlerMethodReturnValueHandler接口的使用与SpringMVC对其的实现

1、这个与前面HandlerMethodArgumentResolver一样,但需要改下xml文件以及自定义一个HandlerMethodReturnValueHandler的实现类。

xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.2.xsd
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
    <context:component-scan base-package="web" />
    <mvc:annotation-driven/>
    <mvc:annotation-driven>
        <mvc:argument-resolvers>
            <bean class="web.config.HeaderHandlerMethodArgumentResolver"/>
        </mvc:argument-resolvers>
        <mvc:return-value-handlers>
            <bean></bean>
        </mvc:return-value-handlers>
    </mvc:annotation-driven>
</beans>

controller:

@Controller
public class SpringController{
   @RequestMapping(value = "parameterResponse",method = RequestMethod.POST)
   public StudentVo parameterResponse()
   {
       StudentVo studentVo = new StudentVo();
       studentVo.setAge(25);
       studentVo.setName("Fev");
       return studentVo;
   }
}

自定义HandlerMethodResultValueHandler

public class HeaderHandlerMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
    public boolean supportsReturnType(MethodParameter methodParameter) {
        if (methodParameter.getParameterType().isAssignableFrom(StudentVo.class))
        {
            return true;
        }
        return false;
    }

    public void handleReturnValue(Object o, MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest) throws Exception {
        if (StudentVo.class.isAssignableFrom(o.getClass()))
        {
            StudentVo studentVo = (StudentVo)o;
            String jsonObject = JSON.toJSONString(studentVo);
            ResponseFacade responseFacade = nativeWebRequest.getNativeResponse(ResponseFacade.class);
            responseFacade.setHeader("token-student",jsonObject);
            //表示不需要再进行视图解析直接返回,为什么要设置这个到关于处理器适配器的源码梳理就明白了
            modelAndViewContainer.setRequestHandled(Boolean.TRUE);
        }
    }
}

可以看到这个demo就是对前面的HandlerMethodArgumentResolver的逆化操作,然后请求

SpringMVC处理器适配器--源码解读前置知识HandlerMethodArgumentResolver与HandlerMethodReturnV接口的使用

可以看到在返回的请求头中就有了token-student。

1、HandlerMethodReturnValueHandler接口的分析

public interface HandlerMethodReturnValueHandler {

   boolean supportsReturnType(MethodParameter returnType);

   void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
         ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}

然后这里的入参MethodParameter 这些在前面讲HandlerMethodArgumentResolver有简答分析,这里就不再赘叙了。

2、SpringMVC对于这个接口是怎样实现的

1、AbstractMessageConverterMethodProcessor

关于HandlerMethodReturnValueHandler的抽象类,SpringMVC就这个AbstractMessageConverterMethodProcessor,其他的都是直接去实现的HandlerMethodReturnValueHandler接口,然后这个AbstractMessageConverterMethodProcessor接口,在前面讲HandlerMethodArgumentResolver的时候有梳理过,不过是梳理的HandlerMethodArgumentResolver部分,现在来梳理HandlerMethodReturnValueHandler部分

SpringMVC处理器适配器--源码解读前置知识HandlerMethodArgumentResolver与HandlerMethodReturnV接口的使用

前面是read,现在是write:

protected <T> void writeWithMessageConverters(T value, MethodParameter returnType, NativeWebRequest webRequest)
      throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

   ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
   ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
   writeWithMessageConverters(value, returnType, inputMessage, outputMessage);
}

然后这个writeWithMessageConverters方法,与前面readWithMessageConverters类似,就不再具体展开,这里再来看下其使用的GenericHttpMessageConverter接口。

public interface GenericHttpMessageConverter<T> extends HttpMessageConverter<T> {

   boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType);

   T read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)
         throws IOException, HttpMessageNotReadableException;

   boolean canWrite(@Nullable Type type, Class<?> clazz, @Nullable MediaType mediaType);

   void write(T t, @Nullable Type type, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
         throws IOException, HttpMessageNotWritableException;
}

可以看到这个接口有四个方法发布定义读与写。然后在这里,这个writeWithMessageConverters方法就是使用的canWrite、write。

3、对应抽象类的的具体实现

@ResponseBody(RequestResponseBodyMethodProcessor)

这个RequestResponseBodyMethodProcessor类我们前面同样梳理的是其的read,这个类就是用来处理@RequestBody、@ResponseBody的,一个入参、一个出参。

public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
     ..............
   @Override
   public boolean supportsParameter(MethodParameter parameter) {
      return parameter.hasParameterAnnotation(RequestBody.class);
   }

   @Override
   public boolean supportsReturnType(MethodParameter returnType) {
      return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
            returnType.hasMethodAnnotation(ResponseBody.class));
   }
    .......
   @Override
   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);
      writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
   }
    ........
}

这里的一些细节与前面类似,就不再一一分析了。

2、ModelAndView(ModelAndViewMethodReturnValueHandler)

我们再来看一个直接实现HandlerMethodReturnValueHandler接口的类,这个类就是用来处理返回类是ModelAndView的:

public class ModelAndViewMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
    ...........
   @Override
   public boolean supportsReturnType(MethodParameter returnType) {
      return ModelAndView.class.isAssignableFrom(returnType.getParameterType());
   }

   @Override
   public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
         ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
      if (returnValue == null) {
         mavContainer.setRequestHandled(true);
         return;
      }
      ModelAndView mav = (ModelAndView) returnValue;
      if (mav.isReference()) {
         String viewName = mav.getViewName();
         mavContainer.setViewName(viewName);
         if (viewName != null && isRedirectViewName(viewName)) {
            mavContainer.setRedirectModelScenario(true);
         }
      }
      else {
         View view = mav.getView();
         mavContainer.setView(view);
         if (view instanceof SmartView) {
            if (((SmartView) view).isRedirectView()) {
               mavContainer.setRedirectModelScenario(true);
            }
         }
      }
      mavContainer.setStatus(mav.getStatus());
      mavContainer.addAllAttributes(mav.getModel());
   }
    .......
   protected boolean isRedirectViewName(String viewName) {
      if (PatternMatchUtils.simpleMatch(this.redirectPatterns, viewName)) {
         return true;
      }
      return viewName.startsWith("redirect:");
   }
}
public boolean isReference() {
   return (this.view instanceof String);
}

这里handleReturnValue主要是三个操作,判断当前ModelAndView是不是直接返回的视图view,还是只是一个viewName,再分别设置到mavContainer中,然后判断需不需要进行重定向,需要的话就进行对应设置。

再补充该接口的实现类:

SpringMVC处理器适配器--源码解读前置知识HandlerMethodArgumentResolver与HandlerMethodReturnV接口的使用

本文地址:https://blog.csdn.net/qq_25179481/article/details/108015045