第10章-SpringMVC中的HandlerAdapter源码解析

测试类

HelloController

package cn.imlql.web.controller;

@Controller
public class HelloController {

public HelloController() {
System.out.println("HelloController.....");
}

@Autowired
HelloService helloService;

@GetMapping("/hello")
public String sayHello(String name,
@RequestParam("user") String user,
HttpSession session) {
String mvc = helloService.say(user + ":MVC" + name);
session.setAttribute("msg", mvc);
return "index.jsp";
}
}

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
$END$
结果: <h1 style="color: red">${sessionScope.msg}</h1>
</body>
</html>

其余的类和前面一样,不列举了。

BeanNameUrlHandlerMapping简介

public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {

/**
* Checks name and aliases of the given bean for URLs, starting with "/".
*/
@Override //决定使用哪个url注册
protected String[] determineUrlsForHandler(String beanName) {
List<String> urls = new ArrayList<>();
if (beanName.startsWith("/")) {
urls.add(beanName);
}
String[] aliases = obtainApplicationContext().getAliases(beanName);
for (String alias : aliases) {
if (alias.startsWith("/")) {
urls.add(alias);
}
}
return StringUtils.toStringArray(urls);
}

}
  1. 很简单,意思就是只要你的BeanName是以/开头,就会将这个Bean封装成一个BeanNameUrlHandlerMapping映射。
  2. 具体路径怎么写呢?你可以在你的类上写一个@Controller("/helloReq") ,这样就有路径了。代表的意思就是/helloReq这个URL和由哪个handler来处理的映射关系被保存在了BeanNameUrlHandlerMapping里
  3. 如果不懂的话可以待会看下面的例子

handler就是咱们常说的XXXController

HandlerAdapter概述

  1. 上面我们从HandlerMapping这个映射中心找到了由哪个Controller执行哪个方法
  2. 按照我们最简单的想法就是直接method.invoke()反射执行
  3. 但是实际上我们要考虑
    1. 是哪个对象执行方法?
    2. 方法里的参数有几个?
    3. 参数类型是什么?参数类型大概率有我们自己写的类,怎么处理
    4. 多个参数我们还要一个一个赋值。
    5. 怎么返回?是直接返回值,还是跳转页面,等等
  4. 其实是比较复杂的,springmvc就写了一个HandlerAdapter来处理这些复杂的逻辑

还是跟HandlerMapping一样,继续Debug DispatcherServlet#doDispatch(HttpServletRequest, HttpServletResponse)

浏览器输入:http://localhost:8080/springmvc_source_test/hello?name=zhangsan&user=haha开始测试

DispatcherServlet#doDispatch()请求派发

//SpringMVC处理请求的核心流程
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null; //handler(目标方法)的执行链
boolean multipartRequestParsed = false; //文件上传标志
//对异步请求的支持(Servlet3.0以后才有的,Webflux)
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

try {
ModelAndView mv = null;
Exception dispatchException = null;

try {
processedRequest = checkMultipart(request); //检查当前是否文件上传请求
multipartRequestParsed = (processedRequest != request);

//构造出了【目标方法+拦截器整个链路】决定使用哪个Handler处理当前请求 Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) { //如果找不到人处理,就send 404
noHandlerFound(processedRequest, response);
return;
}

//适配器怎么找的、 Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//所有拦截器的 preHandle 执行
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return; //使用 mappedHandler整个链
}

//真正来执行目标方法 Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

if (asyncManager.isConcurrentHandlingStarted()) {
return;
}

applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
} //处理结果
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}

F7进入getHandlerAdapter(mappedHandler.getHandler())

DispatcherServlet#getHandlerAdapter()

上面几个默认的适配器还是在DispatcherServlet.properties配置的4个默认适配器,然后在初始化九大组件的时候放到容器里

HttpRequestHandlerAdapter#supports()

@Override //想让他工作,实现HttpRequestHandler接口即可
public boolean supports(Object handler) { //写一个HttpRequestHandler的实现也能处理请求
return (handler instanceof HttpRequestHandler);
}

这里判断当前handler【就是咱们写的Controller】是否实现了HttpRequestHandler接口,是的话就直接返回这个Adapter,不往下走了

SimpleControllerHandlerAdapter#supports()

