探秘Tomcat——启动篇

tomcat作为一款web服务器本身很复杂,代码量也很大,但是模块化很强,最核心的模块还是连接器Connector和容器Container。具体请看下图:

从图中可以看出

a. 高亮的两块是Connector和Container,为什么说他们两最核心,其中Connector是负责接收http请求,当你在浏览器中输入URL为http://www.demon.com,这样的http请求就会被Connector接收并转发给容器Container。

到了Container后,会经过不同的容器层有:

    Engine:表示整个Catalina servlet引擎;

    Host: 表示包含有一个或多个Context容器的虚拟主机;

    Context: 表示一个Web应用程序,一个Context可以有多个Wrapper;

    Wrapper: 表示一个独立的servlet

一层层的处理后最终到达的是Wrapper即特定的serlvet处理器,具体用于处理http的请求响应。

b. 图中除了Connector和Container以外还有一些Naming、Session等一些服务,统统封装在了一个service里面,可以用于对外提供各种服务,比如我们不仅希望 tomcat可以提供一个web请求响应的服务,还希望知道其中的详细处理细节,我们可以使用service中的日志服务,用于打印一些请求处理过程中的细节信息。

c. 有了这些service模块,我们还需要有一个落脚点,这个落脚点用于控制service的生杀大全,负责service的整个生命周期,包括service的初始化、启动、结束等等。

d. 综上举一个例子,现在有A软件公司,共有三个部门——研发部门、财务部门、技术支持部门

其中每个部门相当于一个service,在每个service中可以提供不同的服务,比如研发部门可以提供功能开发服务、功能测试服务、持续集成部署服务、美工服务等。

而封装在server中的各个服务由server来统一管理,好比A公司可以实现研发部门的组建(或重组)——开工——裁撤等一系列的生命周期功能,如果A公司逢上国家法定节假日需要放假休息,那么相关的service部门也都会执行放假模式,同理,如果A公司正常运营上班,那么各个service也都会切换到上班模式。有了server的存在,从而方便对于service的管理。

大致了解了tomcat的架构和工作原理,我们来看看平时我们通过点击startup.bat来启动tomcat是如何从代码层面实现的,在启动过程中又做了哪些事情(基于tomcat6版本的源码)。

1.启动入口

  在代码中,tomcat的启动是通过运行org.apache.catalina.startup.Bootstrap类的main方法来启动服务的

public static void main(String args[]) {

        if (daemon == null) {
            daemon = new Bootstrap();
            try {
                daemon.init();
            } catch (Throwable t) {
                t.printStackTrace();
                return;
            }
        }

        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 {
                log.warn("Bootstrap: command \"" + command + "\" does not exist.");
            }
        } catch (Throwable t) {
            t.printStackTrace();
        }

    }

a. 初始化Bootstrap对象

b. 根据具体的需求完成服务的加载、启动和关闭的功能

备注:这里运行或调试main方法的时候需要在VM arguments中填入类似-Dcatalina.home="C:\Users\Administrator\Desktop\tomcat\apache-tomcat-6.0.43-src\output\build"这样的参数,具体操作参见《探秘Tomcat(一)——Myeclipse中导入Tomcat源码

2.Bootstrap的初始化

2.1 init方法

public void init()
        throws Exception
    {

        // Set Catalina path
        setCatalinaHome();
        setCatalinaBase();

        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;

    }

a. 其中setCatalinaHome和setCatalinaBase分别用于为catalina.name和catalina.base赋值;

b. initClassLoaders用于初始类加载器,分别初始化了三个加载器CommonLoader、CatalinaLoader和SharedLoader,从代码可以发现CommonLoader是CatalinaLoader和SharedLoader的父类,最终初始化完成,两个子类都指向CommonLoader;

c. 通过反射机制生成一份org.apache.catalina.startup.Catalina的实例用于启动。

2.2 createClassLoader方法

  下面我们来看看init->initClassLoaders->createClassLoader这个方法,通过这个方法我们分别得到了三个加载器CommonLoader、CatalinaLoader和SharedLoader,我们加载所需要的目录和jar包等。

