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优先级为最高)
RequestHandlerMapping
优先级最高,通过请求方法、路径,匹配HandlerMethod,即controller中的方法,程序启动后,所有的RequestMapping方法被收集到AbstractHandlerMethodMapping中的mappingRegistry中,如下图
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,如下图所示
其中第3个是我实现addResourceHandler方法自己添加的。
- 当我们的请求不能被前面的handlerMapping解析时,默认会走到该资源处理mapping中。
- 我们在MvcConfigure中配置的ResourceHandler实际上在像该类的Map集合handlerMap中放入ResourceHttpRequestHandler。
getHandler方法从以上的handlerMapping中匹配handler,返回handlerExecutionChain到doDispatch方法中,整个handlerMapping执行链中,可自定义拦截器
处理器适配器,HandlerAdapter
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
适配器模式,默认有四个处理器适配器,可以自定义Bean注入并指定Order,如下图
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;
}
可以看到,如果有一个拦截器返回不放行的话,后续流程都不会执行
总结
-
明确HandlerMapping、HandlerAdapter的作用和关系
- HandlerMapping决定请求走哪条HandlerExecutionChain,这条链中包含前置处理,HandlerAdapter适配器的Handler真正处理,后置处理。
- HandlerAdapter适配器模式,支持了Handler的多样性和灵活性。
- 除默认的四个HandlerMapping外,可以依据需要自定义HandlerMapping和HandlerAdapter,例如Spring Websoket的实现即自定义了WebSocketHandlerMapping。
-
增加自定义的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;
- 更改默认配置
实现该接口,并注入到容器即可
public interface WebMvcRegistrations {
default RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return null;
}
default RequestMappingHandlerAdapter getRequestMappingHandlerAdapter() {
return null;
}
default ExceptionHandlerExceptionResolver getExceptionHandlerExceptionResolver() {
return null;
}
}