Tomcat與SpringMVC結合分析(一)

一:環境配置

  我用的分析的工具是eclipse,源碼用maven的自動下載源碼的功能.而後在maven中添加相應的dependence.這樣但咱們ctrl+leftmouse的時候就會自動幫咱們下載源代碼.java

  SpringMVC版本爲3.2.4,雖然如今基本用的是Spring4.x或是SpringBoot,但基本的原理沒有變更太大因此有典型性.web

  Tomcat的版本爲8.0.47.apache

  servlet-api的版本爲3.1.0.bootstrap

二:Tomcat的基本原理

  1.調用在Bootstrap中的主函數main()程序入口調用自身的start()方法,start()方法中實例化org.apache.catalina.startup.Catalina類調用其start()方法,Catalinastart()中調用load()載入tomcat文件夾目錄下conf文件夾下的server.xml並建立Server,而後調用Serverstart()方法,到這裏咱們的tomcat就運行起來了.api

  2.Server類表明整個Tomcat服務器,這裏有必要介紹一下爲何TomcatServlet容器,Tomcat中包含四個容器,分別爲Engine,Host,Context,Wrapper.EngineHost的父容器,依次類推.四者都繼承自Container接口.通常在Tomcat中一個Engine實例中包含一個Host實例,一個Host實例中包含一個Context實例,一個Context表明一個WEB程序幷包含多個Wrapper實例,一個Wrapper表明一個Servlet.數組

  3.一次請求的大題流程是這樣的,首先由Connector獲取到Http請求,封裝ServletRequestServletResponse對象並派發給Context管道中的全部Valve,執行完全部Valve後執行基礎Valve根據請求的url映射到對應的Wrapper,而後調用個Wrapper管道中的全部Valve,最後調用基礎Valve在如這個Wrapper類所封裝的Servlet,實例化後調用器Service方法對用戶的請求進行處理,最後將結果負載在response中返回給用戶.tomcat

三:從源碼開始分析Tomcat運行原理

1.Bootsrap做爲整個Tomcat的入口,實例化Catalinna而後載入並啓動Server容器。

   public static void main(String args[])
    {
        if (daemon == null)
        {
            // Don't set daemon until init() has completed
            Bootstrap bootstrap = new Bootstrap();
            try
            {
                bootstrap.init();
            } catch (Throwable t)
            {
                handleThrowable(t);
                t.printStackTrace();
                return;
            }
            daemon = bootstrap;
        } else
        {
            // When running as a service the call to stop will be on a new
            // thread so make sure the correct class loader is used to prevent
            // a range of class not found exceptions.
            Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
        }
        try
        {
            String command = "start";
            if (args.length > 0)
            {
                command = args[args.length - 1];
            }

            if (command.equals("startd"))
            {
                args[args.length - 1] = "start";
                daemon.load(args);
                daemon.start();
            } else if (command.equals("stopd"))
            {
                args[args.length - 1] = "stop";
                daemon.stop();
            } else if (command.equals("start"))
            {
                // 在這裏讀取命令行參數,而後啓動。
                daemon.setAwait(true);
                daemon.load(args);
                daemon.start();
            } else if (command.equals("stop"))
            {
                daemon.stopServer(args);
            } else if (command.equals("configtest"))
            {
                daemon.load(args);
                if (null == daemon.getServer())
                {
                    System.exit(1);
                }
                System.exit(0);
            } else
            {
                log.warn("Bootstrap: command \"" + command + "\" does not exist.");
            }
        } catch (Throwable t)
        {
            // Unwrap the Exception for clearer error reporting
            if (t instanceof InvocationTargetException && t.getCause() != null)
            {
                t = t.getCause();
            }
            handleThrowable(t);
            t.printStackTrace();
            System.exit(1);
        }
    }

第一次啓動是會調用內部生成的Bootstrap類,調用start方法。服務器

    public void start() throws Exception
    {
        if (catalinaDaemon == null)
            init();

        Method method = catalinaDaemon.getClass().getMethod("start", (Class[]) null);
        method.invoke(catalinaDaemon, (Object[]) null);

    }

