tomcat + spring mvc原理(十二):spring mvc请求的适配处理和返回2
前言
HandleAdapter作为spring mvc中最重要的组件没有之一,源码中隐藏着非常多对于项目编码有益的知识点。原理(十一)已经介绍了HandleAdapter的基本原理和具体实现类RequestMappingHandlerAdapter处理请求的主要流程。
首先,上篇文章的要点开始于AbstractHandlerMethodAdapter的handle方法。在handle方法中调用了RequestMappingHandlerAdapter的handleInternal方法,而handleInternal方法中代码逻辑中invokeHandlerMethod方法统领了后续处理请求的整个流程。本文主要就是invokeHandlerMethod方法进行分析,并且简要介绍其中使用到的一些组件的原理。
其次,上篇文章还介绍了贯穿整个处理流程的数据体ModelAndViewContainer,其中包含的参数可以参考下面这张图。
处理流程
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
//1. 创建ServletWebRequest,在ArgumentResolver进行参数解析时使用
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
//2. 获取DataBinderFactory,处理和@initBinder相关的参数
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
//3. 创建ModelFactory,处理model相关的操作
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
//4. 创建ServletInvocableHandlerMethod,用来执行最终的请求处理
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
//5. 设置参数解析器argumentResolvers的List
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
//6. 设置返回结果的处理器List
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
//7. 设置DataBinderFactory
invocableMethod.setDataBinderFactory(binderFactory);
//8. 设置参数名获取器
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
//9. 创建ModelAndViewContainer
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
//(如果有)设置重定向的model参数
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
//10. 初始化model
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
//后面主要是异步处理相关
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
//11. 传入参数,处理请求
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
//12. 获取返回结果
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
invokeHandlerMethod引用代码中已经做了详细的注释,下面这张图再对主要流程进行归纳总结,方便大家参考。文章后续会对流程分步分析,以全面探求最终实现的原理细节。
根据参数的分类可知,参与这个流程的参数不仅包括请求request,还有一部分model参数、session缓存参数和重定向model参数。这些参数的准备也需要分类讨论,请求request只需要做一个转换(注释1),根据request和response生成ServletWebRequest,用在后续的argumentResolver的参数解析中。model参数和session参数的设置都在ModelFactory中,session参数使用了专用组件SessionAttributesHandler和SessionAttributeStore工具。处理请求和设置结果返回值统一在ServletInvocableHandlerMethod类中,这个类继承自HandlerMethod类,设置了WebDataBinderFactory、HandlerMethodArgumentResolver、HandlerMethodReturnValueHandler等组件协助。最后,getModelAndView(注释12)从ModelAndViewContainer和ServletWebRequest获得了最终的ModelAndView结果。
request转换
第一步由request和response生成ServletWebRequest,ServletWebRequest中包含了HttpServletRequest和HttpServletResponse这两个类的实例,其他参数还有HttpSession的实例。统一构造这样一个类必要性,可能是为了后续HandlerMethodArgumentResolver参数解析的方便?这里存在一些疑问。
获取WebDataBinderFactory
spring mvc中的Binder可以对请求中输入的参数进行预处理,通过@IntiBinder注解获取Binder后能够注册一个编辑器。例如,可以在Controller中定义如下的方法:
@InitBinder
public void initBinder(WebDataBinder binder) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
binder.registerCustomEditor(Date.class, new CustomDateEditor(sdf, true));
}
这样,这个Controller下所有GET接口传入的符合“yyyy-MM-dd HH:mm:ss”的String参数,都可以被自动转化为Date类型。spring mvc支持多种的编辑器,包括URL、URI、String、Date等。如果使用@ControllerAdvice注解,还可以定义全局或者特定包、特定类的Binder。如果有兴趣可以自行查阅相关资料。
第2步中getBinderFactory方法创建了一个WebDataBinderFactory,同时把对应接口(HandlerMethod)的@InitBinder方法都设置到工厂类中。在后续参数解析时,HandlerMethodArgumentResolver或用到这个构造的WebDataBinderFactory实例。
创建ModelFactory和初始化model
第3步和第10步都是和ModelFactory有关。创建factory和初始化model过程中,就包含了上面流程图中设置session参数和model参数这两步。因为model模型在现在的服务端业务中已经使用很少,所以这部分内容只做简略介绍。
getModelFactory方法在创建ModelFactory时使用了三个参数:
- 注释了@ModelAtrribute的方法
- 上一步中获取的WebDataBinderFactory
- SessionAttributeHandler
在方法上注解@ModelAttribute,可以在Controller中的接口处理请求之前,添加或修改model中的属性值。sessionAttributeHandler是用来处理@SessionAttribute注解相关的处理器。@SessionAttribute注解用来向session中添加参数。
第10步中的initModel方法所做的事情比较简单,就是在第3步的基础上,将@SessionAttribute和@ModelAttribute注解中取出的参数设置到ModelAndViewContainer的实例对象mavContainer中。
方法和方法的参数
WebDataBinderFactory、HandlerMethodArgumentResolver、HandlerMethodReturnValueHandler等组件构造后,都被置入ServletInvocableHandlerMethod类中,实际发挥作用也是在类中被调用。在原理(九)中,HandlerMapping将对应请求的接口方法(HandlerMethod)搜索出来,最终的目的就是在这里被使用。ServletInvocableHandlerMethod就继承自HandlerMethod,具备HandlerMethod所有特性。
使用接口方法(HandlerMethod)处理请求,需要存储方法(Method)和方法参数。
public class HandlerMethod {
private final Object bean;
@Nullable
private final BeanFactory beanFactory;
private final Class<?> beanType;
private final Method method;
private final Method bridgedMethod;
private final MethodParameter[] parameters;
......
private volatile List<Annotation[][]> interfaceParameterAnnotations;
······
}
HandlerMethod中存储了接口方法所在类的bean和BeanFactroy实例。beanFactory可以在bean为beanName(这时Object为String),用来获取真正的bean。method是用来存储接口方法Method实例或者是bridge method。bridge Method是由java虚拟机的特殊机制产生,如果感兴趣自行查阅资料,这里只需要知道method和brigedMethod总会至少有一个存储了方法本身。方法的参数使用MethodParameter的数组来存储。interfaceParameterAnnotations是对应接口上的各种注解。
方法参数信息需要有参数序号、参数名、参数类型等,其中参数还可能复合结构,MethodParameter类在设计时需要考虑这一层。
public class MethodParameter {
private final Executable executable;
private final int parameterIndex;
@Nullable
private volatile Parameter parameter;
private int nestingLevel;
/** Map from Integer level to Integer type index. */
@Nullable
Map<Integer, Integer> typeIndexesPerLevel;
@Nullable
private volatile Class<?> containingClass;
@Nullable
private volatile Class<?> parameterType;
@Nullable
private volatile Type genericParameterType;
@Nullable
private volatile Annotation[] parameterAnnotations;
@Nullable
private volatile ParameterNameDiscoverer parameterNameDiscoverer;
@Nullable
private volatile String parameterName;
······
}
- Executable是Constructor和Method的父类,executable用来存储方法。
- 方法所在的类使用containingClass存储。
- parameterIndex代表该参数在方法中的序号。parameterType存储参数的类型,genericParameterType是Type类型的参数类型。参数名是parameterName。
- Parameter是复合的参数类,里面存储了序号、参数名、方法等,并不一定必须。
- 还有两个类似List这种复合参数的指标。nestingLevel代表嵌套级别,List直接作为参数的话,List嵌套级别为1, Integer嵌套级别为2。对于Integer,还有对应的序号,typeIndexesPerLevel就是存储对应嵌套层数的参数的序数。
- 一般来说使用反射无法获取参数名,ParameterNameDiscoverer就是用来获取参数名的组件。如果使用默认的DefaultParameterNameDiscoverer类,这个类使用多重策略,确保能够查找到参数名。一是Java 8标准反射机制,回调基于ASM的反射机制查找class文件中的debug信息,获取参数名。二是Kotlin反射机制。如果Kotlin的反射机制可用,则会被第一个使用。
参数解析与方法调用
invocableMethod.invokeAndHandle(webRequest, mavContainer);
invokeAndHandle方法的首先调用了:
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
。invokeForRequest方法的具体代码逻辑是由ServletInvocableHandlerMethod的父类InvocableHandlerMethod实现,而InvocableHandlerMethod又继承自HandlerMethod。InvocableHandlerMethod主要的工作是参数的解析。
public class InvocableHandlerMethod extends HandlerMethod {
private static final Object[] EMPTY_ARGS = new Object[0];
@Nullable
private WebDataBinderFactory dataBinderFactory;
private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();
private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
......
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Arguments: " + Arrays.toString(args));
}
return this.doInvoke(args);
}
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
MethodParameter[] parameters = this.getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
} else {
Object[] args = new Object[parameters.length];
for(int i = 0; i < parameters.length; ++i) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] == null) {
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
} catch (Exception var10) {
if (this.logger.isDebugEnabled()) {
String exMsg = var10.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
this.logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw var10;
}
}
}
return args;
}
}
@Nullable
protected Object doInvoke(Object... args) throws Exception {
ReflectionUtils.makeAccessible(this.getBridgedMethod());
try {
return this.getBridgedMethod().invoke(this.getBean(), args);
} catch (IllegalArgumentException var4) {
this.assertTargetBean(this.getBridgedMethod(), this.getBean(), args);
String text = var4.getMessage() != null ? var4.getMessage() : "Illegal argument";
throw new IllegalStateException(this.formatInvokeError(text, args), var4);
} catch (InvocationTargetException var5) {
Throwable targetException = var5.getTargetException();
if (targetException instanceof RuntimeException) {
throw (RuntimeException)targetException;
} else if (targetException instanceof Error) {
throw (Error)targetException;
} else if (targetException instanceof Exception) {
throw (Exception)targetException;
} else {
throw new IllegalStateException(this.formatInvokeError("Invocation failure", args), targetException);
}
}
}
}
InvocableHandlerMethod的WebDataBinderFactory工厂类可以对参数进行预处理,被用在了参数解析的过程中。parameterNameDiscoverer设置了默认值DefaultParameterNameDiscoverer,原理在上一节已经介绍了。HandlerMethodArgumentResolverComposite是HandlerMethodArgumentResolver的复合类,存储所有获取并设置到InvocableHandlerMethod的参数解析器。如果了解HandlerMapping的匹配条件的设计(原理(十)),会发现这两者之间存在比较大的相识性。这个类是参数解析的重要组件,后面会详细介绍。
invokeForRequest方法可以分解为两部分。
- 获取参数:
Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
- 调用接口方法执行请求:
return this.doInvoke(args);
getMethodArgumentValues会对HandlerMethod的每一个参数依次获取对应的传入参数值,内部的for循环所做的就是这样一件事。首先使用parameterNameDiscoverer获取参数名,然后如果传入参数providedArgs已经提供了参数值,就直接获取。在invokeAndHandle方法中可以看到,providedArgs这个参数实际没有设置。那么这样就会调用HandlerMethodArgumentResolverComposite类的supportsParameter方法验证是否支持解析此参数,接着使用resolveArgument方法解析并获取请求中的参数值。所有参数值获取之后,通过数组args返回。
参数值获取之后,doInvoke方法就很简单了,运用反射调用method的invoke方法,按顺序输入所有参数值就能够获得返回结果Object对象。
参数解析的分析
参数值解析最重要的部分就是HandlerMethodArgumentResolver类。
参数解析器的管理
HandlerMethodArgumentResolverComposite实现了HandlerMethodArgumentResolver接口,内部持有所有获取的参数解析器,能够根据传入的参数类型检索到对应的参数解析器进行解析。由于对于服务器来说,参数值解析非常频繁,HandlerMethodArgumentResolverComposite并不是单纯的遍历检索,也做了检索的优化。
public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
private final List<HandlerMethodArgumentResolver> argumentResolvers = new LinkedList();
private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache = new ConcurrentHashMap(256);
......
public boolean supportsParameter(MethodParameter parameter) {
return this.getArgumentResolver(parameter) != null;
}
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
HandlerMethodArgumentResolver resolver = this.getArgumentResolver(parameter);
if (resolver == null) {
throw new IllegalArgumentException("Unsupported parameter type [" + parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
} else {
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
}
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
HandlerMethodArgumentResolver result = (HandlerMethodArgumentResolver)this.argumentResolverCache.get(parameter);
if (result == null) {
Iterator var3 = this.argumentResolvers.iterator();
while(var3.hasNext()) {
HandlerMethodArgumentResolver resolver = (HandlerMethodArgumentResolver)var3.next();
if (resolver.supportsParameter(parameter)) {
result = resolver;
this.argumentResolverCache.put(parameter, resolver);
break;
}
}
}
return result;
}
}
最初,所有注册的HandlerMethodArgumentResolver在invokeHandlerMethod方法中设置到argumentResolvers里。argumentResolverCache是ConcurrentHashMap类型。ConcurrentHashMap线程安全且高效的HashMap,一般用来处理高并发的情况。这里argumentResolverCache用作一种缓存机制,提高
HandlerMethodArgumentResolver的检索效率。
每当需要判断是否支持某种类型的参数值解析,调用supportsParameter方法时,或者实际进行参数值解析时,都需要先获取对应参数值解析的HandlerMethodArgumentResolver。getArgumentResolver
方法会现在argumentResolverCache使用参数类型MethodParameter作为key查找对应参数值解析器,如果没有,再去List中遍历。获取的对应解析器会存入缓存中,这样,下次此类参数的解析会更加的快速。
获取到对应参数的HandlerMethodArgumentResolver后,HandlerMethodArgumentResolverComposite的resolveArgument方法调用对应参数值解析器的resolveArgument来获取最终的参数值。
参数解析器的分类
参数解析器的种类非常多,除了上文的“管理器”,剩下实现HandlerMethodArgumentResolver的类还有31个,我大致将这些分成五大类。
第一大类主要是解析request中的参数,也包括response中需要的参数。这一大类又可以分为四小类。
-
request中参数值的常规解析
这一类在请求体request参数值解析中发挥了比较大的作用,主要成员是上图所示的黑色方框中继承AbstractNamedValueMethodArgumentResolver的子类。-
- AbstractCookieValueMethodArgumentResolver及其子类ServletCookieValueMethodArgumentResolver用作@CookieValue注解的Cookie值的解析。
-
- PathVariableMethodArgumentResolver用来解析@PathVariable参数值。注:在请求时可以使用{}来设置可变的url路径参数,比如可以设置匹配的url为/book/{bookname},而参数中使用@PathVariable String bookname就能解析出传入的bookname的值。
-
- MatrixVariableMethodArgumentResolver处理@MatrixVariable注解的参数。注:可以在url中使用";“为”{}"变量添加限定,比如Get /book/effective-java;author=JoshuaBloch,使用@MatrixVariable就能将author=JoshuaBloch这一限定读取出来。
-
- RequestHeaderMethodArgumentResolver用来解析@RequestHeader注解的参数值,获取header的信息。
-
- RequestParamMethodArgumentResolver平常使用较多,可以获取@RequestParam注解需要的参数值。
-
-
request中参数的map解析
上述request中的参数值除了cookie之外,其他都可以以Map的形式获取,以@RequestParam为例:
@GetMapping("get")
public String test(@RequestParam Map<String,Object> paramsMap){
return paramsMap.get("s1").toString() + " " +paramsMap.get("s2").toString();
}
,这时不再需要匹配参数名,同时业务接口中也能同时获得参数名和参数值。这种方法获取参数值的解析器是上图蓝色方框中的子类,包括PathVariableMapMethodArgumentResolver、MatrixVariableMapMethodArgumentResolver、RequestHeaderMapMethodArgumentResolver、RequestParamMapMethodArgumentResolver这四个直接实现HandlerMethodArgumentResolver接口的子类。
-
请求体中参数的解析
第三小类是用来解析请求的请求体中的数据。上面的继承图谱显示了AbstractMessageConverterMethodProcessor及其子类实现了HandlerMethodReturnValueHandler接口,这个组件是在后面设置返回值这一步发挥作用的。命名后缀为Processor而不是Resolver意味着这些类不只处理请求,还用来处理返回结果。 -
- RequestPartMethodArgumentResolver用来获取@RequestPart注解的参数值。@RequestPart可以获取Multipart类型的数据,如果参数类型是Multipart,就从消息体中获取返回MultipartFile;如果参数不是Multipart类型,可以进行自动转换。
-
- HttpEntityMethodProcessor负责HttpEntity、RequestEntity参数的解析,HttpEntity及子类RequestEntity不仅可以获取请求体中的内容,而且还可以获取请求头中的内容。除此之外,HttpEntityMethodProcessor实现了HandlerMethodReturnValueHandler的方法,还作为返回ResponseEntity结果的处理器。在这种情况下,return参数再作为视图名,供视图解析器使用,而是直接生成http的响应。
-
- RequestResponseBodyMethodProcessor可以用来获取请求体中的内容,对应@RequestBody注解的参数值解析。这个功能目前使用比较多,一般会在POST的请求体中写入json数据,这个数据能够被RequestResponseBodyMethodProcessor解析,赋值给java POJO类。类似于HttpEntityMethodProcessor,RequestResponseBodyMethodProcessor也可以用来处理返回的数据,@ResponseBody(或@RestController)注解的功能实现就是由这个类提供的功能,可以将数据结构解析为json后直接返回。@RestController注解是@Controller和@ResponseBody注解的组合。
-
Servlet请求相关
这个小类使用就比较少,一般情况下用不到这些解析器。 -
- ServletRequestMethodArgumentResolver用来解析WebRequest、ServletRequest、MultipartRequst、HttpSession、Principal、Locale、TimeZone、InputStream、Reader、HttpMethod等类型参数值,都可以通过request获取。
-
- ServletResponseMethodArgumentResolver用来解析ServletResponse、OutputStream、Writer类型的参数。
-
- RequestAttributeMethodArgumentResolver用来解析@RequestAttribute注解的参数值。@RequestAttribute注解的参数必须是@ModelAttributes注解添加到model中或者在Filter使用servletRequest.setAttribute添加的参数,如果不是,则会报错。
第二大类是解析model中的参数值解析。 这几个类中不只有model参数值的解析,也有返回结果model的设置,所以实现了HandlerMethodReturnValueHandler接口且后缀为Processor。
-
MapMethodProcessor解析Map型参数的参数值,会直接返回mavContainer中的model。还用来处理Map类型的返回值。
-
ModelAttributeMethodProcessor解析注释了@ModelAttribute的方法的参数值,同时处理该方法的返回值。
-
ServletModelAttributeMethodProcessor对2中的父类做了补丁,可以使用ServletRequestDataBinder代替父类中的WebDataBinder进行数据绑定。
第三大类包括了session、重定向和一些补充参数值的解析。
- ExpressionValueMethodArgumentResolver用来解析@Value注解的参数。这个注解在Controller方法参数的作用和在普通bean中的作用一样,都是注入环境property值。
- SessionAttributeMethodArgumentResolver用来解析@SessionAttribute注解的参数。
- SessionStatusMethodArgumentResolver用来解析SessionStatus类型参数,以上两个都是和session有关的解析器。
- RedirectAttributesMethodArgumentResolver用来RedirectAttributes类型的参数,RedirectAttributes可以设置参数用于重定向。
- UriComponentsBuilderMethodArgumentResolver用于解析UriComponentsBuilder类型的参数。
- 解析Errors类型的参数,一般是Errors或者BindingResult,可以从model中获取。
第四大类算不上参数解析器,在官方文档中称之为向后兼容的适配器,包括AbstractWebArgumentResolverAdapter和ServletWebArgumentResolverAdapter两个类。ServletWebArgumentResolverAdapter继承了AbstractWebArgumentResolverAdapter,是最终能被实例化的参数解析器的适配工具。除了向后兼容,ServletWebArgumentResolverAdapter还可以用来作为自定义参数解析器的“注册工具”,详细内容可以参考这篇文章:SpringBoot配置参数解析器。
具体参数值解析器的原理可以期待更详细的代码分析,这里内容已经溢出了,不做深入探讨。
设置返回值
参数值解析后,会使用反射调用method的invoke,完成请求的处理。
return this.getBridgedMethod().invoke(this.getBean(), args);
返回值是Object,需要进行后续的处理。这样结束了
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
的调用,整个处理流程又回到了ServletInvocableHandlerMethod类的invokeAndHandle方法中。ServletInvocableHandlerMethod继承自InvocableHandlerMethod,ServletInvocableHandlerMethod主要做的是返回结果的设置。
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
try {
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
}
throw ex;
}
}
继续invokeAndHandle方法。setResponseStatus(webRequest)根据@ResponseStatus注解设置Response状态,后面的代码根据返回值和应答来判断是否直接返回,不需要returnValueHandler处理。需要处理的返回结果会最终由ServletInvocableHandlerMethod持有的HandlerMethodReturnValueHandlerComposite类对象returnValueHandlers的handleReturnValue方法。看到XXXXComposite,和上文内容相联系,应该明白这个类是一个返回值处理器HandlerMethodReturnValueHandler的复合类,用来管理不同的处理器。
//HandlerMethodReturnValueHandlerComposite.java
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
boolean isAsyncValue = isAsyncReturnValue(value, returnType);
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
continue;
}
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
handleReturnValue方法先调用selectHandler方法选择对应返回值的处理器,具体的实现方式是遍历所有处理器,使用处理器的supportsReturnType方法来判断。获取处理器后,接着使用处理器的handleReturnValue,传入返回值returnValue、返回值类型returnType、Model和View相关的mavContainer、复合请求和应答等信息的webRequest,处理返回值。
HandlerMethodReturnValueHandler接口和HandlerMethodArgumentResolver类似,定义的方法只有两个。
public interface HandlerMethodReturnValueHandler {
boolean supportsReturnType(MethodParameter returnType);
void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}
这两个方法上面已经介绍了,supportsReturnType判断该处理器是否支持此返回值的处理,handleReturnValue用来最终处理返回值。
返回值处理器的分类
上面在介绍参数值解析器时,其实也包括了一些返回值的处理器。以XXXProcessor形式命名的解析器,既实现了HandlerMethodArgumentResolver的接口,也实现了HandlerMethodReturnValueHandler,这种类型的处理器上文已经介绍过了,下面就不再涉及。我对剩下的其他的处理器进行了整理。
- AsyncTaskMethodReturnValueHandler,异步请求处理,负责WebAsyncTask类型的返回值。
- HandlerMethodReturnValueHandlerComposite复合类,用来管理返回值处理器。
- CallableMethodReturnValueHandler,异步请求返回值处理,负责Callable类型的返回值。
- DeferredResultMethodReturnValueHandler,异步请求处理,负责DeferredResult类型。
- HttpHeadersReturnValueHandler,负责HttpHeaders类型的返回值。
- ModelAndViewMethodReturnValueHandler,负责ModelAndView类型的返回值,是传统spring mvc模式使用比较多的处理器,现在流行的RESTful服务模式已经不再需要。
- ModelAndViewResolverMethodReturnValueHandler,处理所有返回值,一般放在最后作为补充。
- ResponseBodyEmitterReturnValueHandler,负责ResponseBodyEmitter、子类SseEmitter和封装在ResponseEntity中的同等类型,非常高级的应用,需要研究一下。
- StreamingResponseBodyReturnValueHandler支持StreamingResponseBody或者ResponseEntity返回值的处理,异步I/O请求返回流管理类型。
- ViewMethodReturnValueHandler,支持View类型返回值的处理。
- ViewNameMethodReturnValueHandler,用于处理void和String类型的返回值,主要就是将返回的String作为viewname设置到mavContainer中。
目前业务中使用最多的是直接返回json数据,而不需要spring mvc处理视图相关的工作,所以使用最多的其实是同为参数值解析器的RequestResponseBodyMethodProcessor处理器。在这个处理器中,会使用Converter进行类型解析,生成json数据,最后使用Response流直接返回http应答。这意味着,spring mvc在这个流程之后的视图解析相关组件,已经不能失去作用,被废弃掉了。除了RequestResponseBodyMethodProcessor需要注意,还有上面提到的一些异步类型的处理器,对于高并发可能有帮助,值得学习。
以上是对HandleAdapter整个原理的简要分析,构造和设置解析器、处理器和设置其他参数的部分省略了,但这对于HandleAdapter如何发挥作用的理解并没有太大影响。