亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

詳解從源碼分析tomcat如何調(diào)用Servlet的初始化

 更新時間:2021年04月30日 11:08:48   作者:g-Jack  
這篇文章主要介紹了詳解從源碼分析tomcat如何調(diào)用Servlet的初始化,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

引言

上一篇博客我們將tomcat源碼在本地成功運行了,所以在本篇博客中我們從源碼層面分析,tomcat在啟動的過程中,是如何初始化servlet容器的。我們平常都是將我們的服務(wù)部署到 tomcat中,然后修改一下配置文件,啟動就可以對外提供 服務(wù)了,但是我們對于其中的一些流程并不是非常的了解,例如如何加載的web.xml等。這是我們分析servlet 和 sringMVC必不可少的過程。

注釋源碼地址:https://github.com/good-jack/tomcat_source/tree/master

一、代碼啟動tomcat

平常我們不論是Windows還是linux,我們都是通過腳本來啟動tomcat,這對于我們分析源碼不是很友好,所以我們 需要通過代碼啟動,啟動代碼如下:

Tomcat tomcat = new Tomcat();
        tomcat.setPort(8080);
        //new 出各層容器,并且維護(hù)各層容器的關(guān)系
        tomcat.addWebapp("/","/");
        tomcat.start();
        //阻塞監(jiān)聽端口
        tomcat.getServer().await();

啟動代碼還是非常非常簡單,從代碼中我們就可以看出,我們本篇博客主要分析的就是 addWebapp()方法和start()方法,通過這兩個方法我們就可以找到servlet容器是在什么時候被初始化的。

二、tomcat框架

在我們進(jìn)行分析上面兩個方法之前,我們先總結(jié)一下tomcat的基礎(chǔ)框架,其實從我們非常熟悉的 server.xml配置文件中就可以知道,tomcat就是一系列父子容器組成:

Server ---> Service --> Connector Engine addChild---> context(servlet容器) ,這就是我們從配置文件中分析出來的幾個容器,tomcat啟動時候就是逐層啟動容器。

三、創(chuàng)建容器(addWebapp())

3.1 方法 調(diào)用流程圖

上面的流程圖就是,從源碼中逐步分析出來的幾個重要的方法,這對于我們分析源碼非常有幫助。

3.2 源碼分析

1)通過反射獲得configContext監(jiān)聽器

方法路徑:package org.apache.catalina.startup.Tomcat.addWebapp(Host host, String contextPath, String docBase);

 
    public Context  addWebapp(Host host, String contextPath, String docBase) {
        //通過反射獲得一個監(jiān)聽器  ContextConfig,
        //通過反射得到的一定是LifecycleListener的一個實現(xiàn)類,進(jìn)入getConfigClass得到實現(xiàn)類(org.apache.catalina.startup.ContextConfig)
        LifecycleListener listener = null;
        try {
            Class<?> clazz = Class.forName(getHost().getConfigClass());
            listener = (LifecycleListener) clazz.getConstructor().newInstance();
        } catch (ReflectiveOperationException e) {
            // Wrap in IAE since we can't easily change the method signature to
            // to throw the specific checked exceptions
            throw new IllegalArgumentException(e);
        }
 
        return addWebapp(host, contextPath, docBase, listener);
    }

2) 獲得一個context容器(StandardContext)

在下面代碼中,createContext()方法通過反射加載StandardContext容器,并且將設(shè)置監(jiān)聽ContextConfig, ctx.addLifecycleListener(config);

