spring mvc DispatcherServlet详解之前传---前端控制器架构
前端控制器是整個MVC框架中最為核心的一塊,它主要用來攔截符合要求的外部請求,并把請求分發到不同的控制器去處理,根據控制器處理后的結果,生成相應的響應發送到客戶端。前端控制器既可以使用Filter實現(Struts2采用這種方式),也可以使用Servlet來實現(spring MVC框架)。
?
?
DispatcherServlet 作為前置控制器是web服務器的入口,是spring mvc最重要的一個類,通過它的生命周期可以加深對web服務器的理解。
servlet的生命周期
首先我們回憶一下servlet的生命周期:
Servlet生命周期分為三個階段:【http://www.cnblogs.com/cuiliang/archive/2011/10/21/2220671.html】
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的初始化
protected void initStrategies(ApplicationContext context) {initMultipartResolver(context); //文件上傳解析,如果請求類型是multipart將通過MultipartResolver進行文件上傳解析;initLocaleResolver(context); //本地化解析initThemeResolver(context); //主題解析initHandlerMappings(context); //通過HandlerMapping,將請求映射到處理器initHandlerAdapters(context); //通過HandlerAdapter支持多種類型的處理器initHandlerExceptionResolvers(context); //如果執行過程中遇到異常將交給HandlerExceptionResolver來解析initRequestToViewNameTranslator(context); //直接解析請求到視圖名initViewResolvers(context); //通過ViewResolver解析邏輯視圖名到具體視圖實現initFlashMapManager(context); //flash映射管理器}?
servlet如何處理請求:
servlet的service方法處理http請求。
FrameworkServlet.java?定義了servlet的service和destroy方法,如下所示:
/*** Override the parent class implementation in order to intercept PATCH* requests.*/@Overrideprotected void service(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { String method = request.getMethod();if (method.equalsIgnoreCase(RequestMethod.PATCH.name())) {processRequest(request, response);}else {super.service(request, response);}}?
我們知道http請求類型有七種(外加一個option選項),定義如下:
public enum RequestMethod {GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE} FrameworkServlet的service()處理不同的請求,我們以常見的post來說明:/*** Process this request, publishing an event regardless of the outcome.* <p>The actual event handling is performed by the abstract* {@link #doService} template method.*/protected final void processRequest(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {long startTime = System.currentTimeMillis();Throwable failureCause = null;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);}catch (ServletException ex) {failureCause = ex;throw ex;}catch (IOException ex) {failureCause = ex;throw ex;}catch (Throwable ex) {failureCause = ex;throw new NestedServletException("Request processing failed", ex);}finally {resetContextHolders(request, previousLocaleContext, previousAttributes);if (requestAttributes != null) {requestAttributes.requestCompleted();}if (logger.isDebugEnabled()) {if (failureCause != null) {this.logger.debug("Could not complete request", failureCause);}else {if (asyncManager.isConcurrentHandlingStarted()) {logger.debug("Leaving response open for concurrent processing");}else {this.logger.debug("Successfully completed request");}}}publishRequestHandledEvent(request, startTime, failureCause);}} FrameworkServlet 抽象定義了處理流程,留待子類來實現該方法,完成具體的請求處理。 /*** Subclasses must implement this method to do the work of request handling,* receiving a centralized callback for GET, POST, PUT and DELETE.* <p>The contract is essentially the same as that for the commonly overridden* {@code doGet} or {@code doPost} methods of HttpServlet.* <p>This class intercepts calls to ensure that exception handling and* event publication takes place.* @param request current HTTP request* @param response current HTTP response* @throws Exception in case of any kind of processing failure* @see javax.servlet.http.HttpServlet#doGet* @see javax.servlet.http.HttpServlet#doPost*/protected abstract void doService(HttpServletRequest request, HttpServletResponse response)throws Exception;
具體實現如下:
/*** Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}* for the actual dispatching.*/@Overrideprotected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {if (logger.isDebugEnabled()) {String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +" processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");}// Keep a snapshot of the request attributes in case of an include,// to be able to restore the original attributes after the include.Map<String, Object> attributesSnapshot = null;if (WebUtils.isIncludeRequest(request)) {attributesSnapshot = new HashMap<String, Object>();Enumeration<?> attrNames = request.getAttributeNames();while (attrNames.hasMoreElements()) {String attrName = (String) attrNames.nextElement();if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {attributesSnapshot.put(attrName, request.getAttribute(attrName));}}}// Make framework objects available to handlers and view objects. 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());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()) {return;}// Restore the original attribute snapshot, in case of an include.if (attributesSnapshot != null) {restoreAttributesAfterInclude(request, attributesSnapshot);}}}重頭戲,作為請求分發器的實現:
功能:1. 把請求分發到handler(按照配置順序獲取servlet的映射關系獲取handler);2. 根據servlet已安裝的 ?HandlerAdapters?去查詢第一個能處理的handler;3. handler激發處理請求
/*** Process the actual dispatching to the handler.* <p>The handler will be obtained by applying the servlet's HandlerMappings in order.* The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters* to find the first that supports the handler class.* <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers* themselves to decide which methods are acceptable.* @param request current HTTP request* @param response current HTTP response* @throws Exception in case of any kind of processing failure*/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);// Determine handler for the current request.mappedHandler = getHandler(processedRequest);if (mappedHandler == null || mappedHandler.getHandler() == null) {noHandlerFound(processedRequest, response);return;}// Determine handler adapter for the current request.HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// Process last-modified header, if supported by the handler.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 {// Actually invoke the handler.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()) {// Instead of postHandle and afterCompletion mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);return;}// Clean up any resources used by a multipart request.if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}}servlet銷毀
/*** Close the WebApplicationContext of this servlet.* @see org.springframework.context.ConfigurableApplicationContext#close()*/@Overridepublic void destroy() {getServletContext().log("Destroying Spring FrameworkServlet '" + getServletName() + "'");// Only call close() on WebApplicationContext if locally managed...if (this.webApplicationContext instanceof ConfigurableApplicationContext && !this.webApplicationContextInjected) {((ConfigurableApplicationContext) this.webApplicationContext).close();}}小結:
本文因篇章限制,僅僅介紹了請求處理的流程,沒有對代碼進行深入的分析,接下來的文章將從細微處著手,分析spring的代碼之美。
轉載于:https://www.cnblogs.com/davidwang456/p/4090058.html
總結
以上是生活随笔為你收集整理的spring mvc DispatcherServlet详解之前传---前端控制器架构的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: facade 模式和gateway模式的
- 下一篇: spring mvc Dispatche