@Override
public boolean supports(Object handler) {
return (handler instanceof Controller);
}

同理,这里判断当前handler是否实现了Controller接口,是的话就直接返回这个Adapter,不往下走了

RequestMappingHandlerAdapter

RequestMappingHandlerAdapter自己没有写supports方法,它用的是父类的AbstractHandlerMethodAdapter的

AbstractHandlerMethodAdapter#supports()

public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}

这个就是判断当前handler是不是HandlerMethod类型的,前面大致讲过只要标注了@RequestMapping注解的handler最终都会被封装成HandlerMethod

RequestMappingHandlerAdapter#supportsInternal()

protected boolean supportsInternal(HandlerMethod handlerMethod) {
return true;
}

DispatcherServlet#doDispatch()

举例说明HttpRequestHandlerAdapter和SimpleControllerHandlerAdapter如何与BeanNameUrlHandlerMapping联动

测试类-HelloHttpRequestHandler

@Controller("/helloReq") //BeanNameUrlHandlerMapping 就会把他注册进去
public class HelloHttpRequestHandler implements HttpRequestHandler {
//启用 HttpRequestHandlerAdapter

//处理请求
@Override
public void handleRequest(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
response.getWriter().write("HelloHttpRequestHandler....");
}
}

@Controller("/helloReq")里的/helloReq即是BeanName也是URL。这里就是之前说的BeanName以/为开头的Bean,它的URL和handler的映射关系会被保存在BeanNameUrlHandlerMapping里,如下图

DispatcherServlet#getHandlerAdapter()

因为我们的HelloHttpRequestHandler正好实现了HttpRequestHandler,所以就会直接返回HttpRequestHandlerAdapter

DispatcherServlet#doDispatch()

我们直接来到准备执行的地方

F7进入ha.handle(processedRequest, response, mappedHandler.getHandler())

HttpRequestHandlerAdapter#handle()

直接来到了这里,接着就调用我们自定义的HelloHttpRequestHandler的handleRequest方法。

测试类-HelloSimpleController

//@RequestMapping("/ssss")
@Controller("/helloSimple")
public class HelloSimpleController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
return null;
}
}

实现Controller接口的就是SimpleControllerHandlerAdapter来处理,原理和HttpRequestHandlerAdapter几乎一样。

小总结

  1. 很显然BeanNameUrlHandlerMapping和HttpRequestHandlerAdapter的结合,一个类只能处理一个URL路径的请求。并且这里的参数处理都很麻烦,不像@RequestMapping那么方便
  2. 远不如RequestMappingHandlerMapping和RequestMappingHandlerAdapter的结合来的方便。它两结合后,一个类里可以写多个@RequestMapping注解标注的方法,一个类里就可以处理多个URL请求,并且处理参数和返回值都很方便。
  3. 所以下面就重点讲RequestMappingHandlerAdapter

RequestMappingHandlerAdapter中的参数解析器、返回值处理器=>概述

DispatcherServlet#doDispatch()

AbstractHandlerMethodAdapter#handle()

public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {

return handleInternal(request, response, (HandlerMethod) handler);
}

AbstractHandlerMethodAdapter是RequestMappingHandlerAdapter的父类,之前说过

RequestMappingHandlerAdapter#handleInternal()

   protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

ModelAndView mav;
checkRequest(request);

// 会话锁,每一个用户和服务器交互无论发了多少请求都只有一个会话,限制用户的线程 Execute invokeHandlerMethod in synchronized block if required.
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) { //高并发可以限制一个用户一次进来一个请求
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
//执行目标方法 No synchronization on session demanded at all...
mav = invokeHandlerMethod(request, response, handlerMethod);
}

if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}

return mav;
}

//session锁这个东西不适用于高并发场景,所以spring默认是不适用它,应该是可以开启的,具体怎么开启我没研究,可以看官方文档
private boolean synchronizeOnSession = false;

RequestMappingHandlerAdapter#invokeHandlerMethod( )

