從Tomcat的架構談Servlet職責

文章內容

1.Tomcat框架概述和分析
2.Servlet的實現原理概述
3.Servlet在Tomcat中作用


1.Tomcat框架概述和分析

Tomcat作爲典型的Servlet容器(除此之外還有Jetty、Weblogic、JBOSS等),分析和了解Tomcat的整體框架對於瞭解Servlet在Web開發中的作用很有幫助,有利於加深理解;

這裏寫圖片描述

Tomcat服務器模塊劃分十分清晰,主要有三個部分組成:
Service服務:可以具體到一個Web應用,它負責管理整個應用,包括各個組件生命週期的管理和組件之間的聯繫;

Connector:負責一個應用的對外事務,負責處理瀏覽器的請求和生成Response,以及完成將請求傳遞給具體的處理線程;

Container:實際處理每一個Connector傳遞過來的TCP請求,是一個鏈式的處理過程;

其他組件:包括Jasper、logging等組件,主要完成一些附加或者通用的工作;

由此,我們可以認爲Servlet作爲Container的一部分,主要任務是處理請求並得到響應的結果

2.Servlet的實現原理概述

爲了分析Servlet的實現原理,需要進一步分析Container的組成和工作流程,一般我們將Container叫做Servlet容器;

Container 容器的設計用的是典型的責任鏈的設計模式,它有四個子容器組件構成,分別是:Engine、Host、Context、Wrapper;這樣的形式類似於MVC框架中攔截器和過濾器的設計;

Engine:一個對的Servlet容器引擎;

Host:負責虛擬主機的域名映射,特別的對於通過war包發佈的應用,需要配置Host,一個Engine可以配置多個Host虛擬主機;

Context:應用的具體運行環境和依賴關係,一個Host可以有多個Context,對應多個應用(和通過Service發佈的多個應用略有不同);

Wrapper:對於單個Servlet的封裝,在Container中Servlet被封裝成Wrapper統一調用,Wrapper負責的工作包括的 Servlet 的裝載、初始化、執行以及資源回收;Wrapper是最底層的容器;

要簡單瞭解Servlet的實現原理,首先查看Wrapper的定義

在org.apache.catalina.core包中給出了StandardWrapper的實現:

這裏寫圖片描述

重點關注loadServlet()方法:

public synchronized Servlet loadServlet() throws ServletException {

        // Nothing to do if we already have an instance or an instance pool
        if (!singleThreadModel && (instance != null))
            return instance;

        PrintStream out = System.out;
        if (swallowOutput) {
            SystemLogHandler.startCapture();
        }

        Servlet servlet;
        try {
            long t1=System.currentTimeMillis();
            // Complain if no servlet class has been specified
            if (servletClass == null) {
                unavailable(null);
                throw new ServletException
                    (sm.getString("standardWrapper.notClass", getName()));
            }

            InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();
            try {
                servlet = (Servlet) instanceManager.newInstance(servletClass);
            } catch (ClassCastException e) {
                unavailable(null);
                // Restore the context ClassLoader
                throw new ServletException
                    (sm.getString("standardWrapper.notServlet", servletClass), e);
            } catch (Throwable e) {
                e = ExceptionUtils.unwrapInvocationTargetException(e);
                ExceptionUtils.handleThrowable(e);
                unavailable(null);

                // Added extra log statement for Bugzilla 36630:
                // http://issues.apache.org/bugzilla/show_bug.cgi?id=36630
                if(log.isDebugEnabled()) {
                    log.debug(sm.getString("standardWrapper.instantiate", servletClass), e);
                }

                // Restore the context ClassLoader
                throw new ServletException
                    (sm.getString("standardWrapper.instantiate", servletClass), e);
            }

            if (multipartConfigElement == null) {
                MultipartConfig annotation =
                        servlet.getClass().getAnnotation(MultipartConfig.class);
                if (annotation != null) {
                    multipartConfigElement =
                            new MultipartConfigElement(annotation);
                }
            }

            processServletSecurityAnnotation(servlet.getClass());

            // Special handling for ContainerServlet instances
            if ((servlet instanceof ContainerServlet) &&
                    (isContainerProvidedServlet(servletClass) ||
                            ((Context) getParent()).getPrivileged() )) {
                ((ContainerServlet) servlet).setWrapper(this);
            }

            classLoadTime=(int) (System.currentTimeMillis() -t1);

            if (servlet instanceof SingleThreadModel) {
                if (instancePool == null) {
                    instancePool = new Stack<Servlet>();
                }
                singleThreadModel = true;
            }

            initServlet(servlet);

            fireContainerEvent("load", this);

            loadTime=System.currentTimeMillis() -t1;
        } finally {
            if (swallowOutput) {
                String log = SystemLogHandler.stopCapture();
                if (log != null && log.length() > 0) {
                    if (getServletContext() != null) {
                        getServletContext().log(log);
                    } else {
                        out.println(log);
                    }
                }
            }
        }
        return servlet;

    }

