前言
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;
// 是否要在祖先容器检测所有的HandlerMapping
if (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-data
processedRequest = 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#preHandle
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 处理请求
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
// 触发 HandlerInterceptor#postHandle
mappedHandler.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) {
// 适配器是否支持目标handler
if (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 处理请求的整体流程,但是请求处理的细节,还需要深入到其依赖的各个组件类。