private ClassLoader createClassLoader(String name, ClassLoader parent)
        throws Exception {

        String value = CatalinaProperties.getProperty(name + ".loader");//得到的value为${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar
        if ((value == null) || (value.equals("")))
            return parent;

        ArrayList repositoryLocations = new ArrayList();
        ArrayList repositoryTypes = new ArrayList();
        int i;
 
        StringTokenizer tokenizer = new StringTokenizer(value, ",");//用于分隔String的类
        while (tokenizer.hasMoreElements()) {//遍历value中的值
            String repository = tokenizer.nextToken();

            // Local repository
            boolean replace = false;
            String before = repository;
            while ((i=repository.indexOf(CATALINA_HOME_TOKEN))>=0) {//这样的遍历说明如果value的每项里面包含了${catalina.home}或者${catalina.base}就将标记变量replace置为true,并且确确实实的替换这些变量,比如这里讲${catalina.base}替换为C:\Users\Administrator\Desktop\tomcat\apache-tomcat-6.0.43-src\output\build。每次遍历完value值后,都会根据存储的repository的类型添加不同的值到repositoryTypes中去。
                replace=true;
                if (i>0) {
                repository = repository.substring(0,i) + getCatalinaHome() 
                    + repository.substring(i+CATALINA_HOME_TOKEN.length());
                } else {
                    repository = getCatalinaHome() 
                        + repository.substring(CATALINA_HOME_TOKEN.length());
                }
            }
            while ((i=repository.indexOf(CATALINA_BASE_TOKEN))>=0) {
                replace=true;
                if (i>0) {
                repository = repository.substring(0,i) + getCatalinaBase() 
                    + repository.substring(i+CATALINA_BASE_TOKEN.length());
                } else {
                    repository = getCatalinaBase() 
                        + repository.substring(CATALINA_BASE_TOKEN.length());
                }
            }
            if (replace && log.isDebugEnabled())
                log.debug("Expanded " + before + " to " + repository);

            // Check for a JAR URL repository
            try {
                URL url=new URL(repository);
                repositoryLocations.add(repository);
                repositoryTypes.add(ClassLoaderFactory.IS_URL);
                continue;
            } catch (MalformedURLException e) {
                // Ignore
            }

            if (repository.endsWith("*.jar")) {
                repository = repository.substring
                    (0, repository.length() - "*.jar".length());
                repositoryLocations.add(repository);
                repositoryTypes.add(ClassLoaderFactory.IS_GLOB);
            } else if (repository.endsWith(".jar")) {
                repositoryLocations.add(repository);
                repositoryTypes.add(ClassLoaderFactory.IS_JAR);
            } else {
                repositoryLocations.add(repository);
                repositoryTypes.add(ClassLoaderFactory.IS_DIR);
            }
        }

        String[] locations = (String[]) repositoryLocations.toArray(new String[0]);
        Integer[] types = (Integer[]) repositoryTypes.toArray(new Integer[0]);//分别将两个list集合转化为数组
 
        ClassLoader classLoader = ClassLoaderFactory.createClassLoader
            (locations, types, parent);//调用createClassLoader方法,其中细加载到每个目录和具体的目录下面的文件,注意其中的加载的数据集使用LinkedHashSet,这是利用了set集合的特性,不允许重复元素的出现,因为这里面会有重复加载的情况,所以用set保证了唯一性。通过这个操作等于加载了所有的文件,具体类加载以及URLClassLoader的使用可以参见http://blog.csdn.net/mycomputerxiaomei/article/details/24470465 

        // Retrieving MBean server     
        MBeanServer mBeanServer = null;//得到一个MBean的对象
        if (MBeanServerFactory.findMBeanServer(null).size() > 0) {
            mBeanServer =
                (MBeanServer) MBeanServerFactory.findMBeanServer(null).get(0);
        } else {
            mBeanServer = ManagementFactory.getPlatformMBeanServer();
        }

        // Register the server classloader
        ObjectName objectName =
            new ObjectName("Catalina:type=ServerClassLoader,name=" + name);
        mBeanServer.registerMBean(classLoader, objectName);//将类加载器注册到mbean服务上,这样做的作用是,一旦classLoader有变化了,就会有notification。

        return classLoader;

    }

这里有个小细节在createClassLoader方法中有调用方法getCatalinaBase,而getCatalinaBase又会去调用getCatalinaHome,而不是将getCatalinaHome中的具体功能代码又冗余的写一遍,这是一个很好的习惯^_^