public Context addWebapp(Host host, String contextPath, String docBase,
            LifecycleListener config) {
 
        silence(host, contextPath);
 
        //獲得一個context容器(StandardContext)
        Context ctx = createContext(host, contextPath);
        ctx.setPath(contextPath);
        ctx.setDocBase(docBase);
 
        if (addDefaultWebXmlToWebapp) {
            ctx.addLifecycleListener(getDefaultWebXmlListener());
        }
 
        ctx.setConfigFile(getWebappConfigFile(docBase, contextPath));
        //把監(jiān)聽器添加到context中去
        ctx.addLifecycleListener(config);
 
        if (addDefaultWebXmlToWebapp && (config instanceof ContextConfig)) {
            // prevent it from looking ( if it finds one - it'll have dup error )
            ((ContextConfig) config).setDefaultWebXml(noDefaultWebXmlPath());
        }
 
        if (host == null) {
            //getHost會逐層創(chuàng)建容器,并維護(hù)容器父子關(guān)系
            getHost().addChild(ctx);
        } else {
            host.addChild(ctx);
        }
 
        return ctx;
    }

3)維護(hù)各層容器

getHost()方法中得到各層容器,并且維護(hù)父親容器關(guān)系,其中包括,server容器、Engine容器。并且將StandardContext容器通過getHost().addChild(ctx); 調(diào)用containerBase中的addChild()方法維護(hù)在 children 這個map中。

  public Host getHost() {
        //將每一層的容器都new 出來
        Engine engine = getEngine();
        if (engine.findChildren().length > 0) {
            return (Host) engine.findChildren()[0];
        }
 
        Host host = new StandardHost();
        host.setName(hostname);
        //維護(hù)tomcat中的父子容器
        getEngine().addChild(host);
        return host;
    }

getEngine().addChild(host); 方法選擇調(diào)用父類containerBase中的addChild方法

  @Override
    public void addChild(Container child) {
        if (Globals.IS_SECURITY_ENABLED) {
            PrivilegedAction<Void> dp =
                new PrivilegedAddChild(child);
            AccessController.doPrivileged(dp);
        } else {
            //這里的child 參數(shù)是 context 容器
            addChildInternal(child);
        }
    }

addChildInternal()方法的 核心代碼

 private void addChildInternal(Container child) {
 
        if( log.isDebugEnabled() )
            log.debug("Add child " + child + " " + this);
        synchronized(children) {
            if (children.get(child.getName()) != null)
                throw new IllegalArgumentException("addChild:  Child name '" +
                                                   child.getName() +
                                                   "' is not unique");
            child.setParent(this);  // May throw IAE
            children.put(child.getName(), child);
    }

四、啟動容器(tomcat.start())

4.1、方法調(diào)用流程圖

4.2、源碼分析

說明:StandardServer 、StandardService、StandardEngine等容器都是繼承LifecycleBase

所以這里是模板模式的經(jīng)典應(yīng)用

1)逐層啟動容器

此時的server對應(yīng)的是我們前面創(chuàng)建的StandardServer

  public void start() throws LifecycleException {
        //防止server容器沒有創(chuàng)建
        getServer();
        //獲得connector容器,并且將得到的connector容器設(shè)置到service容器中
        getConnector();
        //這里的start的實現(xiàn)是在 LifecycleBase類中實現(xiàn)
        //LifecycleBase方法是一個模板方法,在tomcat啟動流程中非常關(guān)鍵
        server.start();
    }

2) 進(jìn)入start方法

進(jìn)入LifecycelBase中的start方法,其中核心方法是startInternal。

從上面我們知道現(xiàn)在我們調(diào)用的是StandardServer容器的startInternal()方法,所以我們這里選擇的是StandardServer

方法路徑:org.apache.catalina.core.StandardServer.startInternal()

protected void startInternal() throws LifecycleException {
 
        fireLifecycleEvent(CONFIGURE_START_EVENT, null);
        setState(LifecycleState.STARTING);
 
        globalNamingResources.start();
 
        // Start our defined Services
        synchronized (servicesLock) {
            //啟動 service容器,一個tomcat中可以配置多個service容器,每個service容器都對應(yīng)這我們的一個服務(wù)應(yīng)用
            for (Service service : services) {
                //對應(yīng) StandardService.startInternal()
                service.start();
            }
        }
    }

從上面代碼中我們可以看出,啟動server容器的時候需要啟動子容器 service容器,從這里開始就是容器 逐層向向內(nèi)引爆,所以接下來就是開始依次調(diào)用各層容器的star方法。在這里就不在贅述。

