Tomcat&Servlet工作原理之讀書筆記

Tomcat是Servlet最受歡迎的容器之一,他們之間彼此依存,為了解耦,通過標準化接口相互協(xié)作。

Tomcat的核心組件是ConnectorContainer.其中,Connector組件是可以被替換的,這樣就給設計者提供了比較靈活的設計模式,一個Container可以對應多個Connector,它們一起組成了一個Servcie,就可以對外提供服務了。Servcie還要一個生存環(huán)境,那就是Server。整個Tomcat的生命周期是由Server控制的。

Tomcat的總體結構.png

Tomcat容器分為4個等級:
Container 容器 ==> Engine容器 ==> Host ==> Context
Tomcat容器模型.png

以Servcie作為“婚姻”

Container:小仙女 Connector:男生
他們是一對快樂的情侶,Connector作為男生,平時主要負責對外交流,Container小仙女主要負責內(nèi)部的事物,他們兩彼此精誠合作,生活也是井井有條。
他們兩有了Service這個結婚證呢,就是受外界承認的小夫妻了,無論是在法律上還是生活形式上,他們都是一體的了。


Service接口方法列表.png

為了讓生活更加豐富多彩,他們更加細致的規(guī)劃了自己的生活,就是StandardService,同時實現(xiàn)了Service接口,還實現(xiàn)了Lifecycle接口。

setContainer方法的源碼如下:

 @Override
    public void setContainer(Engine engine) {
        Engine oldEngine = this.engine;
        if (oldEngine != null) {
            oldEngine.setService(null);
        }
        this.engine = engine;
        if (this.engine != null) {
            this.engine.setService(this);
        }
        if (getState().isAvailable()) {
            if (this.engine != null) {
                try {
                    this.engine.start();
                } catch (LifecycleException e) {
                    log.warn(sm.getString("standardService.engine.startFailed"), e);
                }
            }
            // Restart MapperListener to pick up new engine.
            try {
                mapperListener.stop();
            } catch (LifecycleException e) {
                log.warn(sm.getString("standardService.mapperListener.stopFailed"), e);
            }
            try {
                mapperListener.start();
            } catch (LifecycleException e) {
                log.warn(sm.getString("standardService.mapperListener.startFailed"), e);
            }
            if (oldEngine != null) {
                try {
                    oldEngine.stop();
                } catch (LifecycleException e) {
                    log.warn(sm.getString("standardService.engine.stopFailed"), e);
                }
            }
        }

        // Report this property change to interested listeners
        support.firePropertyChange("container", oldEngine, this.engine);
    }

這段代碼邏輯很簡單,首先判斷當前這個Service有沒有關聯(lián)Container,如果已經(jīng)有關聯(lián),就去掉——>oldEngine.setService(null);如果這個Container已經(jīng)啟動了,則結束它的生命周期——>oldEngine.stop();然后再啟動新的關聯(lián)、初始化并開始這個新的Container得生命周期。
addContainer方法的源碼如下:

@Override
    public void addConnector(Connector connector) {
        synchronized (connectorsLock) {
            connector.setService(this);  //設置關聯(lián)關系
            Connector results[] = new Connector[connectors.length + 1]; //初始化工作
            System.arraycopy(connectors, 0, results, 0, connectors.length);
            results[connectors.length] = connector;
            connectors = results;
            if (getState().isAvailable()) {
                try {
                    connector.start();
                } catch (LifecycleException e) {
                    log.error(sm.getString(
                            "standardService.connector.startFailed",
                            connector), e);
                }
            }
            // Report this property change to interested listeners
            support.firePropertyChange("connector", null, connector);
        }
    }

以Server為“居”

既然Container小仙女和Connector有了合法的夫妻關系,那么他們也需要一個基本的條件去維系這段“婚姻”,他們需要一個實體的家,Server.
Server要完成的任務很簡答,就是提高一個接口讓其它程序可以訪問這個Service集合,同時也要維護他所包含的Service的生命周期。
StandardServer是他的標準實現(xiàn)類,同時也繼承了LifecycleMBeanBase。
addService方法源碼如下:

