SpringMVC执行流程

从DispatcherServelet的doDispatch方法说起

WebAsyncManager异步管理器

为SpringMVC的异步请求做准备,如果在Controller层返回Callable或DefferdResult,该异步管理器会起作用,否则无用。
具体可查看SpringMVC异步

HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;

WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

设置webAsyncManager到request域中

处理Multipart

springboot上传文件即会被MultipartResolver解析,默认实现为StandarMultipartResolver,如需自定义,只需实现MultipartResolver接口,并注入到容器即可

WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
	ModelAndView mv = null;
	Exception dispatchException = null;

	try {
		processedRequest = checkMultipart(request);
		multipartRequestParsed = (processedRequest != request);

从HandlerMappings匹配Handler

// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
	noHandlerFound(processedRequest, response);
	return;
}

默认情况下,有5个HandlerMapping,一旦匹配成功某一个,立即返回,否则返回null。

也可向容器中注入Bean,使用@Order注解可控制handlerMapping的顺序,如下图(MyhandlerMapping实现了HandlerMapping接口,并且设置bean优先级为最高)
handlerMappings.png

RequestHandlerMapping

优先级最高,通过请求方法、路径,匹配HandlerMethod,即controller中的方法,程序启动后,所有的RequestMapping方法被收集到AbstractHandlerMethodMapping中的mappingRegistry中,如下图
mappingRegistry.png

WelcomePageHandlerMapping

当RequestMappingHandler匹配不到时且路径为index.html,默认会去寻找index.html(static目录下或其他配置路径)并返回,若找不到该文件,报404

BeanNameUrlHandlerMapping

使用bean的名称匹配请求路径,若bean名称以"/"开头,则程序启动后会被加入到待匹配bean集合中
如下为一个demo,该Bean必须被后续的HandlerAdapter匹配到,否则会抛出异常,本demo中实现了HttpRequestHandler接口,该接口对应HttpRequuestHandlerAdapter,也可以自定义HandlerAdapter。

@Component("/beanNameMapping")
public class MyBeanMapping implements HttpRequestHandler {
    @Override
    public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE);
        if (RequestMethod.POST.equals(RequestMethod.valueOf(request.getMethod()))) {
            response.getWriter().print("{\"msg\":\"Method Not Allowed\"}");
        } else {
            response.getWriter().print("{\"msg\":\"succ\"}");
        }
        response.getWriter().flush();
    }
}

当请求路径为/beanNameMapping时,将会被该类处理,这样的操作,有Servlet的味道了。

RouterFunctionMapping

自定义路由解析,自定义request处理,需要向容器中注入RouterFunction,否则该HandlerMapping找不到routerFunction,不起作用。
如下为一个demo。

@Component
public class MyRouterFunction implements RouterFunction<MyRouterFunction.MyServerResponse> {
    @Override
    public Optional<HandlerFunction<MyServerResponse>> route(ServerRequest request) {
        /* 404 */
        if ("/router".equals(request.path())) {
            return Optional.empty();
        }
        return Optional.of(req -> new MyServerResponse());
    }

    static class MyServerResponse implements ServerResponse {

        private final HttpStatus httpStatus = HttpStatus.OK;
        private HttpHeaders httpHeaders = new HttpHeaders();

        @Override
        public HttpStatus statusCode() {
            return this.httpStatus;
        }

        @Override
        public int rawStatusCode() {
            return this.httpStatus.value();
        }

        @Override
        public HttpHeaders headers() {
            return this.httpHeaders;
        }

        @Override
        public MultiValueMap<String, Cookie> cookies() {
            return null;
        }

        @Override
        public ModelAndView writeTo(HttpServletRequest request, HttpServletResponse response, Context context) throws IOException {
            response.getWriter().print("{\"msg\":\"succ\"}");
            return null;
        }
    }
}

像Servlet但又不完全像,升级版Servlet

SimpleUrlHandlerMapping

当前面所有的HandlerMapping都未匹配到handler,返回null后,由该handler进行处理,顾名思义,简单的url匹配,
其实走到这一步,SpringMVC已经认为这次请求大概率是一次静态资源请求,所以,默认情况下,用于匹配的handlerMap中默认有两个ResouceHttpRequestHandler,如下图所示
simpleUrlMapping.png