2)ContainerBase中的startInternal()方法 核心代碼,從這開始啟動StandardContext容器

 // Start our child containers, if any
        //在addWwbapp的流程中 addChild方法中加入的,所以這里需要找出來
        //這里找出來的就是 context 容器
        Container children[] = findChildren();
        List<Future<Void>> results = new ArrayList<>();
        for (Container child : children) {
            //通過線程池 異步的方式啟動線程池 開始啟動 context容器,進(jìn)入new StartChild
            results.add(startStopExecutor.submit(new StartChild(child)));
        }

new StartChild(child)) 方法開始啟動StandardContext容器

    private static class StartChild implements Callable<Void> {
 
        private Container child;
 
        public StartChild(Container child) {
            this.child = child;
        }
 
        @Override
        public Void call() throws LifecycleException {
            //開始啟動context,實際調(diào)用 StandardContext.startInternal()
            child.start();
            return null;
        }
    }

StandardContext.startInternal() 方法中的核心代碼:

   protected void fireLifecycleEvent(String type, Object data) {
        LifecycleEvent event = new LifecycleEvent(this, type, data);
        //lifecycleListeners 在addwebapp方法的第一步中,設(shè)置的監(jiān)聽的 contextConfig對象
        for (LifecycleListener listener : lifecycleListeners) {
            //這里調(diào)用的是 contextConfig的lifecycleEvent()方法
            listener.lifecycleEvent(event);
        }
    }

進(jìn)入到 contextConfig中的lifecycleEvent()方法

public void lifecycleEvent(LifecycleEvent event) {
 
        // Identify the context we are associated with
        try {
            context = (Context) event.getLifecycle();
        } catch (ClassCastException e) {
            log.error(sm.getString("contextConfig.cce", event.getLifecycle()), e);
            return;
        }
 
        // Process the event that has occurred
        if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
            //完成web.xml的內(nèi)容解析
            configureStart();
        } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
            beforeStart();
        } else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {
            // Restore docBase for management tools
            if (originalDocBase != null) {
                context.setDocBase(originalDocBase);
            }
        } else if (event.getType().equals(Lifecycle.CONFIGURE_STOP_EVENT)) {
            configureStop();
        } else if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) {
            init();
        } else if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) {
            destroy();
        }
 
    }

在上面方法中,完成對web.xml的加載和解析,同時加載xml中配置的servlet并且封裝成wrapper對象。

3)、啟動servlet容器,StandardContext.startInternal() 中的 loadOnStartup(findChildren())方法

public boolean loadOnStartup(Container children[]) {
 
        // Collect "load on startup" servlets that need to be initialized
        TreeMap<Integer, ArrayList<Wrapper>> map = new TreeMap<>();
        for (Container child : children) {
            //這里的 Wrapper就是 我們前面封裝的 servlet
            Wrapper wrapper = (Wrapper) child;
            int loadOnStartup = wrapper.getLoadOnStartup();
            if (loadOnStartup < 0) {
                continue;
            }
            Integer key = Integer.valueOf(loadOnStartup);
            ArrayList<Wrapper> list = map.get(key);
            if (list == null) {
                list = new ArrayList<>();
                map.put(key, list);
            }
            list.add(wrapper);
        }
 
        // Load the collected "load on startup" servlets
        for (ArrayList<Wrapper> list : map.values()) {
            for (Wrapper wrapper : list) {
                try {
                    //通過 load 方法  最終會調(diào)用 servlet的init方法
                    wrapper.load();
                } catch (ServletException e) {
                    getLogger().error(sm.getString("standardContext.loadOnStartup.loadException",
                          getName(), wrapper.getName()), StandardWrapper.getRootCause(e));
                    // NOTE: load errors (including a servlet that throws
                    // UnavailableException from the init() method) are NOT
                    // fatal to application startup
                    // unless failCtxIfServletStartFails="true" is specified
                    if(getComputedFailCtxIfServletStartFails()) {
                        return false;
                    }
                }
            }
        }
        return true;
 
    }