在init方法中實例化Catalinna類而後經過反射調用其start方法。app

    public void init() throws Exception
    {
        initClassLoaders();

        Thread.currentThread().setContextClassLoader(catalinaLoader);

        SecurityClassLoad.securityClassLoad(catalinaLoader);

        // Load our startup class and call its process() method
        if (log.isDebugEnabled())
            log.debug("Loading startup class");
        Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
        Object startupInstance = startupClass.newInstance();

        // Set the shared extensions class loader
        if (log.isDebugEnabled())
            log.debug("Setting startup class properties");
        String methodName = "setParentClassLoader";
        Class<?> paramTypes[] = new Class[1];
        paramTypes[0] = Class.forName("java.lang.ClassLoader");
        Object paramValues[] = new Object[1];
        paramValues[0] = sharedLoader;
        Method method = startupInstance.getClass().getMethod(methodName, paramTypes);
        method.invoke(startupInstance, paramValues);

        catalinaDaemon = startupInstance;
    }

start()中調用load()方法載入了Server,而後調用了其start()方法啓動了Tomcat.框架

   public void start()
    {

        if (getServer() == null)
        {
            load();
        }

        if (getServer() == null)
        {
            log.fatal("Cannot start server. Server instance is not configured.");
            return;
        }

        long t1 = System.nanoTime();

        // Start the new server
        try
        {
            getServer().start();
        } catch (LifecycleException e)
        {
            log.fatal(sm.getString("catalina.serverStartFail"), e);
            try
            {
                getServer().destroy();
            } catch (LifecycleException e1)
            {
                log.debug("destroy() failed for failed Server ", e1);
            }
            return;
        }

        long t2 = System.nanoTime();
        if (log.isInfoEnabled())
        {
            log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
        }

        // Register shutdown hook
        if (useShutdownHook)
        {
            if (shutdownHook == null)
            {
                shutdownHook = new CatalinaShutdownHook();
            }
            Runtime.getRuntime().addShutdownHook(shutdownHook);

            // If JULI is being used, disable JULI's shutdown hook since
            // shutdown hooks run in parallel and log messages may be lost
            // if JULI's hook completes before the CatalinaShutdownHook()
            LogManager logManager = LogManager.getLogManager();
            if (logManager instanceof ClassLoaderLogManager)
            {
                ((ClassLoaderLogManager) logManager).setUseShutdownHook(false);
            }
        }

        if (await)
        {
            await();
            stop();
        }
    }

2.下面讓咱們仔細分析Tomcat是怎麼以優雅的方式配置Server類的。

首先回到上面的load方法。

   public void load()
    {
        long t1 = System.nanoTime();

        initDirs();

        // Before digester - it may be needed
        initNaming();

        // Create and execute our Digester
        Digester digester = createStartDigester();

        InputSource inputSource = null;
        InputStream inputStream = null;
        File file = null;
        try
        {
            try
            {
                file = configFile();
                inputStream = new FileInputStream(file);
                inputSource = new InputSource(file.toURI().toURL().toString());
            } catch (Exception e)
            {
                if (log.isDebugEnabled())
                {
                    log.debug(sm.getString("catalina.configFail", file), e);
                }
            }
            if (inputStream == null)
            {
                try
                {
                    inputStream = getClass().getClassLoader().getResourceAsStream(getConfigFile());
                    inputSource = new InputSource(getClass().getClassLoader().getResource(getConfigFile()).toString());
                } catch (Exception e)
                {
                    if (log.isDebugEnabled())
                    {
                        log.debug(sm.getString("catalina.configFail", getConfigFile()), e);
                    }
                }
            }

            // This should be included in catalina.jar
            // Alternative: don't bother with xml, just create it manually.
            if (inputStream == null)
            {
                try
                {
                    inputStream = getClass().getClassLoader().getResourceAsStream("server-embed.xml");
                    inputSource = new InputSource(getClass().getClassLoader().getResource("server-embed.xml").toString());
                } catch (Exception e)
                {
                    if (log.isDebugEnabled())
                    {
                        log.debug(sm.getString("catalina.configFail", "server-embed.xml"), e);
                    }
                }
            }

            if (inputStream == null || inputSource == null)
            {
                if (file == null)
                {
                    log.warn(sm.getString("catalina.configFail", getConfigFile() + "] or [server-embed.xml]"));
                } else
                {
                    log.warn(sm.getString("catalina.configFail", file.getAbsolutePath()));
                    if (file.exists() && !file.canRead())
                    {
                        log.warn("Permissions incorrect, read permission is not allowed on the file.");
                    }
                }
                return;
            }

            try
            {
                inputSource.setByteStream(inputStream);
                digester.push(this);
                digester.parse(inputSource);
            } catch (SAXParseException spe)
            {
                log.warn("Catalina.start using " + getConfigFile() + ": " + spe.getMessage());
                return;
            } catch (Exception e)
            {
                log.warn("Catalina.start using " + getConfigFile() + ": ", e);
                return;
            }
        } finally
        {
            if (inputStream != null)
            {
                try
                {
                    inputStream.close();
                } catch (IOException e)
                {
                    // Ignore
                }
            }
        }

        getServer().setCatalina(this);
        getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
        getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());

        // Stream redirection
        initStreams();

        // Start the new server
        try
        {
            getServer().init();
        } catch (LifecycleException e)
        {
            if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
            {
                throw new java.lang.Error(e);
            } else
            {
                log.error("Catalina.start", e);
            }
        }

        long t2 = System.nanoTime();
        if (log.isInfoEnabled())
        {
            log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");
        }
    }

