DispatcherServlet
DispatcherServlet(前端控制器)是整个MVC框架中最为核心的一块,它主要用来拦截符合要求的外部请求,并把请求分发到不同的控制器去处理,根据控制器处理后的结果,生成相应的响应发送到客户端。前端控制器既可以使用Filter实现(Struts2采用这种方式),也可以使用Servlet来实现(spring MVC框架)。
DispatcherServlet 作为前置控制器是web服务器的入口,是spring mvc最重要的一个类,通过它的生命周期可以加深对web服务器的理解。
1)初始化阶段:调用init()方法,Servlet被装载后,Servlet容器创建一个Servlet实例并且调用Servlet的init()方法进行初始化。
在Servlet的整个生命周期内,init()方法只被调用一次。
2)响应客户请求阶段:调用service()方法
3)终止阶段:调用destroy()方法
Servlet初始化阶段:
在下列时刻Servlet容器装载Servlet:
1)Servlet容器启动时自动装载某些Servlet,实现它只需要在web.XML文件中的<Servlet></Servlet>之间添加如下代码:
<loadon-startup>1</loadon-startup>
2)在Servlet容器启动后,客户首次向Servlet发送请求
3)Servlet类文件被更新后,重新装载Servlet
DispatcherServlet的结构
复习了上述知识后我们来看看DispatcherServlet的结构:
DispatcherServlet继承自抽象类:FrameworkServlet,间接继承了HttpServlet (FrameworkServlet继承自HttpServletBean,而HttpServletBean继承自HttpServlet )
Servlet的初始化
1 protected void initStrategies(ApplicationContext context) {
2 initMultipartResolver(context); //文件上传解析,如果请求类型是multipart将通过MultipartResolver进行文件上传解析;
3 initLocaleResolver(context); //本地化解析
4 initThemeResolver(context); //主题解析
5 initHandlerMappings(context); //通过HandlerMapping,将请求映射到处理器
6 initHandlerAdapters(context); //通过HandlerAdapter支持多种类型的处理器
7 initHandlerExceptionResolvers(context); //如果执行过程中遇到异常将交给HandlerExceptionResolver来解析
8 initRequestToViewNameTranslator(context); //直接解析请求到视图名
9 initViewResolvers(context); //通过ViewResolver解析逻辑视图名到具体视图实现
10 initFlashMapManager(context); //flash映射管理器
11 }
servlet如何处理请求:
servlet的service方法处理http请求。
FrameworkServlet.java 定义了servlet的service和destroy方法,如下所示:
1 protected void service(HttpServletRequest request, HttpServletResponse response) 2 throws ServletException, IOException { 3 4 String method = request.getMethod(); 5 if (method.equalsIgnoreCase(RequestMethod.PATCH.name())) { 6 processRequest(request, response); 7 } 8 else { 9 super.service(request, response); 10 } 11 }
我们知道http请求类型有七种(外加一个option选项),定义如下:
public enum RequestMethod { GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE }
FrameworkServlet的service()处理不同的请求,我们以常见的post来说明:
1 protected final void processRequest(HttpServletRequest request, HttpServletResponse response) 2 throws ServletException, IOException { 3 4 long startTime = System.currentTimeMillis(); 5 Throwable failureCause = null; 6 7 LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); 8 LocaleContext localeContext = buildLocaleContext(request); 9 10 RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); 11 ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); 12 13 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); 14 asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor()); 15 16 initContextHolders(request, localeContext, requestAttributes); 17 18 try { 19 doService(request, response); 20 } 21 catch (ServletException ex) { 22 failureCause = ex; 23 throw ex; 24 } 25 catch (IOException ex) { 26 failureCause = ex; 27 throw ex; 28 } 29 catch (Throwable ex) { 30 failureCause = ex; 31 throw new NestedServletException("Request processing failed", ex); 32 } 33 34 finally { 35 resetContextHolders(request, previousLocaleContext, previousAttributes); 36 if (requestAttributes != null) { 37 requestAttributes.requestCompleted(); 38 } 39 40 if (logger.isDebugEnabled()) { 41 if (failureCause != null) { 42 this.logger.debug("Could not complete request", failureCause); 43 } 44 else { 45 if (asyncManager.isConcurrentHandlingStarted()) { 46 logger.debug("Leaving response open for concurrent processing"); 47 } 48 else { 49 this.logger.debug("Successfully completed request"); 50 } 51 } 52 } 53 54 publishRequestHandledEvent(request, startTime, failureCause); 55 } 56 }
FrameworkServlet 抽象定义了处理流程,留待子类来实现该方法,完成具体的请求处理。
protected abstract void doService(HttpServletRequest request, HttpServletResponse response) throws Exception;
具体实现如下:
1 protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { 2 if (logger.isDebugEnabled()) { 3 String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : ""; 4 logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed + 5 " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]"); 6 } 7 8 Map<String, Object> attributesSnapshot = null; 9 if (WebUtils.isIncludeRequest(request)) { 10 attributesSnapshot = new HashMap<String, Object>(); 11 Enumeration<?> attrNames = request.getAttributeNames(); 12 while (attrNames.hasMoreElements()) { 13 String attrName = (String) attrNames.nextElement(); 14 if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) { 15 attributesSnapshot.put(attrName, request.getAttribute(attrName)); 16 } 17 } 18 } 19 20 request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); 21 request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); 22 request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); 23 request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource()); 24 25 FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response); 26 if (inputFlashMap != null) { 27 request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap)); 28 } 29 request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap()); 30 request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager); 31 32 try { 33 doDispatch(request, response); 34 } 35 finally { 36 if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { 37 return; 38 } 39 if (attributesSnapshot != null) { 40 restoreAttributesAfterInclude(request, attributesSnapshot); 41 } 42 } 43 }
重头戏,作为请求分发器的实现:
功能:1. 把请求分发到handler(按照配置顺序获取servlet的映射关系获取handler);2. 根据servlet已安装的 HandlerAdapters 去查询第一个能处理的handler;3. 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; Exception dispatchException = null; try { processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); mappedHandler = getHandler(processedRequest); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; } HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); String method = request.getMethod(); boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (logger.isDebugEnabled()) { logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified); } if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } try { mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); } finally { if (asyncManager.isConcurrentHandlingStarted()) { return; } } applyDefaultViewName(request, mv); mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Error err) { triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err); } finally { if (asyncManager.isConcurrentHandlingStarted()) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); return; } if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } }
servlet销毁
1 public void destroy() { 2 getServletContext().log("Destroying Spring FrameworkServlet '" + getServletName() + "'"); 3 if (this.webApplicationContext instanceof ConfigurableApplicationContext && !this.webApplicationContextInjected) { 4 ((ConfigurableApplicationContext) this.webApplicationContext).close(); 5 } 6 }