/**
* Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
* if view resolution is required.
* @since 4.2
* @see #createInvocableHandlerMethod(HandlerMethod)
*/
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
//把原生的request,response封装到一个对象中方便后续只用这一个参数就行【装饰器模式】
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try { //数据绑定器
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
//获取到模型工厂 Model(要交给页面的数据) View(我们要去的 视图),视图可以理解为页面或者图片等等
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

// 这里就是做了一些增强,比如我们可以直接通过ServletInvocableHandlerMethod拿到返回值,可以很方便的拿到其它我们需要的信息
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) { //参数解析器
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) { //返回值解析器
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
//以上的 几个核心组件都挺重要的 ModelAndViewContainer是以后处理过程中产生的ModelAndView数据临时存储容器
// 也是整个请求处理流程,线程共享数据
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
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);
}
//真正开始执行目标方法
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}

return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}

什么是WebDataBinderFactory?

public String sayHello(Person person){ 
//...
}
  1. 假设方法里有一个Person类型的参数,Person里的属性就是age,name,sex这些的
  2. 前端传的参数名刚好能和Person的属性对上,WebDataBinder就会帮我们自动绑定上。

argumentResolvers参数解析器

注意有一些解析器,比如RequestResponseBodyMethodProcessor即是返回值解析器也是参数解析器

returnValueHandlers返回值解析器

15个参数解析器和27个返回值解析器是什么时候有值的?

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
implements BeanFactoryAware, InitializingBean {
// ...
}

我们可以看到RequestMappingHandlerAdapter也是实现了InitializingBean接口,所以它什么时候有值,和RequestMappingHandlerMapping是一样的逻辑

  1. DispatcherServlet#onRefresh()开始初始化九大组件,就会开始初始化HandlerAdapter
  2. 首先是创建DispatcherServlet.properties里指定的四个HandlerAdapter实现类的对象。还是用createBean来创建HandlerAdapter的
  3. 其中RequestMappingHandlerAdapter创建完对象后,因为它实现了InitializingBean,所以会调用RequestMappingHandlerAdapter#afterPropertiesSet()
  4. 在afterPropertiesSet()中就直接new了所有默认的参数解析器和返回值解析器

RequestMappingHandlerAdapter#afterPropertiesSet()

public void afterPropertiesSet() { //初始化以后
// Do this first, it may add ResponseBody advice beans
initControllerAdviceCache(); //初始化 ControllerAdvice 【异常处理相关的功能】

if (this.argumentResolvers == null) { //拿到底层所有的默认 argumentResolvers
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers); //把这些resolver统一组合到一个对象里面,方便管控
}
if (this.initBinderArgumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}

RequestMappingHandlerAdapter#getDefaultArgumentResolvers()

private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(30);

// Annotation-based argument resolution
//这里放了一个RequestParamMethodArgumentResolver,但是它的第二个参数是false,第二个参数是什么呢?我们后面说它的时候再讲
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
resolvers.add(new RequestParamMapMethodArgumentResolver());
resolvers.add(new PathVariableMethodArgumentResolver());
resolvers.add(new PathVariableMapMethodArgumentResolver());
resolvers.add(new MatrixVariableMethodArgumentResolver());
resolvers.add(new MatrixVariableMapMethodArgumentResolver());
resolvers.add(new ServletModelAttributeMethodProcessor(false));
resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
resolvers.add(new RequestHeaderMapMethodArgumentResolver());
resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new SessionAttributeMethodArgumentResolver());
resolvers.add(new RequestAttributeMethodArgumentResolver());

// Type-based argument resolution
resolvers.add(new ServletRequestMethodArgumentResolver());
resolvers.add(new ServletResponseMethodArgumentResolver());
resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RedirectAttributesMethodArgumentResolver());
resolvers.add(new ModelMethodProcessor());
resolvers.add(new MapMethodProcessor());
resolvers.add(new ErrorsMethodArgumentResolver());
resolvers.add(new SessionStatusMethodArgumentResolver());
resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
if (KotlinDetector.isKotlinPresent()) {
resolvers.add(new ContinuationHandlerMethodArgumentResolver());
}

// Custom arguments
if (getCustomArgumentResolvers() != null) {
resolvers.addAll(getCustomArgumentResolvers());
}

// Catch-all
resolvers.add(new PrincipalMethodArgumentResolver());
//这里也放了一个RequestParamMethodArgumentResolver,但是它的第二个参数是true
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
resolvers.add(new ServletModelAttributeMethodProcessor(true));

