tomcat中各个组件的生命周期是由server控制的。那么server的生命周期由谁控制呢?
 
我们先来看下使用脚本启动tomcat的时候,首先会发生什么。
java应用要运行,需要一个main方法。tomcat启动的时候调用的是bootstrap中的main方法。
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
47 
48 
49 
50 
51 
52 
53 
54 
55 
56 
57 
58 
59 
60 
 | 
/** * Main method, used for testing only. * * @param args Command line arguments to be processed */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;    }    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);    }} | 
这个main方法首先会new一个Bootstrap对象,并且把这个对象放到一个static域中。
 然后会对这个对象进行初始化,初始化方法如下:
 
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
 | 
/** * Initialize daemon. */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;} | 
在这个init方法中,会new一个catalina对象。bootstrap的大多数操作,诸如start、stop、load等,实际上都是调用的这个catalina对象的相应方法。
 在bootstrap的初始化完成之后,会根据用户输入的main函数的参数(start、stop等),判断执行什么操作。
 如果是start参数,就表明是启动tomcat。会先后执行daemon.setAwait、daemon.load、daemon.start这三个
方法。他们最终会分别采用反射的方式去调用之前已经初始化的catalina的对应方法setAwait、load、start。也就是说其实
bootstrap就调用了catalina的方法,本身是没做什么额外动作的。
 stop则表明是关闭tomcat。
| 
 1 
2 
3 
4 
 | 
/** * Daemon object used by main. */private static Bootstrap daemon = null; | 
这里需要注意一点,我们使用脚本启动和关闭tomcat的时候,实际上最终都是执行bootstrap的main方法,正因为daemon是static
的,所以,我们start和stop的时候,实际上操作的是同一个bootstrap对象,才能对同一个tomcat的启动和关闭。 
如上面所说,在启动过程中,daemon.load会调用catalina的load方法。catalina的load方法如下:
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
47 
48 
49 
50 
51 
52 
53 
54 
55 
56 
57 
58 
59 
60 
61 
62 
63 
64 
65 
66 
67 
68 
69 
70 
71 
72 
73 
74 
75 
76 
77 
78 
79 
80 
81 
82 
83 
84 
85 
86 
87 
88 
89 
90 
91 
92 
93 
94 
95 
96 
97 
98 
99 
100 
101 
102 
103 
104 
105 
106 
107 
108 
109 
110 
111 
112 
113 
114 
115 
116 
117 
 | 
/** * Start a new server instance. */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 (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 {        try {            inputStream.close();        } catch (IOException e) {            // Ignore        }    }    getServer().setCatalina(this);    // 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");    }} | 
这个方法实际上是在加载、解析server.xml,生成tomcat的组件:server、service、connector、engine、host、context等。最后调用server的init方法。
 我们之前提到过tomcat中server用来管理整个tomcat的生命周期
daemon.load方法执行之后,会执行daemon.start,这个方法调用catalina的start方法,catalina调用server的start方法,从而启动tomcat的各个组件。
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
47 
48 
49 
50 
 | 
/** * Start a new server instance. */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.error("Catalina.start: ", e);    }    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();    }} | 
 server start之后,catalina会创建一个 CatalinaShutdownHook
对象,然后将它添加到运行时的shutdownHook中。即注册一个虚拟机的shutdown
hook,这样在我们不通过脚本关闭tomcat,而是直接杀死进程的时候,也能够执行catalina的stop方法,完成tomcat的关闭过程。
 
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
 | 
/** * Shutdown hook which will perform a clean shutdown of Catalina if needed. */protected class CatalinaShutdownHook extends Thread {    @Override    public void run() {        try {            if (getServer() != null) {                Catalina.this.stop();            }        } catch (Throwable ex) {            ExceptionUtils.handleThrowable(ex);            log.error(sm.getString("catalina.shutdownHookFail"), ex);        } finally {            // If JULI is used, shut JULI down *after* the server shuts down            // so log messages aren't lost            LogManager logManager = LogManager.getLogManager();            if (logManager instanceof ClassLoaderLogManager) {                ((ClassLoaderLogManager) logManager).shutdown();            }        }    }} | 
The Java virtual machine shuts down in response to two kinds of events:
1、程序正常退出,包括最后一个non-daemon线程退出或者Runtime.exit、System.exit方法被调用。
 2、java虚拟机被终止,包括被用户中断,如:^C,或者系统级的事件:用户登出、系统关闭等。
 发生上面的情况的时候,虚拟机就会调用所有的shutdown hook。 
 综上,tomcat启动过程实际就是bootstrap调用catalina的start方法,然后catalina调用server的start方法。
关闭的时候,可以是bootstrap的stop方法调用catalina的stop方法,也可以是Hook调用catalina的stop方法,然后
catalina的stop方法调用server的stop方法。
转载请注明:学时网 » tomcat 中的 bootstrap 与 catalina