其中第3个是我实现addResourceHandler方法自己添加的。

  • 当我们的请求不能被前面的handlerMapping解析时,默认会走到该资源处理mapping中。
  • 我们在MvcConfigure中配置的ResourceHandler实际上在像该类的Map集合handlerMap中放入ResourceHttpRequestHandler。

getHandler方法从以上的handlerMapping中匹配handler,返回handlerExecutionChain到doDispatch方法中,整个handlerMapping执行链中,可自定义拦截器
handlerExecutionChain.png

处理器适配器,HandlerAdapter

// Determine handler adapter for the current request.
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

适配器模式,默认有四个处理器适配器,可以自定义Bean注入并指定Order,如下图
handlerAdapters.png

RequestMappingHandlerAdapter

对应RequestMappingHandler

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

HandlerFunctionAdapter

对应RouterFunctionHandlerMapping,上文例子中MyRouterFunction中返回值即为HandlerFunction

public boolean supports(Object handler) {
	return handler instanceof HandlerFunction;
}

HttpRequestHandlerAdapter

所有HttpRequestHandler,其中资源请求(Handler为HttpRequestResourceHandler)被该Adapter匹配到。

public boolean supports(Object handler) {
	return (handler instanceof HttpRequestHandler);
}

SimpleControllerHandlerAdapter

简单匹配Controller,SPI类,更多内容可查看Controller接口

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

所有的HandlerAdapter都是为Handler服务,二者都可以自定义,扩展性很强。

处理HTTP的LastModified

// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
	long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
	if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
		return;
	}
}

代码比较简单,如果资源未改变,不走下面的流程,直接返回

执行拦截器链preHandler

if (!mappedHandler.applyPreHandle(processedRequest, response)) {
	return;
}

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
	for (int i = 0; i < this.interceptorList.size(); i++) {
		HandlerInterceptor interceptor = this.interceptorList.get(i);
		if (!interceptor.preHandle(request, response, this.handler)) {
			triggerAfterCompletion(request, response, null);
			return false;
		}
		this.interceptorIndex = i;
	}
	return true;
}

可以看到,如果有一个拦截器返回不放行的话,后续流程都不会执行

总结

  1. 明确HandlerMapping、HandlerAdapter的作用和关系

    • HandlerMapping决定请求走哪条HandlerExecutionChain,这条链中包含前置处理,HandlerAdapter适配器的Handler真正处理,后置处理。
    • HandlerAdapter适配器模式,支持了Handler的多样性和灵活性。
    • 除默认的四个HandlerMapping外,可以依据需要自定义HandlerMapping和HandlerAdapter,例如Spring Websoket的实现即自定义了WebSocketHandlerMapping。
  2. 增加自定义的HandlerMapping、HandlerAdapter等
    在DispatchServlet中,有以下几个属性,如果配置为true,会从IOC容器中自动检测Bean并配置,如果需要更改为false,需要重写DispatchServlet并配置。

/** Detect all HandlerMappings or just expect "handlerMapping" bean?. */
private boolean detectAllHandlerMappings = true;

/** Detect all HandlerAdapters or just expect "handlerAdapter" bean?. */
private boolean detectAllHandlerAdapters = true;

/** Detect all HandlerExceptionResolvers or just expect "handlerExceptionResolver" bean?. */
private boolean detectAllHandlerExceptionResolvers = true;

/** Detect all ViewResolvers or just expect "viewResolver" bean?. */
private boolean detectAllViewResolvers = true;

/** Throw a NoHandlerFoundException if no Handler was found to process this request? *.*/
private boolean throwExceptionIfNoHandlerFound = false;
  1. 更改默认配置
    实现该接口,并注入到容器即可
public interface WebMvcRegistrations {
	default RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
		return null;
	}

	default RequestMappingHandlerAdapter getRequestMappingHandlerAdapter() {
		return null;
	}

	default ExceptionHandlerExceptionResolver getExceptionHandlerExceptionResolver() {
		return null;
	}

}

Q.E.D.


一切很好,不缺烦恼。