在梳理处理器适配器(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; } }
请求:

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

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方法

所以我们自定义的该方法就是用来判断在该参数前面有没有加@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类,同时这个类还有继承其他的类:

这里我们简单来看下主要的两个方法:
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 去构建这个注解的描叙:

然后是调用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对该接口还有很多其他实现,这里就不再一一梳理了

2、RequestBody注解(RequestResponseBodyMethodProcessor)

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接口的实现。

可以看到这里的关键词: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; ......... }

我们以RequestBody注解为例,看其获取的是GenericHttpMessageConverter的哪个子类:
@RequestMapping(value = "parameterBody",method = RequestMethod.POST) public String parameterBody(@RequestBody StudentVo studentVo) { return "parameterResolver"; }
再将{"name":"Fev","age":25}放到请求体中:

我们可以看到其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); ....... }

二、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的逆化操作,然后请求

可以看到在返回的请求头中就有了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部分

前面是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中,然后判断需不需要进行重定向,需要的话就进行对应设置。
再补充该接口的实现类:

1357

被折叠的 条评论
为什么被折叠?



