首页 > 代码库 > Tomcat启动分析(一)
Tomcat启动分析(一)
当我们在Linux下启动tomcat的时候,通过ps查看其进程信息为,接下来的内容我们就以此进行分析:
[tomcat@fdd ~]$ ps -ef |grep java tomcat 1521 1 18 23:20 tty1 00:00:09 /usr/bin/java -Djava.util.logging.config.file=/home/tomcat/apache-tomcat-7.0.69/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djdk.tls.ephemeralDHKeySize=2048 -Djava.endorsed.dirs=/home/tomcat/apache-tomcat-7.0.69/endorsed -classpath /home/tomcat/apache-tomcat-7.0.69/bin/bootstrap.jar:/home/tomcat/apache-tomcat-7.0.69/bin/tomcat-juli.jar -Dcatalina.base=/home/tomcat/apache-tomcat-7.0.69 -Dcatalina.home=/home/tomcat/apache-tomcat-7.0.69 -Djava.io.tmpdir=/home/tomcat/apache-tomcat-7.0.69/temp org.apache.catalina.startup.Bootstrap start
1 startup.sh分析
我们启动Tomcat的时候,执行的是sh startup.sh,那么在startup.sh中做了哪些事情呢,实际上就是调用catalina.sh,具体过程请看下面分析
…… # 指定Tomcat路径 PRGDIR=`dirname "$PRG"` # 指定调用脚本 EXECUTABLE=catalina.sh …… # 执行启动脚本 exec "$PRGDIR"/"$EXECUTABLE" start "$@"
2. catalina.sh分析
在catalina.sh中完成环境检查、环境初始化、参数初始化、启动操作,也就是在这个里面利用JVM中的java命令执行tomcat的main方法的:
…… # 环境检查 case $CATALINA_HOME in *:*) echo "Using CATALINA_HOME: $CATALINA_HOME"; echo "Unable to start as CATALINA_HOME contains a colon (:) character"; exit 1; esac case $CATALINA_BASE in *:*) echo "Using CATALINA_BASE: $CATALINA_BASE"; echo "Unable to start as CATALINA_BASE contains a colon (:) character"; exit 1; esac …… # 初始化环境信息、参数初始化 if [ -r "$CATALINA_BASE/bin/setenv.sh" ]; then . "$CATALINA_BASE/bin/setenv.sh" elif [ -r "$CATALINA_HOME/bin/setenv.sh" ]; then . "$CATALINA_HOME/bin/setenv.sh" fi …… # 启动Tomcat,支持 if [ "$1" = "-security" ] ; then if [ $have_tty -eq 1 ]; then echo "Using Security Manager" fi shift eval "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGING_MANAGER $JAVA_OPTS $CATALINA_OPTS -Djava.endorsed.dirs="\"$JAVA_ENDORSED_DIRS\"" -classpath "\"$CLASSPATH\"" -Djava.security.manager -Djava.security.policy=="\"$CATALINA_BASE/conf/catalina.policy\"" -Dcatalina.base="\"$CATALINA_BASE\"" -Dcatalina.home="\"$CATALINA_HOME\"" -Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" org.apache.catalina.startup.Bootstrap "$@" start >> "$CATALINA_OUT" 2>&1 "&" else eval "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGING_MANAGER $JAVA_OPTS $CATALINA_OPTS -Djava.endorsed.dirs="\"$JAVA_ENDORSED_DIRS\"" -classpath "\"$CLASSPATH\"" -Dcatalina.base="\"$CATALINA_BASE\"" -Dcatalina.home="\"$CATALINA_HOME\"" -Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" org.apache.catalina.startup.Bootstrap "$@" start >> "$CATALINA_OUT" 2>&1 "&" fi ……
3 Bootstrap分析
通过第二章节,我们可以看出实际上调用的是 java org.apache.catalina.startup.Bootstrap这个类,那么我们就来分析一下这个类
3.1 静态代码
类中的静态代码块完成一些参数初始化:
static { // Will always be non-null String userDir = System.getProperty("user.dir"); // Home first String home = System.getProperty(Globals.CATALINA_HOME_PROP); File homeFile = null; if (home != null) { File f = new File(home); try { homeFile = f.getCanonicalFile(); } catch (IOException ioe) { homeFile = f.getAbsoluteFile(); } } if (homeFile == null) { // First fall-back. See if current directory is a bin directory // in a normal Tomcat install File bootstrapJar = new File(userDir, "bootstrap.jar"); if (bootstrapJar.exists()) { File f = new File(userDir, ".."); try { homeFile = f.getCanonicalFile(); } catch (IOException ioe) { homeFile = f.getAbsoluteFile(); } } } if (homeFile == null) { // Second fall-back. Use current directory File f = new File(userDir); try { homeFile = f.getCanonicalFile(); } catch (IOException ioe) { homeFile = f.getAbsoluteFile(); } } catalinaHomeFile = homeFile; System.setProperty( Globals.CATALINA_HOME_PROP, catalinaHomeFile.getPath()); // Then base String base = System.getProperty(Globals.CATALINA_BASE_PROP); if (base == null) { catalinaBaseFile = catalinaHomeFile; } else { File baseFile = new File(base); try { baseFile = baseFile.getCanonicalFile(); } catch (IOException ioe) { baseFile = baseFile.getAbsoluteFile(); } catalinaBaseFile = baseFile; } System.setProperty( Globals.CATALINA_BASE_PROP, catalinaBaseFile.getPath()); }
3.2 调用main方法
main方法是启动一个java程序的入口,任何程序都会有自己的main方法,tomcat是一个WEB容器,也不例外:
public static void main(String args[]) { //如果守护进程为空,则进行初始化 if (daemon == null) { // Don‘t set daemon until init() has completed Bootstrap bootstrap = new Bootstrap(); try { //初始化守护进程,初始化内容参考3.2.1 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"; //加载启动参数,参考3.2.2 daemon.load(args); //启动Tomcat,参考3.2.3 daemon.start(); } else if (command.equals("stopd")) { args[args.length - 1] = "stop"; //停止Tomcat守护进程,参考3.2.4 daemon.stop(); } else if (command.equals("start")) { daemon.setAwait(true); daemon.load(args); daemon.start(); } else if (command.equals("stop")) { //单独停止Tomcat服务器,参考3.2.5 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); } }
3.2.1 初始化守护进程
此部分完成类加载器初始化
public void init() throws Exception { //初始化类加载器,根据catalina.properties中的加载项进行类加载 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; }
3.3.2 加载启动参数
在启动脚本中,增加了各种启动参数,此部分就完成这些参数的利用。
//实际调用org.apache.catalina.startup.Catalina类中的load方法 private void load(String[] arguments) throws Exception { // Call the load() method String methodName = "load"; Object param[]; Class<?> paramTypes[]; if (arguments==null || arguments.length==0) { paramTypes = null; param = null; } else { paramTypes = new Class[1]; paramTypes[0] = arguments.getClass(); param = new Object[1]; param[0] = arguments; } Method method = catalinaDaemon.getClass().getMethod(methodName, paramTypes); if (log.isDebugEnabled()) log.debug("Calling startup class " + method); //反射调用 method.invoke(catalinaDaemon, param); }
接下来,我们看具体的load
//org.apache.catalina.startup.Catalina.load(String[] args) public void load(String args[]) { try { //参数检查 if (arguments(args)) { //加载参数 load(); } } catch (Exception e) { e.printStackTrace(System.out); } } //org.apache.catalina.startup.Catalina.load() public void load() { long t1 = System.nanoTime(); //检查临时目录 initDirs(); // Before digester - it may be needed initNaming(); // Create and execute our Digester // ☆☆☆createeStartDigester待详细了解,初步判定完成对象初始化 Digester digester = createStartDigester(); InputSource inputSource = null; InputStream inputStream = null; File file = null; try { try { //获取server.xml配置文件 file = configFile(); //通过new File()方式读取文件 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) { //读取失败,则通过getResource方式读取文件 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) { //如果没有server.xml配置文件,则采用jar包中自带的server-embed.xml 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 { //解析XML配置 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 } } } //设置catalina属性 getServer().setCatalina(this); getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile()); getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile()); // Stream redirection // 设置日志输出流 initStreams(); // Start the new server try { //实际调用的是: org.apache.catalina.util.LifecycleBase.init() //启动Tomcat 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"); } }
3.2.3 启动Server
完成参数服务初始化之后,就可以进行Server启动。主要包含两部分:Service启动和Server启动。启动过程具体处理在org.apache.catalina.util.LifecycleBase.start()中完成,然后调用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) { for (int i = 0; i < services.length; i++) { //启动Service services[i].start(); } } }
3.2.4 停止Tomcat守护进程
此实现过程在org.apache.catalina.startup.Catalina.stop()实现。
public void stop() { try { // Remove the ShutdownHook first so that server.stop() // doesn‘t get invoked twice if (useShutdownHook) { Runtime.getRuntime().removeShutdownHook(shutdownHook); // If JULI is being used, re-enable JULI‘s shutdown to ensure // log messages are not lost LogManager logManager = LogManager.getLogManager(); if (logManager instanceof ClassLoaderLogManager) { ((ClassLoaderLogManager) logManager).setUseShutdownHook( true); } } } catch (Throwable t) { ExceptionUtils.handleThrowable(t); // This will fail on JDK 1.2. Ignoring, as Tomcat can run // fine without the shutdown hook. } // Shut down the server try { //获取服务 Server s = getServer(); LifecycleState state = s.getState(); if (LifecycleState.STOPPING_PREP.compareTo(state) <= 0 && LifecycleState.DESTROYED.compareTo(state) >= 0) { // Nothing to do. stop() was already called } else { //停止服务 s.stop(); s.destroy(); } } catch (LifecycleException e) { log.error("Catalina.stop", e); } }
3.2.5 单独停止Tomcat服务
此实现在org.apache.catalina.startup.Catalina.stopServer(String[] arguments)
public void stopServer(String[] arguments) { if (arguments != null) { arguments(arguments); } //获取Server Server s = getServer(); if (s == null) { // Create and execute our Digester Digester digester = createStopDigester(); File file = configFile(); //如果不存在,则通过配置文件获取Server信息 try (FileInputStream fis = new FileInputStream(file)) { InputSource is = new InputSource(file.toURI().toURL().toString()); is.setByteStream(fis); digester.push(this); digester.parse(is); } catch (Exception e) { log.error("Catalina.stop: ", e); System.exit(1); } } else { // Server object already present. Must be running as a service try { //停止服务 s.stop(); } catch (LifecycleException e) { log.error("Catalina.stop: ", e); } return; } // Stop the existing server s = getServer(); if (s.getPort()>0) { //通过SOCKET通讯发起停止指令 try (Socket socket = new Socket(s.getAddress(), s.getPort()); OutputStream stream = socket.getOutputStream()) { String shutdown = s.getShutdown(); for (int i = 0; i < shutdown.length(); i++) { stream.write(shutdown.charAt(i)); } stream.flush(); } catch (ConnectException ce) { log.error(sm.getString("catalina.stopServer.connectException", s.getAddress(), String.valueOf(s.getPort()))); log.error("Catalina.stop: ", ce); System.exit(1); } catch (IOException e) { log.error("Catalina.stop: ", e); System.exit(1); } } else { log.error(sm.getString("catalina.stopServer")); System.exit(1); } }
通过对比发现,stop()方法与stopServer()方法的区别在于,stop()在执行停止的后,同时进行destroy()操作,而stopServer()并没有发起destory操作。
同时我们可以通过Socket通讯进行关闭Tomcat,在Server.xml中配置了<Server port="8005" shutdown="SHUTDOWN">,那么Tomcat就会开启8005端口,我们可以执行
telnet x.x.x.x 8005
输入: SHUTDOWN
就会发现Tomcat停止了。
那么爱思考的小伙伴就会考虑了,这样岂不是很不安全,如果别人恶意访问我的8005端口,然后发送SHUTDOWN,那我的Tomcat岂不是就给停掉了,这样风险太大了。
放心吧,Tomcat监听的8005端口是监听127.0.0.1的8005端口而不是所有IP的8005端口,除了本机可以访问,其他电脑是不可访问的。
#查看8005端口 C:\Users\FDD>netstat -an|findstr 8005 TCP 127.0.0.1:8005 0.0.0.0:0 LISTENING #查看8080端口 C:\Users\FDD>netstat -an|findstr 8080 TCP 0.0.0.0:8080 0.0.0.0:0 LISTENING TCP 192.168.0.115:52201 58.251.100.102:8080 ESTABLISHED #通过对比可发现,8005是挂在127.0.0.1的,而8080是挂在0.0.0.0(表示本机的所有IP)的
附录:
一、Catalina生命周期
Common interface for component life cycle methods. Catalina components may implement this interface (as well as the appropriate interface(s) for the functionality they support) in order to provide a consistent mechanism to start and stop the component.
The valid state transitions for components that support Lifecycle
are:
start() ----------------------------- | | | init() | NEW -?-- INITIALIZING | | | | | ------------------?----------------------- | | |auto | | | | | \|/ start() \|/ \|/ auto auto stop() | | | INITIALIZED --?-- STARTING_PREP --?- STARTING --?- STARTED --?--- | | | | | | | |destroy()| | | | --?-----?-- ------------------------?-------------------------------- ^ | | | | | | \|/ auto auto start() | | | STOPPING_PREP ----?---- STOPPING ------?----- STOPPED -----?----- | \|/ ^ | ^ | | stop() | | | | | -------------------------- | | | | | | | | | | destroy() destroy() | | | | FAILED ----?------ DESTROYING ---?----------------- | | | ^ | | | | destroy() | |auto | | --------?----------------- \|/ | | DESTROYED | | | | stop() | ----?-----------------------------?------------------------------ Any state can transition to FAILED. Calling start() while a component is in states STARTING_PREP, STARTING or STARTED has no effect. Calling start() while a component is in state NEW will cause init() to be called immediately after the start() method is entered. Calling stop() while a component is in states STOPPING_PREP, STOPPING or STOPPED has no effect. Calling stop() while a component is in state NEW transitions the component to STOPPED. This is typically encountered when a component fails to start and does not start all its sub-components. When the component is stopped, it will try to stop all sub-components - even those it didn‘t start.
二、Tomcat 启动日志
Jan 26, 2016 11:20:31 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Server version: Apache Tomcat/7.0.69
Jan 26, 2016 11:20:31 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Server built: Apr 11 2016 07:57:09 UTC
Jan 26, 2016 11:20:31 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Server number: 7.0.69.0
Jan 26, 2016 11:20:31 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: OS Name: Linux
Jan 26, 2016 11:20:31 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: OS Version: 2.6.32-642.11.1.el6.x86_64
Jan 26, 2016 11:20:31 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Architecture: amd64
Jan 26, 2016 11:20:31 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Java Home: /usr/lib/jvm/java-1.7.0-openjdk-1.7.0.121.x86_64/jre
Jan 26, 2016 11:20:31 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: JVM Version: 1.7.0_121-mockbuild_2016_11_11_19_18-b00
Jan 26, 2016 11:20:31 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: JVM Vendor: Oracle Corporation
Jan 26, 2016 11:20:31 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: CATALINA_BASE: /home/tomcat/apache-tomcat-7.0.69
Jan 26, 2016 11:20:31 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: CATALINA_HOME: /home/tomcat/apache-tomcat-7.0.69
Jan 26, 2016 11:20:31 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Command line argument: -Djava.util.logging.config.file=/home/tomcat/apache-tomcat-7.0.69/conf/logging.properties
Jan 26, 2016 11:20:31 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Command line argument: -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
Jan 26, 2016 11:20:31 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Command line argument: -Djdk.tls.ephemeralDHKeySize=2048
Jan 26, 2016 11:20:31 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Command line argument: -Djava.endorsed.dirs=/home/tomcat/apache-tomcat-7.0.69/endorsed
Jan 26, 2016 11:20:31 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Command line argument: -Dcatalina.base=/home/tomcat/apache-tomcat-7.0.69
Jan 26, 2016 11:20:31 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Command line argument: -Dcatalina.home=/home/tomcat/apache-tomcat-7.0.69
Jan 26, 2016 11:20:31 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Command line argument: -Djava.io.tmpdir=/home/tomcat/apache-tomcat-7.0.69/temp
Jan 26, 2016 11:20:31 PM org.apache.catalina.core.AprLifecycleListener lifecycleEvent
INFO: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: /usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
Jan 26, 2016 11:20:31 PM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["http-bio-8080"]
Jan 26, 2016 11:20:31 PM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["ajp-bio-8009"]
Jan 26, 2016 11:20:31 PM org.apache.catalina.startup.Catalina load
INFO: Initialization processed in 8801 ms
至此Catalina服务已经初始化完毕,接下来就是启动Service、Server
Jan 26, 2016 11:20:31 PM org.apache.catalina.core.StandardService startInternal
INFO: Starting service Catalina
Jan 26, 2016 11:20:31 PM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet Engine: Apache Tomcat/7.0.69
Jan 26, 2016 11:20:31 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deploying web application directory /home/tomcat/apache-tomcat-7.0.69/webapps/host-manager
Jan 26, 2016 11:20:35 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deployment of web application directory /home/tomcat/apache-tomcat-7.0.69/webapps/host-manager has finished in 3,197 ms
Jan 26, 2016 11:20:35 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deploying web application directory /home/tomcat/apache-tomcat-7.0.69/webapps/manager
Jan 26, 2016 11:20:35 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deployment of web application directory /home/tomcat/apache-tomcat-7.0.69/webapps/manager has finished in 223 ms
Jan 26, 2016 11:20:35 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deploying web application directory /home/tomcat/apache-tomcat-7.0.69/webapps/ROOT
Jan 26, 2016 11:20:35 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deployment of web application directory /home/tomcat/apache-tomcat-7.0.69/webapps/ROOT has finished in 103 ms
Jan 26, 2016 11:20:35 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deploying web application directory /home/tomcat/apache-tomcat-7.0.69/webapps/docs
Jan 26, 2016 11:20:35 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deployment of web application directory /home/tomcat/apache-tomcat-7.0.69/webapps/docs has finished in 204 ms
Jan 26, 2016 11:20:35 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deploying web application directory /home/tomcat/apache-tomcat-7.0.69/webapps/examples
Jan 26, 2016 11:20:37 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deployment of web application directory /home/tomcat/apache-tomcat-7.0.69/webapps/examples has finished in 1,425 ms
Jan 26, 2016 11:20:37 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["http-bio-8080"]
Jan 26, 2016 11:20:37 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["ajp-bio-8009"]
Jan 26, 2016 11:20:37 PM org.apache.catalina.startup.Catalina start
INFO: Server startup in 5455 ms
参考资料:
[1] Tomcat启动过程分析(上)
[2] Tomcat启动过程分析(下)
Tomcat启动分析(一)