return resolvers;
}

RequestMappingHandlerAdapter#getDefaultReturnValueHandlers()

private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(20);

// Single-purpose return value types
handlers.add(new ModelAndViewMethodReturnValueHandler());
handlers.add(new ModelMethodProcessor());
handlers.add(new ViewMethodReturnValueHandler());
handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(),
this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager));
handlers.add(new StreamingResponseBodyReturnValueHandler());
handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
this.contentNegotiationManager, this.requestResponseBodyAdvice));
handlers.add(new HttpHeadersReturnValueHandler());
handlers.add(new CallableMethodReturnValueHandler());
handlers.add(new DeferredResultMethodReturnValueHandler());
handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));

// Annotation-based return value types
handlers.add(new ServletModelAttributeMethodProcessor(false));
handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
this.contentNegotiationManager, this.requestResponseBodyAdvice));

// Multi-purpose return value types
handlers.add(new ViewNameMethodReturnValueHandler());
handlers.add(new MapMethodProcessor());

// Custom return value types
if (getCustomReturnValueHandlers() != null) {
handlers.addAll(getCustomReturnValueHandlers());
}

// Catch-all
if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
}
else {
handlers.add(new ServletModelAttributeMethodProcessor(true));
}

return handlers;
}

RequestMappingHandlerAdapter中的参数解析器工作流程

DispatcherServlet#doDispatch()

AbstractHandlerMethodAdapter#handle(HttpServletRequest , HttpServletResponse , Object )RequestMappingHandlerAdapter#handleInternal(HttpServletRequest , HttpServletResponse , HandlerMethod )上面说过,跳过

RequestMappingHandlerAdapter#invokeHandlerMethod()准备执行目标方法

ServletInvocableHandlerMethod#invokeAndHandle()真正开始执行目标方法

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()) {
disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}

mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}

InvocableHandlerMethod#invokeForRequest( )目标方法的反射执行过程

public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//获取方法的请求参数
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
return doInvoke(args); //就是反射执行
}

开始进入正题

InvocableHandlerMethod#getMethodArgumentValues()获取方法的请求参数

protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//直接拿到方法的所有参数,getMethodParameters是HandlerMethod类的,在之前构造handler的时候就已经通过反射将相关信息保存好了
// 我们之前讲过标注@RequestMapping注解的handler最终会被封装成HandlerMethod
MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}
//准备args数组(和parameters一样长),挨个确定每个参数都是什么值
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) {
continue;
}
if (!this.resolvers.supportsParameter(parameter)) { //支持这种参数的解析器也会被放到缓存,下一次进来,就不用27个人挨个判断
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
// Leave stack trace for later, exception may actually be resolved and handled...
if (logger.isDebugEnabled()) {
String exMsg = ex.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw ex;
}
}
return args;
}

HandlerMethodArgumentResolverComposite准备循环27个参数解析器

HandlerMethodArgumentResolverComposite#supportsParameter()

public boolean supportsParameter(MethodParameter parameter) {
return getArgumentResolver(parameter) != null;
}

HandlerMethodArgumentResolverComposite#getArgumentResolver()循环判断哪个参数解析器支持这个参数

private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter); //先看缓存中有没有
if (result == null) {
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
if (resolver.supportsParameter(parameter)) {
result = resolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}

这里就是循环27个参数解析器,看哪一个能解析这种类型的参数。因为参数解析器太多,我们就举几个常用的例子

RequestParamMethodArgumentResolver判断@RequestParam注解的参数解析器

package org.springframework.web.method.annotation


public boolean supportsParameter(MethodParameter parameter) {
if (parameter.hasParameterAnnotation(RequestParam.class)) {
//注意这里是为了不跟下面的RequestParamMapMethodArgumentResolver产生冲突
if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
return (requestParam != null && StringUtils.hasText(requestParam.name()));
}
else {
return true;
}
}
else {
if (parameter.hasParameterAnnotation(RequestPart.class)) {
return false;
}
parameter = parameter.nestedIfOptional();
if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {
return true;
}
else if (this.useDefaultResolution) {
return BeanUtils.isSimpleProperty(parameter.getNestedParameterType());
}
else {
return false;
}
}
}

这个就是判断参数有没有标@RequestParam注解

