通过前面的分析,我们知道DispatcherServlet其本质还是Servlet,那么当客户端的请求到达时,根据Servlet生命周期,其应该会调用其或者其父类中的service方法。
在其父类FrameworkServlet中我们找到了service方法
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/*
* 获取HttpMethod类型,
* HttpMethod为枚举类,支持的Http请求类型有GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
*/
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
// 若方法为 PATCH 方法或为空则单独处理
processRequest(request, response);
}
else {
super.service(request, response);
}
}
这里有两个方法,一个是processRequest(request, response)
和super.service(request, response)
,这里的super就是HttpServlet,我们分别看一下
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();
//如果是get
if (method.equals(METHOD_GET)) {
//lastModified 缓存判断
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < lastModified) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} 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);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
上面几个方法最常用的就是 doGet()
和 doPost()
。这两个方法被 FrameworkServlet
重写了。我们来看看在 FrameworkServlet
中的实现。
@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);
}
我们可以很清楚的看到,对于大部分的请求,还是依赖于 HttpServlet#service(HttpServletRequest, HttpServletResponse) 来进行一个请求的分发。对于我们常见的 doGet() 和 doPost() 方法都是直接调用 processRequest(request, response);, 而processRequest方法 的具体实现在FrameworkServlet#processRequest 中 。
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
// 记录抛出的异常~~~(若有的话)
Throwable failureCause = null;
//国际化设置
// 1 提取当前线程的 LocaleContext 属性
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
// 2. 根据当前的request 创建对应的 LocaleContext ,并绑定到当前线程
LocaleContext localeContext = buildLocaleContext(request);
//构建ServletRequestAttributes对象
//3. 提取当前线程的 RequestAttributes 属性
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
//4.根据当前的request 创建对应的 RequestAttributes ,并绑定到当前线程
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
// 拿到异步管理器。这里是首次获取,会new WebAsyncManager(),然后放到request的attr里面
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
//这里需要注意:给异步上下文恒定注册了RequestBindingInterceptor这个拦截器(作用:绑定当前的request、response、local等)
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
//这句话很明显,就是吧request和Local上下文、RequestContext绑定
initContextHolders(request, localeContext, requestAttributes);
try {
//5.模版设计模式:由子类DispatcherServlet去实现实际逻辑
doService(request, response);
}
catch (ServletException | IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}
finally {
//这个时候已经全部处理完成,视图已经渲染了
//doService()方法完成后,重置上下文,也就是解绑
// 6. 请求结束,恢复线程原状
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
logResult(request, response, failureCause, asyncManager);
//关键:不管执行成功与否,都会发布一个事件,我处理了这个请求(有需要监听的,就可以监听这个事件了,每次请求都会有)
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
由于逻辑都被封装到 doService(request, response);
中,所以这里还是比较简单。而doService又被 DispatcherServlet实现了。因此我们这里来看看 DispatcherServlet#doService
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(request);
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
/*
* 如果当前请求是一个 include request,如:
* 则为此请求属性建立快照,以便include request结束后能够将其恢复
*/
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and view objects.
// 说得很清楚,把一些常用对象放进请求域 方便Handler里面可以随意获取
// Spring上下文
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
// 国际化解析器
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
// 主题解析器
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
// 主题
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
// 如果是重定向,放置得更多一些
if (this.flashMapManager != null) {
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
}
try {
// 【重要】 真正开始处理http请求
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
// 恢复之前保存的数据快照
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
很明显,这 “祖孙三代” 一层一层传递,终于传递到了DispatcherServlet 手里,这里我们直接开始看doDispatch(request, response);
,这里才是核心逻辑的所在!!!
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 此处用processedRequest 需要注意的是:若是处理上传,processedRequest 将和request不再指向同一对象
HttpServletRequest processedRequest = request;
// 链处理器
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
//1.checkMultipart 判断是否是上传需求。且看下面的具体分析:
//如果请求是POST请求,并且请求头中的Context-Type是以multipart/开头的就认为是文件上传的请求
processedRequest = checkMultipart(request);
// 标记一下:是否是文件上传的request了
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
// 2.查找当前请求对应的handler,包括Handler(控制器)本身和Handler拦截器
mappedHandler = getHandler(processedRequest);
// 未能找到对应的handler,抛出NoHandlerFoundException异常并返回404
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
// 3.查找当前请求对应的HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
// 4.处理last-modified请求头,如果当前请求支持的话
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;
}
}
// 5.应用前置拦截器:执行注册拦截器的preHandle方法
// 如果有拦截器返回false,则表明该拦截器已经处理了返回结果,直接返回;
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
// 6.调用HandlerAdapter的handler方法,真正开始处理Controller
// *****真正执行我们自己书写的controller方法的逻辑。返回一个ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 7.如果当前请求是异步处理,直接返回
// 如果异步启动了,这里就先直接返回了,也就不会再执行拦截器PostHandle之类的
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//8. 为返回值设定默认视图名,如果当前返回值中不包含视图名的话
applyDefaultViewName(processedRequest, mv);
// 9.应用已注册拦截器的后置方法:执行所有的拦截器的postHandle方法,并且把mv给他
// 这里有一个小细节:这个时候拦截器是【倒序】执行的
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 10.处理分发调用结果,如视图模型解析、返回等工作
// 处理返回结果,包括处理异常、渲染页面,发出完成通知触发Interceptor的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()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
if (request.getDispatcherType().equals(DispatcherType.REQUEST)) {
logger.trace("Request already resolved to MultipartHttpServletRequest, e.g. by MultipartFilter");
}
}
else if (hasMultipartException(request)) {
logger.debug("Multipart resolution previously failed for current request - " +
"skipping re-resolution for undisturbed error rendering");
}
else {
try {