這裏最核心的部分Digester digester = createStartDigester(),它建立了一個Digester,在createStartDigester內部。

   protected Digester createStartDigester()
    {
        long t1 = System.currentTimeMillis();
        // Initialize the digester
        Digester digester = new Digester();
        digester.setValidating(false);
        digester.setRulesValidation(true);
        HashMap<Class<?>, List<String>> fakeAttributes = new HashMap<>();
        ArrayList<String> attrs = new ArrayList<>();
        attrs.add("className");
        fakeAttributes.put(Object.class, attrs);
        digester.setFakeAttributes(fakeAttributes);
        digester.setUseContextClassLoader(true);

        // Configure the actions we will be using
        digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer", "className");
        digester.addSetProperties("Server");
        digester.addSetNext("Server", "setServer", "org.apache.catalina.Server");

        digester.addObjectCreate("Server/GlobalNamingResources", "org.apache.catalina.deploy.NamingResourcesImpl");
        digester.addSetProperties("Server/GlobalNamingResources");
        digester.addSetNext("Server/GlobalNamingResources", "setGlobalNamingResources", "org.apache.catalina.deploy.NamingResourcesImpl");

        digester.addObjectCreate("Server/Listener", null, "className");
        digester.addSetProperties("Server/Listener");
        digester.addSetNext("Server/Listener", "addLifecycleListener", "org.apache.catalina.LifecycleListener");

        digester.addObjectCreate("Server/Service", "org.apache.catalina.core.StandardService", "className");
        digester.addSetProperties("Server/Service");
        digester.addSetNext("Server/Service", "addService", "org.apache.catalina.Service");

        digester.addObjectCreate("Server/Service/Listener", null, "className");
        digester.addSetProperties("Server/Service/Listener");
        digester.addSetNext("Server/Service/Listener", "addLifecycleListener", "org.apache.catalina.LifecycleListener");

        // Executor
        digester.addObjectCreate("Server/Service/Executor", "org.apache.catalina.core.StandardThreadExecutor", "className");
        digester.addSetProperties("Server/Service/Executor");

        digester.addSetNext("Server/Service/Executor", "addExecutor", "org.apache.catalina.Executor");

        digester.addRule("Server/Service/Connector", new ConnectorCreateRule());
        digester.addRule("Server/Service/Connector", new SetAllPropertiesRule(new String[] { "executor" }));
        digester.addSetNext("Server/Service/Connector", "addConnector", "org.apache.catalina.connector.Connector");

        digester.addObjectCreate("Server/Service/Connector/Listener", null, "className");
        digester.addSetProperties("Server/Service/Connector/Listener");
        digester.addSetNext("Server/Service/Connector/Listener", "addLifecycleListener", "org.apache.catalina.LifecycleListener");

        // Add RuleSets for nested elements
        digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));
        digester.addRuleSet(new EngineRuleSet("Server/Service/"));
        digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
        digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));
        addClusterRuleSet(digester, "Server/Service/Engine/Host/Cluster/");
        digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));

        // When the 'engine' is found, set the parentClassLoader.
        digester.addRule("Server/Service/Engine", new SetParentClassLoaderRule(parentClassLoader));
        addClusterRuleSet(digester, "Server/Service/Engine/Cluster/");

        long t2 = System.currentTimeMillis();
        if (log.isDebugEnabled())
        {
            log.debug("Digester for server.xml created " + (t2 - t1));
        }
        return (digester);

    }

 上面的太多咱們看幾個尤爲有用的地方。

        digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer", "className");
        digester.addSetProperties("Server");
        digester.addSetNext("Server", "setServer", "org.apache.catalina.Server");