至此,我们完成了Bootstrap对象的初始化,为catalina.home等,初始化了三个类加载器。

3.server的加载和启动

3.1 deamon.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 {
            file = configFile();
            inputStream = new FileInputStream(file);
            inputSource = new InputSource("file://" + file.getAbsolutePath());
        } catch (Exception e) {
            ;
        }
        if (inputStream == null) {
            try {
                inputStream = getClass().getClassLoader()
                    .getResourceAsStream(getConfigFile());
                inputSource = new InputSource
                    (getClass().getClassLoader()
                     .getResource(getConfigFile()).toString());
            } catch (Exception 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 ((inputStream == null) && (file != null)) {
            log.warn("Can't load server.xml from " + 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);
            inputStream.close();
        } catch (Exception e) {
            log.warn("Catalina.start using "
                               + getConfigFile() + ": " , e);
            return;
        }

        // Stream redirection
        initStreams();

        // Start the new server
        if (getServer() instanceof Lifecycle) {
            try {
                getServer().initialize();
            } 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");

    }

a. 前面的完成一些初始化的工作;

b. createStartDigester方法很重要,用于声明要实例化的所有服务,当然这个工作需要与file = configFile();中声明的位于conf下的server.xml进行配合;

c. 通过digester.parse(inputSource)解析server.xml中的元素

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<Class, List<String>>();
        ArrayList<String> attrs = new ArrayList<String>();
        attrs.add("className");
        fakeAttributes.put(Object.class, attrs);
        digester.setFakeAttributes(fakeAttributes);
        digester.setClassLoader(StandardServer.class.getClassLoader());

        // 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.NamingResources");
        digester.addSetProperties("Server/GlobalNamingResources");
        digester.addSetNext("Server/GlobalNamingResources",
                            "setGlobalNamingResources",
                            "org.apache.catalina.deploy.NamingResources");

        digester.addObjectCreate("Server/Listener",
                                 null, // MUST be specified in the element
                                 "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, // MUST be specified in the element
                                 "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, // MUST be specified in the element
                                 "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/"));
        digester.addRuleSet(ClusterRuleSetFactory.getClusterRuleSet("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));
        digester.addRuleSet(ClusterRuleSetFactory.getClusterRuleSet("Server/Service/Engine/Cluster/"));

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

    }

从这里我们看到了很多熟悉的字眼比如StandardServer、Engine、Host、Context等,可以对比看下server.xml中的标签

  1 <?xml version='1.0' encoding='utf-8'?>
  2 <!--
  3   Licensed to the Apache Software Foundation (ASF) under one or more
  4   contributor license agreements.  See the NOTICE file distributed with
  5   this work for additional information regarding copyright ownership.
  6   The ASF licenses this file to You under the Apache License, Version 2.0
  7   (the "License"); you may not use this file except in compliance with
  8   the License.  You may obtain a copy of the License at
  9 
 10       http://www.apache.org/licenses/LICENSE-2.0
 11 
 12   Unless required by applicable law or agreed to in writing, software
 13   distributed under the License is distributed on an "AS IS" BASIS,
 14   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 15   See the License for the specific language governing permissions and
 16   limitations under the License.
 17 -->
 18 <!-- Note:  A "Server" is not itself a "Container", so you may not
 19      define subcomponents such as "Valves" at this level.
 20      Documentation at /docs/config/server.html
 21  -->
 22 <Server port="8005" shutdown="SHUTDOWN">
 23 
 24   <!--APR library loader. Documentation at /docs/apr.html -->
 25   <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
 26   <!--Initialize Jasper prior to webapps are loaded. Documentation at /docs/jasper-howto.html -->
 27   <Listener className="org.apache.catalina.core.JasperListener" />
 28   <!-- Prevent memory leaks due to use of particular java/javax APIs-->
 29   <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
 30   <!-- JMX Support for the Tomcat server. Documentation at /docs/non-existent.html -->
 31   <Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" />
 32   <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
 33 
 34   <!-- Global JNDI resources
 35        Documentation at /docs/jndi-resources-howto.html
 36   -->
 37   <GlobalNamingResources>
 38     <!-- Editable user database that can also be used by
 39          UserDatabaseRealm to authenticate users
 40     -->
 41     <Resource name="UserDatabase" auth="Container"
 42               type="org.apache.catalina.UserDatabase"
 43               description="User database that can be updated and saved"
 44               factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
 45               pathname="conf/tomcat-users.xml" />
 46   </GlobalNamingResources>
 47 
 48   <!-- A "Service" is a collection of one or more "Connectors" that share
 49        a single "Container" Note:  A "Service" is not itself a "Container", 
 50        so you may not define subcomponents such as "Valves" at this level.
 51        Documentation at /docs/config/service.html
 52    -->
 53   <Service name="Catalina">
 54   
 55     <!--The connectors can use a shared executor, you can define one or more named thread pools-->
 56     <!--
 57     <Executor name="tomcatThreadPool" namePrefix="catalina-exec-" 
 58         maxThreads="150" minSpareThreads="4"/>
 59     -->
 60     
 61     
 62     <!-- A "Connector" represents an endpoint by which requests are received
 63          and responses are returned. Documentation at :
 64          Java HTTP Connector: /docs/config/http.html (blocking & non-blocking)
 65          Java AJP  Connector: /docs/config/ajp.html
 66          APR (HTTP/AJP) Connector: /docs/apr.html
 67          Define a non-SSL HTTP/1.1 Connector on port 8080
 68     -->
 69     <Connector port="8080" protocol="HTTP/1.1" 
 70                connectionTimeout="20000" 
 71                redirectPort="8443" />
 72     <!-- A "Connector" using the shared thread pool-->
 73     <!--
 74     <Connector executor="tomcatThreadPool"
 75                port="8080" protocol="HTTP/1.1" 
 76                connectionTimeout="20000" 
 77                redirectPort="8443" />
 78     -->           
 79     <!-- Define a SSL HTTP/1.1 Connector on port 8443
 80          This connector uses the JSSE configuration, when using APR, the 
 81          connector should be using the OpenSSL style configuration
 82          described in the APR documentation -->
 83     <!--
 84     <Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
 85                maxThreads="150" scheme="https" secure="true"
 86                clientAuth="false" sslProtocol="TLS" />
 87     -->
 88 
 89     <!-- Define an AJP 1.3 Connector on port 8009 -->
 90     <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
 91 
 92 
 93     <!-- An Engine represents the entry point (within Catalina) that processes
 94          every request.  The Engine implementation for Tomcat stand alone
 95          analyzes the HTTP headers included with the request, and passes them
 96          on to the appropriate Host (virtual host).
 97          Documentation at /docs/config/engine.html -->
 98 
 99     <!-- You should set jvmRoute to support load-balancing via AJP ie :
100     <Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">         
101     --> 
102     <Engine name="Catalina" defaultHost="localhost">
103 
104       <!--For clustering, please take a look at documentation at:
105           /docs/cluster-howto.html  (simple how to)
106           /docs/config/cluster.html (reference documentation) -->
107       <!--
108       <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
109       -->        
110 
111       <!-- The request dumper valve dumps useful debugging information about
112            the request and response data received and sent by Tomcat.
113            Documentation at: /docs/config/valve.html -->
114       <!--
115       <Valve className="org.apache.catalina.valves.RequestDumperValve"/>
116       -->
117 
118       <!-- This Realm uses the UserDatabase configured in the global JNDI
119            resources under the key "UserDatabase".  Any edits
120            that are performed against this UserDatabase are immediately
121            available for use by the Realm.  -->
122       <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
123              resourceName="UserDatabase"/>
124 
125       <!-- Define the default virtual host
126            Note: XML Schema validation will not work with Xerces 2.2.
127        -->
128       <Host name="localhost"  appBase="webapps"
129             unpackWARs="true" autoDeploy="true"
130             xmlValidation="false" xmlNamespaceAware="false">
131 
132         <!-- SingleSignOn valve, share authentication between web applications
133              Documentation at: /docs/config/valve.html -->
134         <!--
135         <Valve className="org.apache.catalina.authenticator.SingleSignOn" />
136         -->
137 
138         <!-- Access log processes all example.
139              Documentation at: /docs/config/valve.html -->
140         <!--
141         <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"  
142                prefix="localhost_access_log." suffix=".txt" pattern="common" resolveHosts="false"/>
143         -->
144 
145       </Host>
146     </Engine>
147   </Service>
148 </Server>

  Digester就是根据web.xml中声明的标签生成对象,并把元素的属性赋值为对象的属性,并关联起他们之间的父子关系。

3.2 start方法

  既然已经加载好了server以及所需要的service,那么就可以开始启动了。

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

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

    }

通过反射机制,可是调用Catalina类下面的start方法。

  下面我们看看Catalina中的start方法具体都做了什么

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
        if (getServer() instanceof Lifecycle) {
            try {
                ((Lifecycle) getServer()).start();
            } catch (LifecycleException e) {
                log.error("Catalina.start: ", e);
            }
        }

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

        try {
            // 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);
                }
            }
        } catch (Throwable t) {
            // This will fail on JDK 1.2. Ignoring, as Tomcat can run
            // fine without the shutdown hook.
        }

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

    }

