1.HttpServletBean
HttpServletBean主要参与创建过程,并没有涉及请求的处理。
2.FrameworkServlet
FrameworkServlet重写了service,doGet,doPost,doPut,doDelete,doOptions,doTrace方法(除了doHead的所有处理请求的方法),在service方法中增加了对PATCH类型请求的处理,其他类型交给父类进行处理。doGet,doPost,doPut,doDelete都是自己处理,所有需要自己处理的请求都交给了processRequest方法进行统一处理。
@Overrideprotected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpMethod httpMethod = HttpMethod.resolve(request.getMethod()); if (httpMethod == HttpMethod.PATCH || httpMethod == null) { processRequest(request, response); } else { super.service(request, response); }}
@Overrideprotected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response);}
@Overrideprotected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response);}
@Overrideprotected final void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response);}
@Overrideprotected final void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response);}
processRequest方法是FrameworkServlet中最核心的方法
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis(); Throwable failureCause = null; //获取LocaleContextHolder中原来保存的LocaleContext LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); // 获取当前请求的LocaleContext LocaleContext localeContext = buildLocaleContext(request); //获取RequestContextHolder中原来保存的RequestAttributes RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); //获取当前请求的RequestAttributes ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); //异步处理管理器设置拦截器,异步请求后面介绍 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor()); //将当前请求的LocaleContext,ServletRequestAttributes设置到LocaleContextHolder和RequestContextHolder initContextHolders(request, localeContext, requestAttributes); try { // 实际处理入口,模板方法在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 { // 恢复原来的LocaleContext,ServletRequestAttributes resetContextHolders(request, previousLocaleContext, previousAttributes); if (requestAttributes != null) { requestAttributes.requestCompleted(); } if (logger.isDebugEnabled()) { boolean isRequestDispatch = request.getDispatcherType().equals(DispatcherType.REQUEST); String dispatchType = request.getDispatcherType().name(); if (failureCause != null) { if (!isRequestDispatch) { logger.debug("Unresolved failure from \"" + dispatchType + "\" dispatch: " + failureCause); } else if (logger.isTraceEnabled()) { logger.trace("Failed to complete request", failureCause); } else { logger.debug("Failed to complete request: " + failureCause); } } else { if (asyncManager.isConcurrentHandlingStarted()) { logger.debug("Exiting but response remains open for further handling"); } else { int status = response.getStatus(); if (!isRequestDispatch) { logger.debug("Exiting from \"" + dispatchType + "\" dispatch (status " + status + ")"); } else { HttpStatus httpStatus = HttpStatus.resolve(status); logger.debug("Completed " + (httpStatus != null ? httpStatus : status)); } } } } //处理完后发布ServletRequestHandledEvent消息 publishRequestHandledEvent(request, response, startTime, failureCause); }}
processRequest自己主要做了两件事情:
1、对LocaleContext和RequestAttributes的设置和恢复,LocaleContext里面存放着本地化信息,比如zh-cn等。RequestAttributes是spring的一个借口,通过它可以get/set/removeAttrbute,根据scope参数判断操作是request还是session。具体实现是ServletRequestAttributes,看看它的数据结构和方法
private final HttpServletRequest request;@Nullableprivate HttpServletResponse response;@Nullableprivate volatile HttpSession session;private final MapsessionAttributesToUpdate = new ConcurrentHashMap<>(1);
@Overridepublic Object getAttribute(String name, int scope) { if (scope == SCOPE_REQUEST) { if (!isRequestActive()) { throw new IllegalStateException( "Cannot ask for request attribute - request is not active anymore!"); } return this.request.getAttribute(name); } else { HttpSession session = getSession(false); if (session != null) { try { Object value = session.getAttribute(name); if (value != null) { this.sessionAttributesToUpdate.put(name, value); } return value; } catch (IllegalStateException ex) { // Session invalidated - shouldn't usually happen. } } return null; }}@Overridepublic void setAttribute(String name, Object value, int scope) { if (scope == SCOPE_REQUEST) { if (!isRequestActive()) { throw new IllegalStateException( "Cannot set request attribute - request is not active anymore!"); } this.request.setAttribute(name, value); } else { HttpSession session = obtainSession(); this.sessionAttributesToUpdate.remove(name); session.setAttribute(name, value); }}@Overridepublic void removeAttribute(String name, int scope) { if (scope == SCOPE_REQUEST) { if (isRequestActive()) { this.request.removeAttribute(name); removeRequestDestructionCallback(name); } } else { HttpSession session = getSession(false); if (session != null) { this.sessionAttributesToUpdate.remove(name); try { session.removeAttribute(name); // Remove any registered destruction callback as well. session.removeAttribute(DESTRUCTION_CALLBACK_NAME_PREFIX + name); } catch (IllegalStateException ex) { // Session invalidated - shouldn't usually happen. } } }}
2.处理完后发布ServletRequestHandledEvent消息
private void publishRequestHandledEvent(HttpServletRequest request, HttpServletResponse response, long startTime, @Nullable Throwable failureCause) { //publishEvents可以在配置Servlet时设置,默认为true if (this.publishEvents && this.webApplicationContext != null) { // 无论请求是否执行成功都会发布消息 long processingTime = System.currentTimeMillis() - startTime; this.webApplicationContext.publishEvent( new ServletRequestHandledEvent(this, request.getRequestURI(), request.getRemoteAddr(), request.getMethod(), getServletConfig().getServletName(), WebUtils.getSessionId(request), getUsernameForRequest(request), processingTime, failureCause, response.getStatus())); }}
写一个简单的日志请求监听器,只要继承ApplicationListener监听ServletRequestHandledEvent事件就行了。
@Componentpublic class ServletRequestHandleEventListener implements ApplicationListener{ private static Logger logger = Logger.getLogger("request process log"); public void onApplicationEvent(ServletRequestHandledEvent event){ logger.info(event.getDescription()); }}
打印日志如下
FrameworkServlet的一个处理任务到这就结束了,接下来应该是DispatcherServlet的表演时刻
3.DispatcherServlet
DispatcherServlet是SpringMVC最核心的处理类,整个处理过程的顶层设计都在这个里面,所以我们要把这个类彻底弄明白了。通过之前的分析我们知道DispatcherServlet的入口是doService()
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { logRequest(request);//日志 MapattributesSnapshot = null;//快照备份记录用的 if (WebUtils.isIncludeRequest(request)) {// 当include请求是对request的Attribute做快照备份 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)); } } } // 对request设置一些属性 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 { 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); } } }}
doService主要是对request设置了一些属性,核心入口是doDispatch方法,doDispatch方法也非常的简洁,最主要的任务是:
1根据request找到Handler;
2.根据Handler找到对应的HandlerAdapter;
3.用HandlerAdapter处理Handler,
4调用processDispatchResult方法处理上面处理后的结果
Handler:也就是处理器,它直接对应着Controller中@RequestMapping的所有方法都可以看成一个Handler,只要可以实际处理请求的就可以是Handler
HandlerMappering:用来查找Handler的,在SpringMVC中会有很多处理请求,每个请求都需要一个Handler来处理,具体接收到一个请求后倒地使用哪个Handler来处理呢,这就是HandlerMapping要做的事情。
HandlerAdapter:它是一个是配置用来调用handler处理事情,就是Handler用来干活的工具
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;//封装Model和View的容器 Exception dispatchException = null; try { //检测是否上传请求 processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); // 根据request获取Handler mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } // 根据Handler找到相应的Adapter HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // 处理get,head请求的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; } } // 执行相应Interceptor的preHandle if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // 实际上调用处理程序,HandlerAdapter使用Handler处理请求 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); //如果需要异步处理直接返回 if (asyncManager.isConcurrentHandlingStarted()) { return; } //当view为空时(比如返回值为void),根据request设置默认的view applyDefaultViewName(processedRequest, mv); //执行相应Interceptor的postHandler 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); } //处理返回结果。包括异常处理,渲染页面,发出完成通知触发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 { // 删除上传请求的资源 if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } }}
1、首先检查是不是上传请求,如果是设置上传的request,并设置标志位为true。
2、通过getHandler获取Handler处理器链
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;}
3、接下来处理get,head请求的Last-Modified后一次调用相应Interceptor的preHandle
4、接下来最关键的就是让HandlerAdapter使用Handler处理请求返回ModelAndView
5、继续调用processDispatchResult方法处理返回的结果
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception { boolean errorView = false; // 如果请求过程中有异常则处理异常 if (exception != null) { if (exception instanceof ModelAndViewDefiningException) { logger.debug("ModelAndViewDefiningException encountered", exception); mv = ((ModelAndViewDefiningException) exception).getModelAndView(); } else { Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); mv = processHandlerException(request, response, handler, exception); errorView = (mv != null); } } // 渲染页面 if (mv != null && !mv.wasCleared()) { render(mv, request, response); if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } else { if (logger.isTraceEnabled()) { logger.trace("No view rendering, null ModelAndView returned."); } } if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // 异步请求直接返回 return; } // 发出请求处理完成的通知,触发Interceptor的afterCompletion if (mappedHandler != null) { mappedHandler.triggerAfterCompletion(request, response, null); }}
6、最后就是触发Interceptor的afterCompletion方法了。
doDispatch方法就分析到这了,可以看出它的顶层设计很简洁,然后具体的处理交给不同的组件具体实现的。doDispatch处理流程图如下(图来源SpringMvc源码分析与实战)
总结:
三个Servlet的处理过程大致功能如下:
1.HttpServletBean:没有参与实际请求的处理
2.FrameworkServlet:将不同类型的请求合并到processRequest方法统一处理,processRequest方法中做了三件事:
(1)调用doServeice模板方法具体处理请求
(2)将当前请求的LocalContext和ServletRequestAttributes设置到相应的Holder,并在请求处理完成后恢复。
(3)请求处理完成后发布消息
3.DispatcherServlet:doService方法给request设置了一些属性并将请求交给doDispatch方法处理,它使用了相应的组件完成处理请求,另外HanderMapping,Handler和HandlerAdapter这三个概念的含义以及他们之间的关系也非常重要