這裏先解釋一下Digester是一個用於解析xml的小框架,內部經過封裝sax解析來實現的.介紹一下Digester的功能:

A.在遇到patter對應的標籤時建立對應的對象,將對象壓棧,而後標籤結束後再將這個對象出棧。

        digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer", "className");

經過上面的代碼配置在遇到<Server>標籤時建立StandardServer對象,並將該對象壓進Digester內部的stack中,遇到</Server>的時候將StandardServer對象出棧。

B.開啓對應標籤的attribute屬性填充到實例化的類中的功能。

        digester.addSetProperties("Server");

C.這是最關鍵的地方,在遇到對應的匹配規則後在end方法調用棧頂下面緊挨着的對象的指定的方法並將棧頂的對象做爲參數穿進去。

        digester.addSetNext("Server", "setServer", "org.apache.catalina.Server");

上面傳進來的方法名就是setServer也就是說會調用棧低的setServer方法將它上面的類做爲參數傳進去。

    public void end(String namespace, String name) throws Exception
    {
        // Identify the objects to be used
        Object child = digester.peek(0);
        Object parent = digester.peek(1);
        if (digester.log.isDebugEnabled())
        {
            if (parent == null)
            {
                digester.log.debug(
                        "[SetNextRule]{" + digester.match + "} Call [NULL PARENT]." + methodName + "(" + child + ")");
            } else
            {
                digester.log.debug("[SetNextRule]{" + digester.match + "} Call " + parent.getClass().getName() + "."
                        + methodName + "(" + child + ")");
            }
        }

        // Call the specified method
        IntrospectionUtils.callMethod1(parent, methodName, child, paramType, digester.getClassLoader());
    }

剛開始的時候我很迷惑,由於在整個程序中都沒有看到Catalina何時顯式的設置了Server類型的屬性,後來我才發現這個祕密仍是在Catalinaload()方法中。

            try
            {
                inputSource.setByteStream(inputStream);
                digester.push(this);
                digester.parse(inputSource);
            }

在真正開始Digesterparse()方法以前先將自身壓進了自身的棧中,而後咱們再看下面,這個是Tomcat的server.xml配置文件。

咱們結合Tomcatconf目錄下的server.xml文件,<Server>就是根元素.因此解析最後出棧的確定是<Server>標籤對應的StandardServer對象.我接着看這幾行代碼.

        digester.addSetNext("Server", "setServer", "org.apache.catalina.Server");

由於最開始將Catalina類自身以this傳了進來因此棧底是Catalina對象,<server>標籤結束的時候會調用CtalinasetServer()方法將StandardServer對象的實例做爲參數傳進來。到這裏咱們成功設置了server屬性,調用StandardServerstart()方法。 

