本文分析了DispatcherServlet对于请求路径的分发与处理的主要流程。
由于DispatcherServlet的核心本质是一个Servlet,所以在研究DispatcherServlet处理请求过程之前,先看下Servlet的是怎么接收请求的。
(一)Servlet的执行过程
Tomcat的web容器在接收到http请求时,主调度线程会从事先定义好的线程池中分配一个当前工作线程,将请求分配给当前的工作线程。该线程会创建一个封装了http请求消息的HttpServletRequest对象和一个代表http响应消息的HttpServletResponse对象,根据web.xml文件中的servlet-mapping中的url-pattern确定请求对应的处理Servlet,然后调用Servlet的service()方法,并将请求和响应对象作为参数传递进去。
HttpServlet继承于Servlet,对service方法做了一些预处理:
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException
{
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException("non-HTTP request or response");
}
//具体处理逻辑在这里
service(request, response);
}
HttpServlet中的service方法就不去细看了,无非就是根据request的method分发给不同的方法执行请求。http get方法会调用doGet方法,http post方法调用doPost方法,etc.
(二)FrameworkServlet对请求的预处理
FrameworkServlet继承了HttpServlet,并重写了其中一系列对于请求进行处理的方法,如
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
主要逻辑在processRequest方法中,下面对其关键操作做了注释:
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
//从当前请求线程本地变量中获取LocaleContext
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
//构造新的LocaleContext
LocaleContext localeContext = buildLocaleContext(request);
//从当前请求线程本地变量中获取ServletRequestAttributes
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
//构造新的ServletRequestAttributes
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
//将新建的LocaleContext与ServletRequestAttributes设置进线程本地变量
initContextHolders(request, localeContext, requestAttributes);
try {
//抽象方法,由子类DispatcherServlet实现
doService(request, response);
}
catch (ServletException ex) {
failureCause = ex;
throw ex;
}
catch (IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}
finally {
//重置LocaleContext与ServletRequestAttributes对象,解除与请求线程的绑定
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
if (logger.isDebugEnabled()) {
if (failureCause != null) {
this.logger.debug("Could not complete request", failureCause);
}
else {
if (asyncManager.isConcurrentHandlingStarted()) {
logger.debug("Leaving response open for concurrent processing");
}
else {
this.logger.debug("Successfully completed request");
}
}
}
//发布RequestHandledEvent事件,通知监听器
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
(三)DispatcherServlet处理请求过程
DispatcherServlet继承了FrameworkServlet,并实现了其中的抽象方法doService,是处理请求的具体实现。其中最关键的处理逻辑就是doDispatch方法。doDispatch主要分为以下几个部分:
- 根据请求的路径找到用于处理请求的HandlerExecutionChain;
//使用配置中的multipartResolver预处理多媒体请求,把request转换为MultipartHttpServletRequest
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 通过getHandler方法决定处理请求的handler(具体逻辑在下一节)
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
//如果找不到handler,则会报404错误
noHandlerFound(processedRequest, response);
return;
}
- 获取HandlerExecutionChain对应的HandlerAdapter;
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
- 获取当前请求的最后更改时间,判断浏览器是否可以直接使用之前缓存的结果,如果缓存未过期,直接返回304状态码;
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;
}
}
4.按顺序调用拦截器的preHandle方法,全部通过以后,使用对应的HandlerAdapter处理HandlerExecutionChain,得到ModelAndView对象;
//调用interceptor链预处理请求,若不通过,直接返回
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 调用handle方法得到ModelAndView对象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
applyDefaultViewName(processedRequest, mv);
//在渲染视图之前,按顺序调用interceptor链的postHandle请求
mappedHandler.applyPostHandle(processedRequest, response, mv);
//使用ViewResolver渲染视图
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
(四)路径映射查找
下面进入到DispatcherServlet的getHandler方法中,分析一下路径对应的HandlerExecutionChain是怎么获得的。
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
上一篇提到过,DispatcherServlet在进行初始化的时候,会从springmvc容器中将handlerMappings实例取出,通过order进行排序后设置进属性中。
在getHandler方法中,DispatcherServlet遍历初始化过程中已经装载好的handlerMappings,按顺序查找路径对应的处理器。当上一个HandlerMapping查找不到的时候才会由下一个HandlerMapping处理。
RequestMappingHandlerMapping优先级最高,用于查找@Controller与@RequestMapping注解对应的处理器。从它入手看看具体是怎么处理的吧~
RequestMappingHandlerMapping继承了AbstractHandlerMapping,其中的getHandler主要分为两步:
@Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//1、通过子类实现获取handler
Object handler = getHandlerInternal(request);
//省略部分代码
//2、根据请求与handler构建HandlerExecutionChain
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
//省略部分代码
return executionChain;
}
- AbstractHandlerMethodMapping实现了AbstractHandlerMapping中的getHandlerInternal抽象方法,最终实现了handler的查找,主要代码如下:
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<Match>();
//以lookupPath作为key,mappingRegistry.urlLookup中获取匹配的RequestMappingInfo
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
//若通过url能直接查找到,则在对RequestMappingInfo与request中相关属性进行比对后,
将符合的RequestMappingInfo与HandlerMethod对象包装为Match对象,放到matches中
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
//没有找到的情况下只能遍历mappingRegistry.mappingLookup中的所有RequestMappingInfo实例
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
if (!matches.isEmpty()) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
//若有多个匹配项,将它们排序
Collections.sort(matches, comparator);
//获取匹配率最高的
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
}
}
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
else {
//细化错误提示,如请求方法不适配,媒体类型不适配等
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
- 在获得handler后,通过getHandlerExecutionChain方法将拦截器添加上去:
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
//如果是AbstractUrlHandlerMapping的getHandlerInternal,可能返回HandlerExecutionChain对象
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
//adaptedInterceptors是在HandlerMapping初始化时注入进来的
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
//根据mvc中interceptor的mapping配置,添加符合路径的拦截器
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}
(五)处理请求
HandlerMapping的作用主要是根据request请求获取能够处理当前request的handler,而HandlerAdapter的作用在于将request中的各个属性,如request param适配为handler能够处理的形式。HandlerMethod类型的handler由RequestMappingHandlerAdapter进行处理。
@Override
public final boolean supports(Object handler) {
// 判断当前handler是否为HandlerMethod类型,并且判断supportsInternal()方法返回值是否为true,
// 这里supportsInternal()方法是提供给子类实现的一个方法,对于RequestMappingHandlerAdapter而言,其返回值始终是true
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
其中最主要的处理逻辑在invokeHandlerMethod方法中,具体如下:
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
// 获取容器中全局配置的InitBinder和当前HandlerMethod所对应的Controller中
// 配置的InitBinder,用于进行参数的绑定
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
// 获取容器中全局配置的ModelAttribute和当前当前HandlerMethod所对应的Controller
// 中配置的ModelAttribute,这些配置的方法将会在目标方法调用之前进行调用
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
//将HandlerMethod封装为ServletInvocableHandlerMethod
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
//注入@SessionAttributes声明的参数,并在在目标Handler调用之前调用@ModelAttribute标注的方法
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
//省略部分代码
//使用handler处理请求,并将返回值封装为一个ModelAndView对象
invocableMethod.invokeAndHandle(webRequest, mavContainer);
return getModelAndView(mavContainer, modelFactory, webRequest);
}
至此,一个http请求处理完毕,返回DispatcherServlet中执行后续收尾操作。