注意这个是org.springframework.web.method.annotation包下的

  1. 第一次进入RequestParamMethodArgumentResolver#supportsParameter(MethodParameter parameter)时,我们在前面讲过,在RequestMappingHandlerAdapter#getDefaultArgumentResolvers()添加默认解析器的时候,spring往参数解析器里加了两个
//这里放了一个RequestParamMethodArgumentResolver,但是它的第二个参数是false,第二个参数是什么呢?我们后面说它的时候再讲
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));

//这里也放了一个RequestParamMethodArgumentResolver,但是它的第二个参数是true
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
  1. 这里的true和false指定的就是useDefaultResolution这个东西

  2. 然后我们验证一下第二个RequestParamMethodArgumentResolver

  1. useDefaultResolution这个看名字应该就是前面25个处理器都用不到的时候,就用这个默认中的默认处理器第26个RequestParamMethodArgumentResolver(双重默认)
  2. 那什么时候用第27个ServletModelAttributeMethodProcessor这个处理器呢?当第26个在上面那几个if else中返回false,就会用最后这个,至于什么时候返回false,我没有深入研究。

RequestParamMapMethodArgumentResolver参数解析器

@RequestParam Map<String,Object> params

public boolean supportsParameter(MethodParameter parameter) {
RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
return (requestParam != null && Map.class.isAssignableFrom(parameter.getParameterType()) &&
!StringUtils.hasText(requestParam.name()));
}

判断有没有标@RequestParam注解,并且类型是Map

PathVariableMethodArgumentResolver判断@PathVariable注解的参数解析器

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注解

返回到InvocableHandlerMethod#getMethodArgumentValues( )

具体的参数赋值过程,就是一些数据类型的转换,可以自己去看下。

args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);

返回到InvocableHandlerMethod#invokeForRequest()

接下来就是交给反射去执行

返回到ServletInvocableHandlerMethod#invokeAndHandle()

拿到返回值了,接下来交给返回值处理器

SpringMVC到底能写哪些参数?

详见官方文档-参数相关

RequestMappingHandlerAdapter中的返回值解析器工作流程

@Controller
public class HelloController {

public HelloController() {
System.out.println("HelloController.....");
}

@Autowired
HelloService helloService;

@GetMapping("/hello")
public String sayHello(String name,
@RequestParam("user") String user,
HttpSession session, HttpServletRequest request, //原生的session对象
@RequestHeader("User-Agent") String ua) { //@RequestParam Map<String,Object> params:所有请求参数全封装进来
// @RequestHeader("User-Agent") String ua 获取指定请求头的值
String header = request.getHeader("User-Agent");
//方法的签名,到底能写那些?
//详细参照 https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-arguments
//https://www.bilibili.com/video/BV19K4y1L7MT?p=32
String mvc = helloService.say(user + ":MVC" + name);
session.setAttribute("msg", mvc);


//SpringMVC的目标方法能写哪些返回值
//https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-return-types
return "index.jsp";
}
}

ServletInvocableHandlerMethod#invokeAndHandle()

我们可以看到RequestResponseBodyMethodProcessor比ViewNameMethodReturnValueHandler优先级高,接下来我们细讲

HandlerMethodReturnValueHandlerComposite#handleReturnValue()

@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);
}

HandlerMethodReturnValueHandlerComposite#selectHandler()循环15个返回值处理器找到合适的返回值处理器

private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
boolean isAsyncValue = isAsyncReturnValue(value, returnType);
//还是老样子for循环
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
continue;
}
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}

我们就直接放行,看最终找到的是哪个

ViewNameMethodReturnValueHandler#supportsReturnType()

public boolean supportsReturnType(MethodParameter returnType) {
Class<?> paramType = returnType.getParameterType();
//返回值是void,或者字符串
return (void.class == paramType || CharSequence.class.isAssignableFrom(paramType));
}

返回到HandlerMethodReturnValueHandlerComposite#handleReturnValue()

ViewNameMethodReturnValueHandler#handleReturnValue()开始处理返回值

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