3.StandardServer中的start方法繼承自LifecycleBase而後又調用本身的startInternal方法。

    public final synchronized void start() throws LifecycleException
    {

        if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state)
                || LifecycleState.STARTED.equals(state))
        {

            if (log.isDebugEnabled())
            {
                Exception e = new LifecycleException();
                log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
            } else if (log.isInfoEnabled())
            {
                log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
            }

            return;
        }

        if (state.equals(LifecycleState.NEW))
        {
            init();
        } else if (state.equals(LifecycleState.FAILED))
        {
            stop();
        } else if (!state.equals(LifecycleState.INITIALIZED) && !state.equals(LifecycleState.STOPPED))
        {
            invalidTransition(Lifecycle.BEFORE_START_EVENT);
        }

        try
        {
            setStateInternal(LifecycleState.STARTING_PREP, null, false);
            startInternal();
            if (state.equals(LifecycleState.FAILED))
            {
                // This is a 'controlled' failure. The component put itself into
                // the
                // FAILED state so call stop() to complete the clean-up.
                stop();
            } else if (!state.equals(LifecycleState.STARTING))
            {
                // Shouldn't be necessary but acts as a check that sub-classes
                // are
                // doing what they are supposed to.
                invalidTransition(Lifecycle.AFTER_START_EVENT);
            } else
            {
                setStateInternal(LifecycleState.STARTED, null, false);
            }
        } catch (Throwable t)
        {
            // This is an 'uncontrolled' failure so put the component into the
            // FAILED state and throw an exception.
            ExceptionUtils.handleThrowable(t);
            setStateInternal(LifecycleState.FAILED, null, false);
            throw new LifecycleException(sm.getString("lifecycleBase.startFail", toString()), t);
        }
    }

咱們看一看StandardServer的startInternal方法。

    protected void startInternal() throws LifecycleException
    {

        fireLifecycleEvent(CONFIGURE_START_EVENT, null);
        setState(LifecycleState.STARTING);

        globalNamingResources.start();

        // Start our defined Services
        synchronized (servicesLock)
        {
            for (int i = 0; i < services.length; i++) { services[i].start(); }
        }
    }

startInternal()中啓動了由Digesterserver.xml中解析出來的默認的Service組件,再看一下Catalina中的代碼。

        digester.addObjectCreate("Server/Service", "org.apache.catalina.core.StandardService", "className");
        digester.addSetProperties("Server/Service");
        digester.addSetNext("Server/Service", "addService", "org.apache.catalina.Service");

和Catalinna配置Server的原理同樣,遇到<Service>後會實例化StandardService實例而後在解析</Service>結束後會將StandardService的實例添加到StandardServer實例中。

4.StandardService中一次調用了StandardEngineStandardHoststart方法,可是咱們始終找不到StandardContext是何時被初始化,下面讓咱們看看程序是怎麼建立StandardContext實例的。

        digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));
        digester.addRuleSet(new EngineRuleSet("Server/Service/"));
        digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
        digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));
        addClusterRuleSet(digester, "Server/Service/Engine/Host/Cluster/");
        digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));

仍是回到Catalina初始化Digester實例時的代碼,咱們看對於<Host>標籤加的一些規則。

   public void addRuleInstances(Digester digester)
    {
        digester.addObjectCreate(prefix + "Host", "org.apache.catalina.core.StandardHost", "className");
        digester.addSetProperties(prefix + "Host");
        digester.addRule(prefix + "Host", new CopyParentClassLoaderRule());
     // 最爲核心的配置 digester.addRule(prefix
+ "Host", new LifecycleListenerRule("org.apache.catalina.startup.HostConfig", "hostConfigClass")); digester.addSetNext(prefix + "Host", "addChild", "org.apache.catalina.Container"); digester.addCallMethod(prefix + "Host/Alias", "addAlias", 0); // Cluster configuration start digester.addObjectCreate(prefix + "Host/Cluster", null, "className"); digester.addSetProperties(prefix + "Host/Cluster"); digester.addSetNext(prefix + "Host/Cluster", "setCluster", "org.apache.catalina.Cluster"); // Cluster configuration end digester.addObjectCreate(prefix + "Host/Listener", null, "className"); digester.addSetProperties(prefix + "Host/Listener"); digester.addSetNext(prefix + "Host/Listener", "addLifecycleListener", "org.apache.catalina.LifecycleListener"); digester.addRuleSet(new RealmRuleSet(prefix + "Host/")); digester.addObjectCreate(prefix + "Host/Valve", null, "className"); digester.addSetProperties(prefix + "Host/Valve"); digester.addSetNext(prefix + "Host/Valve", "addValve", "org.apache.catalina.Valve"); }

