前言
DispatcherServlet 是 Spring MVC 的核心类,它本质是一个 Servlet,负责接管 HTTP 请求并把它分发给对应的处理器处理,最后处理响应结果渲染页面。
DispatcherServlet 本身并不复杂,它提供了一个模板方法doDispatch()
来处理请求,把请求处理的细节交给了依赖的其它组件。
本文先分析 DispatcherServlet 的初始化流程,再分析请求的整体处理流程,至于请求处理中涉及到的其它组件,会另起篇幅。
初始化
首先看一下 DispatcherServlet 的类图:
DispatcherServlet 本质是一个 Servlet,所以它实现了 Servlet 接口,也就有对应的 Servlet 生命周期方法,在 Servlet 容器启动时,会触发其init()
生命周期方法。
HttpServletBean#init
会基于 ServletConfig 配置初始化 bean,Spring 会使用 BeanWrapper 操作 bean 属性,最后调用initServletBean()
初始化子类。
public final void init() throws ServletException {PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);if (!pvs.isEmpty()) {try {BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));initBeanWrapper(bw);bw.setPropertyValues(pvs, true);}catch (BeansException ex) {if (logger.isErrorEnabled()) {logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);}throw ex;}}initServletBean();
}
FrameworkServlet#initServletBean
的核心是初始化 WebApplicationContext,然后预留了一个钩子函数initFrameworkServlet()
给子类扩展。
protected final void initServletBean() throws ServletException {this.webApplicationContext = initWebApplicationContext();initFrameworkServlet();
}
initWebApplicationContext()
用于初始化 Web 容器上下文对象,Spring 首先会在 ServletContext 查找根容器,然后判断 DispatcherServlet 实例化时是否设置上下文对象,如果没有设置则基于ContextClass
反射创建上下文对象。
protected WebApplicationContext initWebApplicationContext() {WebApplicationContext rootContext =WebApplicationContextUtils.getWebApplicationContext(getServletContext());WebApplicationContext wac = null;if (this.webApplicationContext != null) {wac = this.webApplicationContext;if (wac instanceof ConfigurableWebApplicationContext) {ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;if (!cwac.isActive()) {if (cwac.getParent() == null) {cwac.setParent(rootContext);}configureAndRefreshWebApplicationContext(cwac);}}}if (wac == null) {wac = findWebApplicationContext();}if (wac == null) {wac = createWebApplicationContext(rootContext);}if (!this.refreshEventReceived) {synchronized (this.onRefreshMonitor) {onRefresh(wac);}}if (this.publishContext) {String attrName = getServletContextAttributeName();getServletContext().setAttribute(attrName, wac);}return wac;
}
上下文对象初始化完成后,最后会触发onRefresh()
模板方法完成子类的初始化。对于 DispatcherServlet,主要是初始化一些依赖的组件类,比如:文件上传解析器、处理器映射器、异常处理器、视图解析器等等。
protected void onRefresh(ApplicationContext context) {initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {// 多文件上传解析器initMultipartResolver(context);// 国际化支持initLocaleResolver(context);// 主题解析器initThemeResolver(context);// 处理器映射器initHandlerMappings(context);// 处理器适配器initHandlerAdapters(context);// 异常处理解析器initHandlerExceptionResolvers(context);// 请求->视图名 转换initRequestToViewNameTranslator(context);// 视图解析器initViewResolvers(context);// 重定向参数传递initFlashMapManager(context);
}
组件初始化的逻辑都是一致的,从下文容器查找对应的 Bean、排序并组装 List。
以initHandlerMappings()
为例:
private void initHandlerMappings(ApplicationContext context) {this.handlerMappings = null;// 是否要在祖先容器检测所有的HandlerMappingif (this.detectAllHandlerMappings) {Map<String, HandlerMapping> matchingBeans =BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);if (!matchingBeans.isEmpty()) {this.handlerMappings = new ArrayList<>(matchingBeans.values());// 排序AnnotationAwareOrderComparator.sort(this.handlerMappings);}}else {try {HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);this.handlerMappings = Collections.singletonList(hm);}catch (NoSuchBeanDefinitionException ex) {// Ignore, we'll add a default HandlerMapping later.}}// 如果一个都没找到,获取默认策略if (this.handlerMappings == null) {this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);if (logger.isTraceEnabled()) {logger.trace("No HandlerMappings declared for servlet '" + getServletName() +"': using default strategies from DispatcherServlet.properties");}}for (HandlerMapping mapping : this.handlerMappings) {if (mapping.usesPathPatterns()) {this.parseRequestPath = true;break;}}
}
至此,DispatcherServlet 就初始化完成了。简单总结一下,因为 DispatcherServlet 本质是一个 Servlet,所以 Servlet 容器启动时会触发它的init()
生命周期方法完成初始化。初始化最重要的两个动作,一个是初始化 Web 上下文容器对象、一个是初始化 DispatcherServlet 依赖的各个组件。
处理请求
Servlet 职责是处理请求,Servlet 容器在接收到请求后会调用Servlet#service
以响应请求。因为是 HTTP 协议发的请求,所以我们直接看子类 HttpServlet。
为了大家更好的理解,这里画了张流程图:
HttpServlet 就是单纯的把 ServletRequest 强转成 HttpServletRequest、ServletResponse 强转成 HttpServletResponse,再调用重载方法。
public void service(ServletRequest req, ServletResponse res)throws ServletException, IOException
{HttpServletRequest request;HttpServletResponse response;if (!(req instanceof HttpServletRequest &&res instanceof HttpServletResponse)) {throw new ServletException("non-HTTP request or response");}request = (HttpServletRequest) req;response = (HttpServletResponse) res;service(request, response);
}
重载方法里,HttpServlet 根据HttpServletRequest#getMethod
把请求拆分成了 doGet、doPost、doPut 等方法交给子类处理。
protected void service(HttpServletRequest req, HttpServletResponse resp){String method = req.getMethod();if (method.equals(METHOD_GET)) {doGet(req, resp);}else if (method.equals(METHOD_HEAD)) {long lastModified = getLastModified(req);maybeSetLastModified(resp, lastModified);doHead(req, resp);}else if (method.equals(METHOD_POST)) {doPost(req, resp);}......
}
子类 FrameworkServlet 再把拆分后的方法又聚合到一起,统一交给processRequest()
处理,做了几件事:
- 初始化 LocaleContextHolder、RequestContextHolder,把对象写入 ThreadLocal,方便透传
- 调用模板方法
doService()
由子类处理 - 恢复 ContextHolder 上下文对象
- 发布请求处理事件 ServletRequestHandledEvent
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {// 初始化XXXContextHolder 把对象写入ThreadLocal,方便透传LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();LocaleContext localeContext = buildLocaleContext(request);RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());initContextHolders(request, localeContext, requestAttributes);try {doService(request, response);}finally {// 恢复上下文resetContextHolders(request, previousLocaleContext, previousAttributes);// 发布处理事件,不管成功与否publishRequestHandledEvent(request, response, startTime, failureCause);}
}
子类 DispatcherServlet 会把一些需要暴露的对象写入到Request#attributes
透传,再把请求实际委托给doDispatch()
处理。
doDispatch()
是个模板方法,处理请求的过程会依赖很多相关组件,主要流程如下:
- 检查是不是文件上传请求,是的话就把请求转换成 MultipartHttpServletRequest
- 查找能处理请求的目标处理器 Handler
- 查找能协调目标 Handler 干活的适配器 HandlerAdapter
- 触发拦截器方法:HandlerInterceptor#preHandle
- HandlerAdapter#handle 适配器协调处理器处理请求
- 触发拦截器方法:HandlerInterceptor#postHandle
- 处理响应结果 ModelAndView
- 触发拦截器方法:HandlerInterceptor#afterCompletion
- 清理文件上传相关的资源
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;// 是否是文件上传请求WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {ModelAndView mv = null;Exception dispatchException = null;try {// 检查文件上传请求 Content-Type:multipart/form-dataprocessedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// 获取处理器mappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}// 获取处理器适配器,协调Handler处理请求HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// 处理last-modified,如果资源没发生变更,让客户端用缓存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;}}// 触发 HandlerInterceptor#preHandleif (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// 处理请求mv = ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}applyDefaultViewName(processedRequest, mv);// 触发 HandlerInterceptor#postHandlemappedHandler.applyPostHandle(processedRequest, response, mv);} catch (Exception ex) {dispatchException = ex;} catch (Throwable err) {dispatchException = new NestedServletException("Handler dispatch failed", err);}// 处理结果 对于@ResponseBody接口,只剩触发afterCompletion()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()) {if (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}} else {if (multipartRequestParsed) {// 清理文件上传相关的资源cleanupMultipart(processedRequest);}}}
}
检查是不是多文件上传请求,交给了组件 MultipartResolver,判断规则也很简单,检查请求头 ContentType 是不是以 “multipart/form-data” 为前缀:
public boolean isMultipart(HttpServletRequest request) {return StringUtils.startsWithIgnoreCase(request.getContentType(),(this.strictServletCompliance ? MediaType.MULTIPART_FORM_DATA_VALUE : "multipart/"));
}
如果是文件上传请求,会把请求对象转换成 MultipartHttpServletRequest,可以很方便的遍历上传的文件列表。
public MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException {return new StandardMultipartHttpServletRequest(request, this.resolveLazily);
}
DispatcherServlet#getHandler
用来查找目标处理器,依赖组件 HandlerMapping。除了查找 Handler,还会顺带把能应用的拦截器 HandlerInterceptor 一起封装好,构建一个调用链对象 HandlerExecutionChain。
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {if (this.handlerMappings != null) {for (HandlerMapping mapping : this.handlerMappings) {HandlerExecutionChain handler = mapping.getHandler(request);if (handler != null) {return handler;}}}return null;
}
HandlerMapping#getHandlerAdapter
用来查找处理器适配器,在 Spring MVC 里面,处理器 Handler 用 Object 表示,可以以任何形式存在。为了协调 Handler 干活,还必须找到对应的 HandlerAdapter。
DispatcherServlet 初始化会加载容器内所有的 HandlerAdapter 类型的 Bean 封装成 List,查找的过程就是按顺序遍历是否支持目标 Handler,找到了就直接返回。
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {if (this.handlerAdapters != null) {for (HandlerAdapter adapter : this.handlerAdapters) {// 适配器是否支持目标handlerif (adapter.supports(handler)) {return adapter;}}}
}
找到了目标 Handler 和 HandlerAdapter,先触发前置拦截器:HandlerInterceptor#preHandle
,再调用HandlerAdapter#handle
把请求交给目标处理器处理,这一步才是真正的核心,也很复杂,会另起篇幅分析。请求处理完后,触发后置拦截器:HandlerInterceptor#postHandle
,最后处理 ModelAndView 渲染视图,触发拦截器:HandlerInterceptor#afterCompletion
。
最后的最后,整个请求处理完毕后,如果是文件上传请求,还需要调用cleanupMultipart()
清理资源。
尾巴
DispatcherServlet 本质是个 Servlet,Servlet 容器启动会触发其init
生命周期方法做初始化,初始化最重要的两件事分别是初始化 Web 上下文容器对象、以及依赖的各种解析器,映射器等组件。
Servlet 容器收到请求会调用Servlet#service
响应请求,HttpServlet 会根据方法类型把请求拆分成对应的 doXXX 方法由子类处理,FrameworkServlet 再把请求聚合起来,最终由DispatcherServlet#doDispatch
处理。这是个模板方法,先检查是不是文件上传请求,再找到目标处理器 Handler、再查找适配器 HandlerAdapter、让 HandlerAdapter 协调 Handler 处理请求,最终处理 ModelAndView 渲染视图。
DispatcherServlet 本身并不复杂,它只是定义了一个处理请求的算法骨架,处理的细节交给了对应的组件类,这也很负责单一职责原则。
通过 DispatcherServlet 我们已经了解了 Spring MVC 处理请求的整体流程,但是请求处理的细节,还需要深入到其依赖的各个组件类。