Servlet和SpringMVC的初始化及請求處理過程淺析
阿新 • • 發佈:2019-01-02
Servlet是一套Web應用的開發規範,我們按照這套規範編碼就可以實現一個Web應用,使其在Web容器中執行。
我們最開始學習J2EE時,學習和建立的就是Servlet的實現類,後來學習了MVC框架以後,尤其是SpringMVC,就很少直接建立Servlet的實現類了。雖然SpringMVC簡化和隱藏了Servlet,但是我們也要了解Servlet的執行原理,這樣對了解SpringMVC的原理也很有幫助
一.繼承圖:
首先看一下Servlet的類結構
public interface Servlet {
public void init(ServletConfig config) throws ServletException;
public ServletConfig getServletConfig();
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
public String getServletInfo();
public void destroy();
}
Servlet是個介面,定義了init、service、destroy等方法。其中init方法在容器啟動時(或Servlet第一次呼叫時)會被呼叫,進行Servlet的初始化工作;service方法會在容器接收到請求後處理請求時呼叫。
下面看一下Servlet介面的各個實現類,從而分析Servlet的初始化原理和Spring的DispatchServlet是如何作為請求分發器的。
二.Servlet的初始化過程—init(ServletConfig config)方法
GenericServlet:
/**重寫init(ServletConfig)方法,將引數config儲存在物件中,將init()方法交給實現類重寫,這樣既可以讓本類的init(ServletConfig)得到執行,也可以讓實現類的init()得到執行。這種方式在Servlet的實現類中經常用到:模板方法**/ public void init(ServletConfig config) throws ServletException { this.config = config; this
重寫了init方法,儲存了配置引數的物件,將具體的init操作交給實現類
- HttpServlet: 沒有重寫init相關的方法,定義了一些Servlet中的常量(POST、GET等)和常用方法(doPost、doGet等),供實現類使用和重寫
HttpServletBean:
@Override public final void init() throws ServletException { //注意看log的內容,是我們在啟動tomcat時控制檯經常看到的輸出內容 if (logger.isDebugEnabled()) { logger.debug("Initializing servlet '" + getServletName() + "'"); } try { PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); initBeanWrapper(bw); bw.setPropertyValues(pvs, true); } catch (BeansException ex) { logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); throw ex; } /*很重要的方法,在本類中是空方法。HttpServletBean的init()方法做了基本的引數處理後,將其他的 init操作交給了實現類,實現類不重寫init()方法,而是重寫initServletBean(),這樣它們都會得到執行。*/ initServletBean(); if (logger.isDebugEnabled()) { logger.debug("Servlet '" + getServletName() + "' configured successfully"); } }
FrameworkServlet:
//注意看log的內容,是我們在啟動tomcat時控制檯經常看到的輸出內容 @Override protected final void initServletBean() throws ServletException { getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'"); if (this.logger.isInfoEnabled()) { this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started"); } long startTime = System.currentTimeMillis(); try { //初始化應用上下文 this.webApplicationContext = initWebApplicationContext(); //交給實現類繼續做初始化工作,目前未見相關實現類重寫了該方法 initFrameworkServlet(); }。。。 if (this.logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +elapsedTime + " ms"); } } /** 初始化應用上下文,包括子容器上下文和父容器上下文,onRefresh(ApplicationContext)方法交給實現類 處理,DispatchServlet會在此方法中對mvc-servlet.xml檔案進行讀取和引數配置初始化等工作 **/ protected WebApplicationContext initWebApplicationContext() { WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) { // A context instance was injected at construction time -> use it wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { if (cwac.getParent() == null) { //設定子容器的父容器 cwac.setParent(rootContext); } //初始化應用上下文物件 configureAndRefreshWebApplicationContext(cwac); } } } if (!this.refreshEventReceived) { //實現類進行操作 onRefresh(wac); } 。。。。。 return wac; } protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) { 。。。。。。。//wac的初始化工作 //很重要的一步 wac.refresh(); } //AbstractApplicationContext的方法 @Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { prepareRefresh(); ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); prepareBeanFactory(beanFactory); try { 。。。。。//BeanFactory和MessageSource初始化 onRefresh(); 。。。。。//註冊監聽器 } } }
DispatchServlet:
//呼叫DispatchServlet的onRefresh(ApplicationContext)方法,讀取mvc-servlet.xml檔案,並初始化 //HandlerMapping、HandlerAdapters等屬性 @Override protected void onRefresh(ApplicationContext context) { initStrategies(context); } protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); }
三.Servlet的呼叫過程—service(ServletRequest req, ServletResponse res)方法
- GenericServlet:沒有重新service(ServletRequest,ServletResponse)方法
HttpServlet:
//重寫Servlet的service(ServletRequest,ServletResponse)方法,主要做了請求引數型別判斷和引數轉換 @Override public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { HttpServletRequest request; HttpServletResponse response; if (!(req instanceof HttpServletRequest && res instanceof HttpServletResponse)) { throw new ServletException("non-HTTP request or response"); } request = (HttpServletRequest) req; response = (HttpServletResponse) res; service(request, response); } //將請求分為POST、GET、DELETE等幾種常見方法,然後分別進行處理。以前寫Servlet時都是繼承該類,然後 //重寫doGet、doPost等方法,對請求進行處理。SpringMVC的處理方式於此不同。 protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); if (method.equals(METHOD_GET)) { 。。。 doGet(req, resp); 。。。 } } else if (method.equals(METHOD_POST)) { doPost(req, resp); } else if (method.equals(METHOD_DELETE)) { doDelete(req, resp); } 。。。 }
- HttpServletBean:未重寫service或doXxx方法
FrameworkServlet:
/*重寫service方法,增加了PATCH方法的處理,其他方法如GET會呼叫父類的FrameworkServlet重寫了 doXxx方法,實際上會呼叫本類的doXxx方法,也就是呼叫SpringMVC的統一請求處理方法---p rocessRequest(HttpServletRequest, HttpServletResponse)*/ @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); } } @Override protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 。。。 try { //具體的doService實現交給了子類 doService(request, response); } 。。 publishRequestHandledEvent(request, response, startTime, failureCause); 。。。 }
DispatchServlet:
@Override protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { 。。。//設定相關屬性 try {//該方法是SpringMVC處理請求的關鍵,也是DispatchServlet分發請求的關鍵:根據 //pathHandlerMapping查詢對應的HandlerExecutionChain,呼叫HandlerAdapter的handle方法進行處理, //獲取ModelAndView物件,最後處理結果。 doDispatch(request, response); } 。。。//儲存相關屬性 }