通過 load 方法 最終會調(diào)用 servlet的init方法。

五、總結(jié)

上面內(nèi)容就是整個tomcat是如何調(diào)用servlet初始化方法的流程,整個流程小編的理解,如果有錯誤,歡迎指正,小編已經(jīng)在源碼中重要部分進(jìn)行了注釋,所以如果有需要的各位讀者,可以下載我的注釋 源碼,注釋源碼地址:

https://github.com/good-jack/tomcat_source/tree/master

到此這篇關(guān)于詳解從源碼分析tomcat如何調(diào)用Servlet的初始化的文章就介紹到這了,更多相關(guān)tomcat調(diào)用Servlet初始化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Linux系統(tǒng)下安裝三個或者多個tomcat(步驟詳細(xì))

    Linux系統(tǒng)下安裝三個或者多個tomcat(步驟詳細(xì))

    這篇文章主要介紹了Linux系統(tǒng)下安裝三個或者多個tomcat(步驟詳細(xì)),文中通過步驟介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • 詳解Tomcat中查看JVM內(nèi)存使用情況

    詳解Tomcat中查看JVM內(nèi)存使用情況

    這篇文章主要介紹了Tomcat中查看JVM內(nèi)存使用情況,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-03-03
  • nginx+tomcat單個域名及多個域名配置教程

    nginx+tomcat單個域名及多個域名配置教程

    這篇文章主要介紹了nginx+tomcat單個域名及多個域名配置教程,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2017-12-12
  • Tomcat中使用ipv6地址的示例代碼

    Tomcat中使用ipv6地址的示例代碼

    在公司的一次項目改造過程中,需要將原來的IPV6替換成IPV4,本文就詳細(xì)的介紹Tomcat中使用ipv6地址的示例代碼,具有一定的參考價值,感興趣的可以了解一下
    2022-05-05
  • tomcat點擊startup.bat出現(xiàn)閃退的原因及解決方法

    tomcat點擊startup.bat出現(xiàn)閃退的原因及解決方法

    本文主要介紹了tomcat點擊startup.bat出現(xiàn)閃退的原因及解決方法,文中通過圖文介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2023-09-09
  • Linux下重啟多個 tomcat 服務(wù)的腳本(推薦)

    Linux下重啟多個 tomcat 服務(wù)的腳本(推薦)

    由于修改tomcat的配置文件或手動操作數(shù)據(jù)庫數(shù)據(jù)后,tomcat的緩存和redis的緩存很嚴(yán)重,需要經(jīng)常重啟tomcat來釋放緩存,經(jīng)常就是手動重啟。下面給大家分享Linux下重啟多個 tomcat 服務(wù)的腳本,一起看看吧
    2017-06-06
  • Linux下Tomcat8如何修改JVM內(nèi)存配置

    Linux下Tomcat8如何修改JVM內(nèi)存配置

    這篇文章主要介紹了Linux下Tomcat8如何修改JVM內(nèi)存配置問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • idea專業(yè)版和idea社區(qū)版整合Tomcat并將war包部署

    idea專業(yè)版和idea社區(qū)版整合Tomcat并將war包部署

    IDEA是一個功能完善的Java開發(fā)工具,除了具備有良好的代碼開發(fā)提示之外,還可以直接在IDEA中集成并啟動Tomcat實現(xiàn)程序的自動部署,本文主要介紹了idea專業(yè)版和idea社區(qū)版整合Tomcat并將war包部署,感興趣的可以了解一下
    2023-11-11
  • Apache及Tomcat搭建集群環(huán)境過程解析

    Apache及Tomcat搭建集群環(huán)境過程解析

    這篇文章主要介紹了Apache及Tomcat搭建集群環(huán)境過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-10-10
  • tomcat同時使用http和https訪問的配置方法

    tomcat同時使用http和https訪問的配置方法

    這篇文章主要介紹了tomcat同時使用http和https訪問的配置方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-07-07

最新評論