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