這裏的比較核心的就是HostConfig,它繼承了LifecycleListener,StandHost啓動的時候會調用監聽器的啓動事件的處理邏輯,當調用StandardHoststart方法實際上調用的是父類LifecycleBasestart方法。

這裏調用子類的startInternal方法,StandardHostContainerBase都實現了這個方法,根據多態規則首先會調用最底層子類的方法。

 

   protected synchronized void startInternal() throws LifecycleException
    {

        // Set error report valve
        String errorValve = getErrorReportValveClass();
        if ((errorValve != null) && (!errorValve.equals("")))
        {
            try
            {
                boolean found = false;
                Valve[] valves = getPipeline().getValves();
                for (Valve valve : valves)
                {
                    if (errorValve.equals(valve.getClass().getName()))
                    {
                        found = true;
                        break;
                    }
                }
                if (!found)
                {
                    Valve valve = (Valve) Class.forName(errorValve).newInstance();
                    getPipeline().addValve(valve);
                }
            } catch (Throwable t)
            {
                ExceptionUtils.handleThrowable(t);
                log.error(sm.getString("standardHost.invalidErrorReportValveClass", errorValve), t);
            }
        }
        super.startInternal();
    }

而後在這裏又會調用父類ContainerBase類的startInternal()方法,讓咱們看這個方法。

 

    protected synchronized void startInternal() throws LifecycleException
    {

        // Start our subordinate components, if any
        logger = null;
        getLogger();
        Cluster cluster = getClusterInternal();
        if ((cluster != null) && (cluster instanceof Lifecycle))
            ((Lifecycle) cluster).start();
        Realm realm = getRealmInternal();
        if ((realm != null) && (realm instanceof Lifecycle))
            ((Lifecycle) realm).start();

        // Start our child containers, if any
        Container children[] = findChildren();
        List<Future<Void>> results = new ArrayList<>();
        for (int i = 0; i < children.length; i++)
        {
            results.add(startStopExecutor.submit(new StartChild(children[i])));
        }

        boolean fail = false;
        for (Future<Void> result : results)
        {
            try
            {
                result.get();
            } catch (Exception e)
            {
                log.error(sm.getString("containerBase.threadedStartFailed"), e);
                fail = true;
            }

        }
        if (fail)
        {
            throw new LifecycleException(sm.getString("containerBase.threadedStartFailed"));
        }

        // Start the Valves in our pipeline (including the basic), if any
        if (pipeline instanceof Lifecycle)
            ((Lifecycle) pipeline).start();
     // 觸發生命週期時間,觸發監聽器setState(LifecycleState.STARTING);

        // Start our thread
        threadStart();

    }

 

 

 

 

 

 

   public enum LifecycleState 
   {
       NEW(false, null),
       INITIALIZING(false, Lifecycle.BEFORE_INIT_EVENT),
       INITIALIZED(false, Lifecycle.AFTER_INIT_EVENT),
       STARTING_PREP(false, Lifecycle.BEFORE_START_EVENT),
       STARTING(true, Lifecycle.START_EVENT),
       STARTED(true, Lifecycle.AFTER_START_EVENT),
       STOPPING_PREP(true, Lifecycle.BEFORE_STOP_EVENT),
       STOPPING(false, Lifecycle.STOP_EVENT),
       STOPPED(false, Lifecycle.AFTER_STOP_EVENT),
       DESTROYING(false, Lifecycle.BEFORE_DESTROY_EVENT),
       DESTROYED(false, Lifecycle.AFTER_DESTROY_EVENT),
       FAILED(false, null),
   }

 

這個方法會出發生命週期start的狀態。

 

    protected synchronized void setState(LifecycleState state) throws LifecycleException
    {
        setStateInternal(state, null, true);
    } 