if (returnValue instanceof CharSequence) { //只要是字符串就是跳转到的页面地址
String viewName = returnValue.toString();
mavContainer.setViewName(viewName);
if (isRedirectViewName(viewName)) { //是否是重定向的方式 redirect:
mavContainer.setRedirectModelScenario(true);
}
}
else if (returnValue != null) {
// should not happen
throw new UnsupportedOperationException("Unexpected return type: " +
returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
}
}

然后就准备返回了

返回到RequestMappingHandlerAdapter#invokeHandlerMethod()

RequestMappingHandlerAdapter#getModelAndView()进行视图解析相关工作

private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
//modelFactory准备模型数据 (请求域数据共享)session里面的数据搬家到request域
modelFactory.updateModel(webRequest, mavContainer);
if (mavContainer.isRequestHandled()) {
return null;
}
ModelMap model = mavContainer.getModel();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request != null) { //重定向数据的共享,RedirectView。先把数据移到request,再把request移到session
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
}
return mav;
}

注意如果你返回值写index而不是index.jsp,它会给你报404。意思就是它不会给我们加jsp后缀。

返回到DispatcherServlet#doDispatch()

DispatcherServlet#applyDefaultViewName()

   private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception {
if (mv != null && !mv.hasView()) { //如果没有指定跳转的页面
String defaultViewName = getDefaultViewName(request); //给一个默认页面
if (defaultViewName != null) {
mv.setViewName(defaultViewName);
}
}
}


protected String getDefaultViewName(HttpServletRequest request) throws Exception {
return (this.viewNameTranslator != null ? this.viewNameTranslator.getViewName(request) : null);
}

@Nullable //把请求转成视图名(我们要跳转的页面地址)的翻译器【没啥用】
private RequestToViewNameTranslator viewNameTranslator;

这里不是重点,我直接告诉你结果吧,默认页面就是把request的请求路径直接拿来当要去的页面地址 。

比如你的请求路径是@GetMapping("/hello.html"),但是你返回值写的是void,那么它就会给你返回到hello.html页面

返回到DispatcherServlet#doDispatch()

来到拦截器的后置处理环节

然后来到处理结果环节

springmvc能写哪些返回值

官方文档:https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-return-types

视图解析器解析流程

DispatcherServlet#processDispatchResult()处理返回值

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {

boolean errorView = false;

if (exception != null) { //如果有异常处理异常
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();//即使有异常,这里也会返回ModelAndView
}
else { //定义无数种异常解析器就会得到不同的异常解析效果
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}

// 动态策略。 Did the handler return a view to render? 为啥?@ResponseBody(提前在解析返回值的时候,就已经把数据写出去了,所以这一步就没有了)
if (mv != null && !mv.wasCleared()) {
render(mv, request, response); //渲染ModeAndView,来解析模型和视图;最终决定响应效果
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("No view rendering, null ModelAndView returned.");
}
}

if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}

if (mappedHandler != null) {
// Exception (if any) is already handled..
mappedHandler.triggerAfterCompletion(request, response, null);
}
}

进入此方法

DispatcherServlet#render()渲染ModelAndView

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
Locale locale =
(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);

View view;
String viewName = mv.getViewName();
if (viewName != null) {
// We need to resolve the view name. 关键还是这里
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}

// Delegate to the View object for rendering.
if (logger.isTraceEnabled()) {
logger.trace("Rendering view [" + view + "] ");
}
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "]", ex);
}
throw ex;
}
}

DispatcherServlet#resolveViewName()

protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
Locale locale, HttpServletRequest request) throws Exception {

if (this.viewResolvers != null) {
//一样的for循环视图解析器
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
}
return null;
}

不过默认的视图解析器只有一个

这里没有拼接前缀和后缀,所以我们必须要自己写.jsp或者.html

RequestResponseBodyMethodProcessor即是返回值解析器也是参数解析器

   @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));
}

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);
if (arg != null) {
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
}
}
if (mavContainer != null) {
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
}
}

return adaptArgumentIfNecessary(arg, parameter);
}

@Override //如果返回值标注了 @ResponseBody注解。会被这个人拦截处理器
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);

// Try even with null return value. ResponseBodyAdvice could get involved.
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}

由于现在的前后端分离时代,我们直接返回视图的需求已经很少很少了。现在一般都是直接返回数据,所以视图解析器详细原理不再细述,后面就直接开始讲异常处理流程。