a. 首先判断这个server是否为空,如果为空则需要先加载load

b. 判断getServer是否是Lifecycle的实例,显然是,我们可以从StandardServer类的声明中

“public final class StandardServer implements Lifecycle, Server, MBeanRegistration”可以看出,StandardServer实现了LifeCycle的接口,Server接口。

而且当我们进入Server类的时候,可以看到类的注释上写了:一般而且如果某类实现了Server接口,同时也要实现LifeCycle接口,这也正好验证了这里StandardServer的声明;

c. 如果满足是LifeCycle的实例的条件,则执行StandardServer中的start方法,该方法主要用于启动所有前面解析出来的service,包括进入类Connector启动Connector服务,进入Container中依次通过Engine、Host、Context和Wrapper启动相应的服务。

至此,就完成了

  • Bootstrap的初始化
  • 加载server服务
  • 启动server服务

最终实现了启动tomcat的目的,其实现在回头来看,启动一个服务器无非就是启动了一个server^^

如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!如果您想持续关注我的文章,请扫描二维码,关注JackieZheng的微信公众号,我会将我的文章推送给您,并和您一起分享我日常阅读过的优质文章。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏刘望舒

Android资源动态加载以及相关原理分析

思考 一般情况下,我们在设计一个插件化框架的时候,要解决的无非是下面几个问题: 四大组件的动态注册 组件相关的类的加载 资源的动态加载 实际上从目前的主流插件...