setStateInternal的方法中獲取上面的枚舉類型的LifecycleStateString類型的成員變量,STARTING類型對應Lifecycle.START_EVENT類型的事件,而後看fireLifecycleEvent方法。

 

    private synchronized void setStateInternal(LifecycleState state, Object data, boolean check) throws LifecycleException
    {

        if (log.isDebugEnabled())
        {
            log.debug(sm.getString("lifecycleBase.setState", this, state));
        }

        if (check)
        {
            // Must have been triggered by one of the abstract methods (assume
            // code in this class is correct)
            // null is never a valid state
            if (state == null)
            {
                invalidTransition("null");
                // Unreachable code - here to stop eclipse complaining about
                // a possible NPE further down the method
                return;
            }

            // Any method can transition to failed
            // startInternal() permits STARTING_PREP to STARTING
            // stopInternal() permits STOPPING_PREP to STOPPING and FAILED to
            // STOPPING
            if (!(state == LifecycleState.FAILED
                    || (this.state == LifecycleState.STARTING_PREP && state == LifecycleState.STARTING)
                    || (this.state == LifecycleState.STOPPING_PREP && state == LifecycleState.STOPPING)
                    || (this.state == LifecycleState.FAILED && state == LifecycleState.STOPPING)))
            {
                // No other transition permitted
                invalidTransition(state.name());
            }
        }

        this.state = state;
        String lifecycleEvent = state.getLifecycleEvent();
        if (lifecycleEvent != null)
        {
            fireLifecycleEvent(lifecycleEvent, data);
        }
    }

這裏將實踐類型傳給了LifecycleSupport中維護的LifecycleListener類型的集合,並調用Catalinna從Server.xml中解析出來的LifecycleListener方法lifecycleEvent方法。

 

    protected void fireLifecycleEvent(String type, Object data)
    {
        lifecycle.fireLifecycleEvent(type, data);
    }

 

 

 

因此回到上面建立Digester的時候咱們定義了將匹配規則,在解析到<Host>的時候會開始實例化HostConfig類型的LifecycleListener並將其添加到StandardHost當中,而後在start()方法調用後會觸發HostConfig中的lifecycleEvent()方法。

 

   public void lifecycleEvent(LifecycleEvent event)
    {

        // Identify the host we are associated with
        try
        {
            host = (Host) event.getLifecycle();
            if (host instanceof StandardHost)
            {
                setCopyXML(((StandardHost) host).isCopyXML());
                setDeployXML(((StandardHost) host).isDeployXML());
                setUnpackWARs(((StandardHost) host).isUnpackWARs());
                setContextClass(((StandardHost) host).getContextClass());
            }
        } catch (ClassCastException e)
        {
            log.error(sm.getString("hostConfig.cce", event.getLifecycle()), e);
            return;
        }

        // Process the event that has occurred
        if (event.getType().equals(Lifecycle.PERIODIC_EVENT))
        {
            check();
        } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT))
        {
            beforeStart();
        } else if (event.getType().equals(Lifecycle.START_EVENT))
        {
            start();
        } else if (event.getType().equals(Lifecycle.STOP_EVENT))
        {
            stop();
        }
    }

調用start()方法.

 

    public void start()
    {

        if (log.isDebugEnabled())
            log.debug(sm.getString("hostConfig.start"));

        try
        {
            ObjectName hostON = host.getObjectName();
            oname = new ObjectName(hostON.getDomain() + ":type=Deployer,host=" + host.getName());
            Registry.getRegistry(null, null).registerComponent(this, oname, this.getClass().getName());
        } catch (Exception e)
        {
            log.error(sm.getString("hostConfig.jmx.register", oname), e);
        }

        if (!host.getAppBaseFile().isDirectory())
        {
            log.error(sm.getString("hostConfig.appBase", host.getName(), host.getAppBaseFile().getPath()));
            host.setDeployOnStartup(false);
            host.setAutoDeploy(false);
        }

        if (host.getDeployOnStartup())
            deployApps();

    } 

start()中調用了deployApps方法,其中appBase = "webapps"。

 

    protected void deployApps()
    {

        File appBase = host.getAppBaseFile();
        File configBase = host.getConfigBaseFile();
        String[] filteredAppPaths = filterAppPaths(appBase.list());
        // Deploy XML descriptors from configBase
        deployDescriptors(configBase, configBase.list());
        // Deploy WARs
        deployWARs(appBase, filteredAppPaths);
        // Deploy expanded folders
 deployDirectories(appBase, filteredAppPaths);

    }