@Override
    public void addService(Service service) {
        service.setServer(this);  //由此可以看出Service和Server是相互關聯(lián)的
        synchronized (servicesLock) {
            Service results[] = new Service[services.length + 1];
            System.arraycopy(services, 0, results, 0, services.length);
            results[services.length] = service;
            services = results;
            if (getState().isAvailable()) {
                try {
                    service.start();
                } catch (LifecycleException e) {
                    // Ignore
                }
            }
            // Report this property change to interested listeners
            support.firePropertyChange("service", null, service);
        }
    }

組件的生命線“Lifecycle”

Tomcat中組件的生命周期都是由Lifecycle接口來控制的,組件只要繼承這個接口并實現(xiàn)其中的方法就可以統(tǒng)一被擁有他的組件控制了。這樣一層一層到最高級的組件就可以控制Tomcat中所有組件的生命周期了,這個最高級的組件就是Server,控制Server的是Startup,也就是啟動和關閉Tomcat.


Lifecycle接口方法.png

除了控制生命周期的start和stop方法外,還有一個監(jiān)聽機制,在生命周期開始和結束時做一些額外的操作。
Lifecycle接口實現(xiàn)都在其它組件中。

負責外部交流的Connector

接受瀏覽器發(fā)過來的TCP請求,創(chuàng)建一個Request和Response對象分別用于請求端交換數(shù)據(jù),然后將產(chǎn)生一個線程來處理這個請求并把產(chǎn)生的Request和Response對象傳給處理這個請求的線程,之后處理這個線程就是Container的事情了。

Servlet容器Container

Container是容器的父接口,所有容器必須實現(xiàn)這個接口,設計是典型的責任鏈的設計模式,他有4個容器組成,分別是Engine、Context和Wrapper,這四個組件不是平行的,而是父子關系。通常一個Servlet class對應一個Wrapper.

Engine容器

標準實現(xiàn)類時StandardEngine,Engine沒有父容器了,調(diào)用setParent就會出錯

 @Override
    public void setParent(Container container) {

        throw new IllegalArgumentException
            (sm.getString("standardEngine.notParent"));

    }

添加子容器也只能是Host類型

 @Override
    public void addChild(Container child) {

        if (!(child instanceof Host))
            throw new IllegalArgumentException
                (sm.getString("standardEngine.notHost"));
        super.addChild(child);

    }

Host容器

Host是Engine的子容器,一個Host在Engine中代表一個虛擬主機,這個虛擬主機的作用就是運行多個應用,它負責安裝和展開這些應用,而且進行標識以便于區(qū)分。他的子容器是Context,它除了關聯(lián)子容器外,還有就是保存一個主機應該有的信息。

Context容器

他具備Servlet運行的基本環(huán)境,是在管理Servlet實例,Servlet實例在Context容器中是以Wrapper出現(xiàn)的。

Wrapper容器

Wrapper代表一個Servlet,包括Servlet的裝載、初始化、執(zhí)行和資源回收。是最底層的,沒有再底層的容器了。
loadServlet是個很重要的方法,代碼片段如下:

public synchronized Servlet loadServlet() throws ServletException {
     ······
        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);
                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<>();
                }
                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頂層類關聯(lián)圖.png

由上圖可知,Servlet規(guī)范就是基于上面的幾個類運轉的,與Servlet主動關聯(lián)的是三個類:ServletConfig、ServletRequest、ServletResponse,這3個類都是通過容器傳遞給Servlet的。
用戶從瀏覽器向服務器發(fā)起一個請求通常會包含如下信息:
http://hostname:port/contextpath/servletpath
hostname:port用來與服務器建立TCP連接,而后面的URL才用來選擇服務器的哪個子容器服務用戶的請求。
這種映射關系專門由一個類來完成,這個類就是org.apache.servlet.util.http.mapper,這個類保存了Tomcat的Container所有子容器的信息。

  • 創(chuàng)建一個Context容器,很重要的一個配置是ContextConfig,負責整個Web應用的配置文件解析工作。
  • Context init(一個Context對應一個web),Context容器的Listener將被調(diào)用,ContextConfig調(diào)用了LifecycleListener接口。
  • Context執(zhí)行startInternal方法
  • Web應用的初始化工作,ContextConfig中的configureStart方法實現(xiàn)。
  • 創(chuàng)建Servlet對象
  • 初始化Servlet

Servlet中的url-pattern

匹配順序:精確匹配 路徑匹配 后綴匹配

程序媛小白一枚,如有錯誤,煩請批評指正!(#.#)

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容