3368
来自专栏hotqin888的专栏

engineercms利用pdf.js制作连续看图功能

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hotqin888/article/det...

1811
来自专栏JackieZheng

探秘Tomcat——连接篇

  前两篇我们分别粗线条和细粒度的讲解了tomcat的服务是如何启动以及连接器Connector和容器Container又分别是如何被启动的.    本篇我们主...

3129
来自专栏Golang语言社区

用Go实现一门解释型语言

A interpreter language implementation in Go

912
来自专栏移动端开发

Android学习--跨程序共享数据之内容提供其探究

      跨程序共享数据之内容提供器,这是个什么功能?看到这个名称的时候最能给我们提供信息的应该是“跨程序”这个词了,是的重点就是这个词,这个内容提供器的作用...

1293
来自专栏菩提树下的杨过

rpc框架之 thrift连接池实现

接前一篇rpc框架之HA/负载均衡构架设计 继续,写了一个简单的thrift 连接池: 先做点准备工作: package yjmyzz; public cla...

6438
来自专栏MasiMaro 的技术博文

hook键盘驱动中的分发函数实现键盘输入数据的拦截

我自己在看《寒江独钓》这本书的时候,书中除了给出了利用过滤的方式来拦截键盘数据之外,也提到了另外一种方法,就是hook键盘分发函数,将它替换成我们自己的,然后再...

1032
来自专栏haifeiWu与他朋友们的专栏

golang重构博客统计服务

作为一个后端开发,在docker,etcd,k8s等新技术不断涌现的今天,其背后的功臣golang在语言排行榜上持续走高,因此楼主也就开了这次使用golang自...

1352
来自专栏chenssy

【死磕 Spring】----- IOC 之 IOC 初始化总结

前面 13 篇博文从源码层次分析了 IOC 整个初始化过程,这篇就这些内容做一个总结将其连贯起来。

1044
来自专栏Java开发者杂谈

XSS事件(一)

​ 最近做的一个项目因为安全审计需要,需要做安全改造。其中自然就包括XSS和CSRF漏洞安全整改。关于这两个网络安全漏洞的详细说明,可以参照我本篇博客最后的参考...

1914

扫码关注云+社区

领取腾讯云代金券