讓咱們直接看Tomcat咱們部署的項目的載入方式deployDirectories(appBase, filteredAppPaths),項目的文件夾爲webapps。

 

   protected void deployDirectories(File appBase, String[] files)
    {

        if (files == null)
            return;

        ExecutorService es = host.getStartStopExecutor();
        List<Future<?>> results = new ArrayList<>();

        for (int i = 0; i < files.length; i++)
        {

            if (files[i].equalsIgnoreCase("META-INF"))
                continue;
            if (files[i].equalsIgnoreCase("WEB-INF"))
                continue;
            File dir = new File(appBase, files[i]);
            if (dir.isDirectory())
            {
                ContextName cn = new ContextName(files[i], false);

                if (isServiced(cn.getName()) || deploymentExists(cn.getName()))
                    continue;

                results.add(es.submit(new DeployDirectory(this, cn, dir)));
            }
        }

        for (Future<?> result : results)
        {
            try
            {
                result.get();
            } catch (Exception e)
            {
                log.error(sm.getString("hostConfig.deployDir.threaded.error"), e);
            }
        }
    }

Digester解析生成的線程池中提交一個DeployDirectory類的實例。

 

    private static class DeployDirectory implements Runnable
    {

        private HostConfig config;
        private ContextName cn;
        private File dir;

        public DeployDirectory(HostConfig config, ContextName cn, File dir)
        {
            this.config = config;
            this.cn = cn;
            this.dir = dir;
        }

        @Override
        public void run()
        {
            config.deployDirectory(cn, dir);
        }
    }  

在裏面又調用了HostConfig類的deployDirectory()方法.

  

  在deployDirectory()方法中前面對路徑進行了一系列的判斷

  

  讓咱們看看這個路徑.

  

  有時候咱們的的項目的相關目錄下並無這個文件.

  

  因此直接進入了else塊直接生成了StandardContext實例初始化參數後直接添加到了父容器StandardHost.

 5.StandardContext類的初始化

  讓咱們先看看在HostConfig中是怎麼配置StandardContext.

  

  首先先給他添加了一個生命週期事件監聽器.

  

  在StandardContext中咱們找到了這個監聽器,ContextConfig.想必到這裏你們若是看懂前面HostConfig的執行原理,這個也不難理解,在這裏我就不仔細的介紹了,Context啓動的時候確定會出發start類型的時間,而後調用ContextConfiglifecycleEvent()方法.讓咱們看看在這個ContextConfig中都作了什麼.

  

  

  調用configureStart()方法中調用webConfig()載入咱們WEB-INF文件夾下面的web.xml.

  

  這裏也是用Digester進行解析的,添加的匹配規則無非是對Servlet,Filter等咱們在開發中常常用到的配置標籤的解析並將生成的實體類填充在WebXml類型的實例中.還有一點值得提示的是在第一次獲取ServletContext的時候會對其進行初始化爲ApplicationContext.

  

  最後在configureContext方法中將填充完畢的WebXml實例中的相關數據填充到Context.

  

 

  下面的代碼接着上面的.

  

  這裏看Servlet填充的過程,Wrapper包裝Servlet再將這些Wrapper填充到Context.

  讓咱們接着看在StandardContextstartInternal()咱們比較感興趣的地方.

  

  這裏會將以字符串數組保存的listener數組進行初始化.

  

  對實例化的listener進行分類

  

  

  對初始化的監聽器分類咱們這裏關心ServletContextListener類型的監聽器,判斷若是符合該類型的監聽器後便會加入方法中臨時建立的名爲lifecycleListenersArrayList.

  

  設置剛生成的lifecycleListenders.

   

  生成事件,調用ServletContextListener類型實例的contextInitialized()方法,這就是TomcatSpringMVC勾搭開始的地方.具體的內容咱們在下一篇講,睡了.

相關文章
相關標籤/搜索