1. 程式人生 > >Servlet和SpringMVC的初始化及請求處理過程淺析

Servlet和SpringMVC的初始化及請求處理過程淺析

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)方法

  1. GenericServlet:

    /**重寫init(ServletConfig)方法,將引數config儲存在物件中,將init()方法交給實現類重寫,這樣既可以讓本類的init(ServletConfig)得到執行,也可以讓實現類的init()得到執行。這種方式在Servlet的實現類中經常用到:模板方法**/
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this
    .init(); } public void init() throws ServletException { }

    重寫了init方法,儲存了配置引數的物件,將具體的init操作交給實現類

  2. HttpServlet: 沒有重寫init相關的方法,定義了一些Servlet中的常量(POST、GET等)和常用方法(doPost、doGet等),供實現類使用和重寫
  3. 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");
    }
    }
  4. 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();
             。。。。。//註冊監聽器
        }
    }
    }
  5. 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)方法

  1. GenericServlet:沒有重新service(ServletRequest,ServletResponse)方法
  2. 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);
    
        } 
        。。。
    }
  3. HttpServletBean:未重寫service或doXxx方法
  4. 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);
        。。。
    }
  5. DispatchServlet:

    @Override
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        。。。//設定相關屬性
        try {//該方法是SpringMVC處理請求的關鍵,也是DispatchServlet分發請求的關鍵:根據
        //pathHandlerMapping查詢對應的HandlerExecutionChain,呼叫HandlerAdapter的handle方法進行處理,
        //獲取ModelAndView物件,最後處理結果。
            doDispatch(request, response);
        }
        。。。//儲存相關屬性
    }