SpringMVC处理器适配器--源码解读前置知识HandlerMethodArgumentResolver与HandlerMethodReturnV接口的使用
在梳理处理器适配器(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中,然后判断需不需要进行重定向,需要的话就进行对应设置。
再补充该接口的实现类:
本文地址:https://blog.csdn.net/qq_25179481/article/details/108015045