接觸Web時間比較久,雖然知道Servlet的生命周期但是理解卻還是不夠,今天剛好debug代碼涉及這塊就利用余下時間研究了一下。
Servlet的生命周期以及處理瀏覽器請求的過程。Servlet接口中定義的方法有:
而init -> service -> destory剛好就是servlet的生命周期。通常當web容器(即web服務器,例如tomct等)啟動時候回根據配置文件進行加載servlet,加載servlet過程就是初始化servlet的過程。
由web容器進行調用生命周期init方法進行初始化。初始化完成便可以進行處理瀏覽器發送的請求了。
當瀏覽器向Web容器發送請求時候便由Web容器調用Servelt的service方法(此處實現是HttpServlet的service方法)進行處理瀏覽器請求。HttpService的service方法關鍵源碼實現如下:
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); //判斷客戶端的請求是get、post、... if (method.equals(METHOD_GET)) { long lastModified = getLastModified(req); if (lastModified == -1) { doGet(req, resp); } else { long ifModifiedSince; try { ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE); } catch (IllegalArgumentException iae) { ifModifiedSince = -1; } if (ifModifiedSince < (lastModified / 1000 * 1000)) { 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);//處理post請求 } 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 { 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); } }
本例以post請求進行源碼跟蹤,處理post請求調用doPost方法。此處回顧一下我們一般寫一個servlet時候需要的步驟了,我們都是自己定義一個Servlet類然后一般都是實現doPost和doGet方法,回顧到此就可以回到本文了,本例中調用的doPost方法就是調用我們自己實現的doPost方法,這個流程下來就完成了瀏覽器的請求。而對於Servlet的銷毀由Web容器調用destory方法進行銷毀。
Spring MVC中DispatherServlet的繼承結構:
在使用Spring MVC進行開發時候,在web.xml中需要配置DispatherServlet,讓Web容器啟動時候加載Servlet。通過繼承結構可以發現DispatherServlet是HttpServlet的子類。
當瀏覽器進行請求時候Web容器會調用DispatherServlet的service方法,DispatherServlet的service方法在父類FrameWorkServlet中實現;FrameWorkServlet#service方法源碼如下:
/** * Override the parent class implementation in order to intercept PATCH requests. */ @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpMethod httpMethod = HttpMethod.resolve(request.getMethod()); if (HttpMethod.PATCH == httpMethod || httpMethod == null) { processRequest(request, response); } else { super.service(request, response); } }
最后調用super.service調用HttpServlet#service方法,進行判斷請求的方法是get、post還是...然后調用對應的doXXX方法,此處的doXXX方法在FrameWorkServlet中實現(FrameWorkServlet是框架實現的一個Servlet,與正常Servlet開發的servlet類似)。
org.springframework.web.servlet.FrameworkServlet#doServiceorg.springframework.web.servlet.FrameworkServlet#doPost源碼如下:
@Override protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); }
org.springframework.web.servlet.FrameworkServlet#processRequest源碼如下:
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) //DispatcherServlet的processRequst方法在FrameWork中實現 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, response, startTime, failureCause); } }
org.springframework.web.servlet.DispatcherServlet#doService源碼:
@Override protected 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(DEFAULT_STRATEGIES_PREFIX)) { 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()) { // Restore the original attribute snapshot, in case of an include. if (attributesSnapshot != null) { restoreAttributesAfterInclude(request, attributesSnapshot); } } } }
最后調用doDispatch(request, response)方法完成處理(后續代碼有時間在跟蹤)。
過程總結: