国产成人精品18p,天天干成人网,无码专区狠狠躁天天躁,美女脱精光隐私扒开免费观看

詳解從源碼分析tomcat如何調用Servlet的初始化

發(fā)布時(shí)間:2021-08-15 18:37 來(lái)源: 閱讀:0 作者:g-Jack 欄目: 服務(wù)器 歡迎投稿:712375056

目錄

    引言

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

    注釋源碼地址:

    一、代碼啟動(dòng)tomcat

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

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

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

    二、tomcat框架

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

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

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

    3.1 方法 調用流程圖

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

    3.2 源碼分析

    1)通過(guò)反射獲得configContext監聽(tīng)器

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

     
        public Context  addWebapp(Host host, String contextPath, String docBase) {
            //通過(guò)反射獲得一個(gè)監聽(tīng)器  ContextConfig,
            //通過(guò)反射得到的一定是LifecycleListener的一個(gè)實(shí)現類(lèi),進(jìn)入getConfigClass得到實(shí)現類(lèi)(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) 獲得一個(gè)context容器(StandardContext)

    在下面代碼中,createContext()方法通過(guò)反射加載StandardContext容器,并且將設置監聽(tīng)ContextConfig, ctx.addLifecycleListener(config);

    public Context addWebapp(Host host, String contextPath, String docBase,
                LifecycleListener config) {
     
            silence(host, contextPath);
     
            //獲得一個(gè)context容器(StandardContext)
            Context ctx = createContext(host, contextPath);
            ctx.setPath(contextPath);
            ctx.setDocBase(docBase);
     
            if (addDefaultWebXmlToWebapp) {
                ctx.addLifecycleListener(getDefaultWebXmlListener());
            }
     
            ctx.setConfigFile(getWebappConfigFile(docBase, contextPath));
            //把監聽(tīng)器添加到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會(huì )逐層創(chuàng  )建容器,并維護容器父子關(guān)系
                getHost().addChild(ctx);
            } else {
                host.addChild(ctx);
            }
     
            return ctx;
        }

    3)維護各層容器

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

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

    getEngine().addChild(host); 方法選擇調用父類(lèi)containerBase中的addChild方法

      @Override
        public void addChild(Container child) {
            if (Globals.IS_SECURITY_ENABLED) {
                PrivilegedAction<Void> dp =
                    new PrivilegedAddChild(child);
                AccessController.doPrivileged(dp);
            } else {
                //這里的child 參數是 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);
        }

    四、啟動(dòng)容器(tomcat.start())

    4.1、方法調用流程圖

    4.2、源碼分析

    說(shuō)明:StandardServer 、StandardService、StandardEngine等容器都是繼承LifecycleBase

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

    1)逐層啟動(dòng)容器

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

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

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

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

    從上面我們知道現在我們調用的是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) {
                //啟動(dòng) service容器,一個(gè)tomcat中可以配置多個(gè)service容器,每個(gè)service容器都對應這我們的一個(gè)服務(wù)應用
                for (Service service : services) {
                    //對應 StandardService.startInternal()
                    service.start();
                }
            }
        }

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

    2)ContainerBase中的startInternal()方法 核心代碼,從這開(kāi)始啟動(dòng)StandardContext容器

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

    new StartChild(child)) 方法開(kāi)始啟動(dòng)StandardContext容器

        private static class StartChild implements Callable<Void> {
     
            private Container child;
     
            public StartChild(Container child) {
                this.child = child;
            }
     
            @Override
            public Void call() throws LifecycleException {
                //開(kāi)始啟動(dòng)context,實(shí)際調用 StandardContext.startInternal()
                child.start();
                return null;
            }
        }

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

       protected void fireLifecycleEvent(String type, Object data) {
            LifecycleEvent event = new LifecycleEvent(this, type, data);
            //lifecycleListeners 在addwebapp方法的第一步中,設置的監聽(tīng)的 contextConfig對象
            for (LifecycleListener listener : lifecycleListeners) {
                //這里調用的是 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的內容解析
                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的加載和解析,同時(shí)加載xml中配置的servlet并且封裝成wrapper對象。

    3)、啟動(dòng)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 {
                        //通過(guò) load 方法  最終會(huì )調用 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;
     
        }

    通過(guò) load 方法 最終會(huì )調用 servlet的init方法。

    五、總結

    上面內容就是整個(gè)tomcat是如何調用servlet初始化方法的流程,整個(gè)流程小編的理解,如果有錯誤,歡迎指正,小編已經(jīng)在源碼中重要部分進(jìn)行了注釋?zhuān)匀绻行枰母魑蛔x者,可以下載我的注釋 源碼,注釋源碼地址:

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

    免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自本網(wǎng)站內容采集于網(wǎng)絡(luò )互聯(lián)網(wǎng)轉載等其它媒體和分享為主,內容觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如侵犯了原作者的版權,請告知一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容,聯(lián)系我們QQ:712375056,同時(shí)歡迎投稿傳遞力量。

    to
    人人爽人人澡人人人人妻| 亚洲婷婷五月综合狠狠| 色欲国产麻豆一精品一AV一免费| 日韩AV东京社区男人的天堂| 日韩VA中文字幕无码电影| 太深太粗太爽太猛了视频免费观看|