關鍵代碼有以下幾條:


//創建實例
servlet = (Servlet) instanceManager.newInstance(servletClass);

//設置實例的Wrapper
((ContainerServlet) servlet).setWrapper(this);


//設置Servlet運行模式
if (servlet instanceof SingleThreadModel) {
   if (instancePool == null) {
          instancePool = new Stack<Servlet>();
      }
      singleThreadModel = true;
  }


//初始化並啓動Servlet,完成附加工作
initServlet(servlet);
fireContainerEvent("load", this); //可以看出Tomcat的典型的設計模式

DefaultServlet定義

在org\apache\catalina\servlets包中給出了DefaultServlet的定義,主要包括init()、doGet()、doPost()方法和抽象方法;

這裏寫圖片描述

public void init() throws ServletException {

        if (getServletConfig().getInitParameter("debug") != null)
            debug = Integer.parseInt(getServletConfig().getInitParameter("debug"));

        if (getServletConfig().getInitParameter("input") != null)
            input = Integer.parseInt(getServletConfig().getInitParameter("input"));

        if (getServletConfig().getInitParameter("output") != null)
            output = Integer.parseInt(getServletConfig().getInitParameter("output"));

        listings = Boolean.parseBoolean(getServletConfig().getInitParameter("listings"));

        if (getServletConfig().getInitParameter("readonly") != null)
            readOnly = Boolean.parseBoolean(getServletConfig().getInitParameter("readonly"));

        if (getServletConfig().getInitParameter("sendfileSize") != null)
            sendfileSize =
                Integer.parseInt(getServletConfig().getInitParameter("sendfileSize")) * 1024;

        fileEncoding = getServletConfig().getInitParameter("fileEncoding");

        globalXsltFile = getServletConfig().getInitParameter("globalXsltFile");
        contextXsltFile = getServletConfig().getInitParameter("contextXsltFile");
        localXsltFile = getServletConfig().getInitParameter("localXsltFile");
        readmeFile = getServletConfig().getInitParameter("readmeFile");

        if (getServletConfig().getInitParameter("useAcceptRanges") != null)
            useAcceptRanges = Boolean.parseBoolean(getServletConfig().getInitParameter("useAcceptRanges"));

        // Sanity check on the specified buffer sizes
        if (input < 256)
            input = 256;
        if (output < 256)
            output = 256;

        if (debug > 0) {
            log("DefaultServlet.init:  input buffer size=" + input +
                ", output buffer size=" + output);
        }

        // Load the proxy dir context.
        resources = (ProxyDirContext) getServletContext()
            .getAttribute(Globals.RESOURCES_ATTR);
        if (resources == null) {
            try {
                resources =
                    (ProxyDirContext) new InitialContext()
                    .lookup(RESOURCES_JNDI_NAME);
            } catch (NamingException e) {
                // Failed
                throw new ServletException("No resources", e);
            }
        }

        if (resources == null) {
            throw new UnavailableException("No resources");
        }

    }
@Override
protected void doGet(HttpServletRequest request,
                         HttpServletResponse response)
        throws IOException, ServletException {

        // Serve the requested resource, including the data content
        serveResource(request, response, true);

    }
@Override
    protected void doPost(HttpServletRequest request,
                          HttpServletResponse response)
        throws IOException, ServletException {
        doGet(request, response);
    }

3.Servlet在Tomcat中作用

1.Servlet容器通過將瀏覽器請求逐層傳遞,最終到達Servlet完成實際的處理邏輯;

2.Servlet的創建和管理由最底層容器Wrapper來完成;

3.Servlet通過統一接口來完成特定的操作,包括初始化、加載、doGet、doPost等;

注:關於Tomcat的框架,除了通過源碼分析,從配置文件server.xml的結構來分析是很清晰的:

這裏寫圖片描述

參考文章 Tomcat 系統架構與設計模式,第 1 部分 許 令波(阿里大佬)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章