tomcat源碼版本:apache-tomcat-8.5.54-src
一、代碼流程
1、代碼入口(運行startup.bat或startup.sh) Bootstrap::main 2、初始化 -->Bootstrap::init 初始化類加載器java.net.URLClassLoader -->Bootstrap::initClassLoaders -->Bootstrap::createClassLoader加載Tomcat公共資源lib目錄 -->讀取配置文件conf\catalina.properties屬性:common.loader="${catalina.base}/lib","${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar" 如果沒有lib目錄,ClassLoaderFactory::validateFile會打印如下警告信息: 四月 16, 2020 9:35:52 上午 org.apache.catalina.startup.ClassLoaderFactory validateFile 警告: Problem with directory [E:\workspace\mot\tomcat8.5\lib], exists: [false], isDirectory: [false], canRead: [false] 四月 16, 2020 9:35:52 上午 org.apache.catalina.startup.ClassLoaderFactory validateFile 警告: Problem with directory [E:\workspace\mot\tomcat8.5\lib], exists: [false], isDirectory: [false], canRead: [false] 四月 16, 2020 9:35:52 上午 org.apache.catalina.startup.ClassLoaderFactory validateFile 警告: Problem with directory [E:\workspace\mot\tomcat8.5\lib], exists: [false], isDirectory: [false], canRead: [false] 四月 16, 2020 9:35:52 上午 org.apache.catalina.startup.ClassLoaderFactory validateFile 警告: Problem with directory [E:\workspace\mot\tomcat8.5\lib], exists: [false], isDirectory: [false], canRead: [false] 3、解析server.xml -->Bootstrap::setAwait(true);//設置await -->Bootstrap::load() ---->反射調用org.apache.catalina.startup.Catalina::load()加載server.xml ------>org.apache.catalina.startup.Catalina::createStartDigester設置Digester匹配模式 ------>Digester.parse根據模式匹配解析server.xml,生成org.apache.catalina.core.StandardServer、org.apache.catalina.core.StandardService等server.xml配置的各個元素 4、初始化server ------>org.apache.catalina.core.StandardServer::init初始化Server ------>org.apache.catalina.util.LifecycleBase::init server根據配置需要加載6個監聽器: org.apache.catalina.core.NamingContextListener org.apache.catalina.startup.VersionLoggerListener org.apache.catalina.core.AprLifecycleListener org.apache.catalina.core.JreMemoryLeakPreventionListener org.apache.catalina.mbeans.GlobalResourcesLifecycleListener org.apache.catalina.core.ThreadLocalLeakPreventionListener 其中當server處於before_init狀態時執行org.apache.catalina.startup.VersionLoggerListener::log打印tomcat版本相關信息,讀取配置文件org/apache/catalina/util/ServerInfo.properties打印tomcat版本信息: 四月 16, 2020 2:13:36 下午 org.apache.catalina.startup.VersionLoggerListener log 信息: Server.服務器版本: Apache Tomcat/@VERSION@ 四月 16, 2020 2:13:36 下午 org.apache.catalina.startup.VersionLoggerListener log 信息: 服務器構建: @VERSION_BUILT@ 四月 16, 2020 2:13:36 下午 org.apache.catalina.startup.VersionLoggerListener log 信息: 服務器版本號(:@VERSION_NUMBER@ 四月 16, 2020 2:13:36 下午 org.apache.catalina.startup.VersionLoggerListener log 信息: OS Name: Windows 7 四月 16, 2020 2:13:36 下午 org.apache.catalina.startup.VersionLoggerListener log 信息: OS.版本: 6.1 四月 16, 2020 2:13:36 下午 org.apache.catalina.startup.VersionLoggerListener log 信息: 架構: amd64 四月 16, 2020 2:13:36 下午 org.apache.catalina.startup.VersionLoggerListener log 信息: Java 環境變量: C:\java\jdk1.8.0_171\jre 四月 16, 2020 2:13:36 下午 org.apache.catalina.startup.VersionLoggerListener log 信息: JVM 版本: 1.8.0_171-b11 四月 16, 2020 2:13:36 下午 org.apache.catalina.startup.VersionLoggerListener log 信息: JVM.供應商: Oracle Corporation 四月 16, 2020 2:13:36 下午 org.apache.catalina.startup.VersionLoggerListener log 信息: CATALINA_BASE:[E:\workspace\mot\tomcat8.5] 四月 16, 2020 2:13:37 下午 org.apache.catalina.startup.VersionLoggerListener log 信息: CATALINA_HOME: E:\workspace\mot\tomcat8.5 四月 16, 2020 2:13:37 下午 org.apache.catalina.startup.VersionLoggerListener log 信息: 命令行參數:[-agentlib:jdwp=transport=dt_socket,suspend=y,address=localhost:57732] 四月 16, 2020 2:13:37 下午 org.apache.catalina.startup.VersionLoggerListener log 信息: 命令行參數:[-Dmaven.multiModuleProjectDirectory=$M2_HOME] 四月 16, 2020 2:13:37 下午 org.apache.catalina.startup.VersionLoggerListener log 信息: 命令行參數:[-Dfile.encoding=UTF-8] 需要到java path下查找libtcnative-1.dll和tcnative-1.dll,如果找不到org.apache.catalina.core.AprLifecycleListener::lifecycleEvent打印如下信息: 四月 16, 2020 2:27:21 下午 org.apache.catalina.core.AprLifecycleListener lifecycleEvent 信息: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [C:\java\jdk1.8.0_171\bin;C:\Windows\Sun\Java\bin;C:\Windows\system32;C:\Windows;C:/java/jdk1.8.0_171/bin/../jre/bin/server;C:/java/jdk1.8.0_171/bin/../jre/bin;C:/java/jdk1.8.0_171/bin/../jre/lib/amd64;C:\Program Files (x86)\Common Files\NetSarang;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files\TortoiseSVN\bin;C:\java\jdk1.8.0_171\bin;C:\java\jdk1.8.0_171\jre\bin;D:\apache-maven-3.6.1\bin;D:\apache-maven-3.6.1\bin;D:\Git\cmd;C:\Python27;D:\mongoDB4.0\bin;D:\mysql-5.6.45-winx64\bin;C:\erl10.5\bin;C:\RabbitMQServer\rabbitmq_server-3.7.17\sbin;D:\gradle-4.6/bin;D:\apache-ant-1.10.7/bin;C:\Users\Administrator\AppData\Local\GitHubDesktop\bin;D:\pythoneclipse;;.] ------>org.apache.catalina.core.StandardServer::initInternal ------>org.apache.catalina.util.LifecycleMBeanBase::initInternal ------>org.apache.catalina.deploy.NamingResourcesImpl::init初始化JNDI ------>org.apache.catalina.util.LifecycleBase::init ------>org.apache.catalina.deploy.NamingResourcesImpl::initInternal ------>org.apache.catalina.util.LifecycleMBeanBase::initInternal ------>org.apache.catalina.core.StandardService::init初始化Service ------>org.apache.catalina.util.LifecycleBase::init ------>org.apache.catalina.core.StandardService::initInternal ------>org.apache.catalina.util.LifecycleMBeanBase::initInternal ------>org.apache.catalina.core.StandardEngine::init初始化Engine ------>org.apache.catalina.util.LifecycleBase::init ------>org.apache.catalina.core.StandardEngine::initInternal ------>org.apache.catalina.core.StandardEngine::getRealm獲取Realm ------>org.apache.catalina.util.LifecycleMBeanBase::initInternal ------>org.apache.catalina.core.StandardThreadExecutor::init初始化線程池 ------>org.apache.catalina.util.LifecycleBase::init ------>org.apache.catalina.core.StandardThreadExecutor::initInternal ------>org.apache.catalina.util.LifecycleMBeanBase::initInternal ------>org.apache.catalina.mapper.MapperListener::init初始化Mapper監聽器 ------>org.apache.catalina.util.LifecycleBase::init ------>org.apache.catalina.util.LifecycleMBeanBase::initInternal ------>org.apache.catalina.connector.Connector::init初始化連接器 ------>org.apache.catalina.util.LifecycleBase::init ------>org.apache.catalina.connector.Connector::initInternal -------->org.apache.coyote.http11.Http11NioProtocol::init初始化連接協議 -------->org.apache.coyote.http11.AbstractHttp11Protocol::init -------->org.apache.coyote.AbstractProtocol::init 會打印如下信息: 四月 16, 2020 3:08:26 下午 org.apache.coyote.AbstractProtocol init 信息: 初始化協議處理器 ["http-nio-8080"] -------->org.apache.tomcat.util.net.NioEndpoint::init -------->org.apache.tomcat.util.net.AbstractJsseEndpoint::init ---------->org.apache.tomcat.util.net.AbstractEndpoint::init ------------>org.apache.tomcat.util.net.NioEndpoint::bind -------------->org.apache.tomcat.util.net.NioSelectorPool::open --------------->org.apache.tomcat.util.net.NioSelectorPool::getSharedSelector會打印如下信息: 四月 16, 2020 3:14:37 下午 org.apache.tomcat.util.net.NioSelectorPool getSharedSelector 信息: Using a shared selector for servlet write/read ------>org.apache.catalina.util.LifecycleMBeanBase::initInternal Catalina::load方法結束,會打印如下信息: 四月 16, 2020 2:59:23 下午 org.apache.catalina.startup.Catalina load 信息: Initialization processed in 23101 ms 5、啟動server -->Bootstrap::start() ---->反射調用Catalina::start() ------>org.apache.catalina.core.StandardServer::start啟動Server ------>org.apache.catalina.util.LifecycleBase::start ------>org.apache.catalina.core.StandardServer::startInternal -------->org.apache.catalina.deploy.NamingResourcesImpl::start啟動JNDI -------->org.apache.catalina.util.LifecycleBase::start -------->org.apache.catalina.deploy.NamingResourcesImpl::startInternal -------->org.apache.catalina.core.StandardService::start啟動Service -------->org.apache.catalina.util.LifecycleBase::start -------->org.apache.catalina.core.StandardService::startInternal打印如下信息: 四月 16, 2020 3:22:09 下午 org.apache.catalina.core.StandardService startInternal 信息: Starting service [Catalina] ---------->org.apache.catalina.core.StandardEngine::start啟動Engine ---------->org.apache.catalina.util.LifecycleBase::start ---------->org.apache.catalina.core.StandardEngine::startInternal打印如下信息: 四月 16, 2020 3:22:57 下午 org.apache.catalina.core.StandardEngine startInternal 信息: Starting Servlet Engine: Apache Tomcat/@VERSION@ ---------->org.apache.catalina.core.ContainerBase::startInternal ------------>org.apache.catalina.util.LifecycleBase::fireLifecycleEvent -------------->org.apache.catalina.startup.HostConfig::lifecycleEvent -------------->org.apache.catalina.startup.HostConfig::start -------------->org.apache.catalina.startup.HostConfig::deployApps ---------------->org.apache.catalina.startup.HostConfig::deployDescriptors部署XML配置 ---------------->org.apache.catalina.startup.HostConfig::deployWARs部署war包 ---------------->org.apache.catalina.startup.HostConfig::deployDirectories部署目錄,打印如下信息: 四月 16, 2020 3:38:47 下午 org.apache.catalina.startup.HostConfig deployDirectory 信息: 把web 應用程序部署到目錄 [E:\workspace\mot\tomcat8.5\webapps\docs] 四月 16, 2020 4:08:36 下午 org.apache.catalina.startup.HostConfig deployDirectory 信息: Deployment of web application directory [E:\workspace\mot\tomcat8.5\webapps\docs] has finished in [1,018,421] ms ------------>org.apache.catalina.ha.tcp.SimpleTcpCluster::start啟動Cluster ------------>org.apache.catalina.util.LifecycleBase::start -------------->org.apache.catalina.tribes.group.GroupChannel::start -------------->org.apache.catalina.ha.deploy.FarmWarDeployer::start ------------>org.apache.catalina.realm.LockOutRealm::start啟動LockOutRealm ------------>org.apache.catalina.util.LifecycleBase::start ------------>org.apache.catalina.realm.LockOutRealm::startInternal ------------>org.apache.catalina.realm.CombinedRealm::startInternal ------------>org.apache.catalina.realm.RealmBase::startInternal ------------>org.apache.catalina.realm.UserDatabaseRealm::start啟動UserDatabaseRealm ------------>org.apache.catalina.util.LifecycleBase::start ------------>org.apache.catalina.realm.UserDatabaseRealm::startInternal ------------>org.apache.catalina.realm.RealmBase::startInternal ------------>org.apache.catalina.core.StandardHost::start啟動容器 ------------>org.apache.catalina.util.LifecycleBase::start ------------>org.apache.catalina.core.StandardHost::startInternal -------------->org.apache.catalina.core.StandardContext::startInternal -------------->org.apache.jasper.servlet.JasperInitializer::onStartup -------------->org.apache.jasper.servlet.TldScanner::scan -------------->org.apache.jasper.servlet.TldScanner::scanJars 掃描所有部署應用下面WEB-INF下的tld文件 如果找不到就打印如下信息: 四月 16, 2020 3:57:36 下午 org.apache.jasper.servlet.TldScanner scanJars 信息: 至少有一個JAR被掃描用於TLD但尚未包含TLD。 為此記錄器啟用調試日志記錄,以獲取已掃描但未在其中找到TLD的完整JAR列表。 在掃描期間跳過不需要的JAR可以縮短啟動時間和JSP編譯時間。 ---------------->org.apache.catalina.startup.HostConfig::deployDirectories部署目錄,打印如下信息: 四月 16, 2020 4:08:36 下午 org.apache.catalina.startup.HostConfig deployDirectory 信息: Deployment of web application directory [E:\workspace\mot\tomcat8.5\webapps\docs] has finished in [1,018,421] ms ------------>org.apache.catalina.core.StandardPipeline::start啟動Pipeline ------------>org.apache.catalina.util.LifecycleBase::start ------------>org.apache.catalina.core.StandardPipeline::startInternal ------------>org.apache.catalina.valves.AccessLogValve::start啟動AccessLogValve ------------>org.apache.catalina.util.LifecycleBase::start ------------>org.apache.catalina.valves.AccessLogValve::startInternal ------------>org.apache.catalina.valves.AbstractAccessLogValve::startInternal ---------->org.apache.catalina.core.StandardThreadExecutor::start啟動線程池 ---------->org.apache.catalina.util.LifecycleBase::start ---------->org.apache.catalina.core.StandardThreadExecutor::startInternal ---------->org.apache.catalina.mapper.MapperListener::start啟動Mapper監聽器 ---------->org.apache.catalina.util.LifecycleBase::start ---------->org.apache.catalina.mapper.MapperListener::startInternal ---------->org.apache.catalina.connector.Connector::start啟動連接器 ---------->org.apache.catalina.util.LifecycleBase::start ---------->org.apache.catalina.connector.Connector::startInternal ------------>org.apache.coyote.http11.Http11NioProtocol::start啟動連接協議 ------------>org.apache.coyote.AbstractProtocol::start 打印如下信息: 四月 16, 2020 4:42:15 下午 org.apache.coyote.AbstractProtocol start 信息: 開始協議處理句柄["http-nio-8080"] ------------>org.apache.tomcat.util.net.NioEndpoint::start ------------>org.apache.tomcat.util.net.AbstractEndpoint::start ------------>org.apache.tomcat.util.net.NioEndpoint::startInternal Catalina::start方法最后打印如下信息: 四月 16, 2020 4:47:43 下午 org.apache.catalina.startup.Catalina start 信息: Server startup in 385951 ms 6、監聽停止端口 ------>Catalina::await ------>org.apache.catalina.core.StandardServer::await 使用ServerSocket監聽localhost 8005端口 接收關閉指令(telnet localhost 8005 >SHUTDOWN) StandardServer::await方法打印如下信息: 四月 16, 2020 4:49:30 下午 org.apache.catalina.core.StandardServer await 信息: A valid shutdown command was received via the shutdown port. Stopping the Server instance. 7、關閉server ------>Catalina::stop ------>org.apache.catalina.core.StandardServer::stop停止server ------>org.apache.catalina.util.LifecycleBase::stop ------>org.apache.catalina.core.StandardServer::stopInternal -------->org.apache.catalina.core.StandardService::stop停止Service -------->org.apache.catalina.util.LifecycleBase::stop -------->org.apache.catalina.core.StandardService::stopInternal 四月 16, 2020 4:51:52 下午 org.apache.catalina.core.StandardService stopInternal 信息: 正在停止服務[Catalina] -------->org.apache.catalina.connector.Connector::pause -------->org.apache.coyote.http11.Http11NioProtocol::pause -------->org.apache.coyote.AbstractProtocol::pause打印如下信息: 四月 16, 2020 4:51:52 下午 org.apache.coyote.AbstractProtocol pause 信息: Pausing ProtocolHandler ["http-nio-8080"] -------->org.apache.coyote.AbstractProtocol::closeServerSocketGraceful ---------->org.apache.tomcat.util.net.NioEndpoint::closeServerSocketGraceful -------->org.apache.catalina.core.StandardEngine::stop -------->org.apache.catalina.util.LifecycleBase::stop ---------->org.apache.catalina.core.ContainerBase::stopInternal -------->Pipeline::stop -------->StandardHost::stop -------->Realm::stop -------->Cluster::stop -------->StandardContext::stopInternal -------->StandardWrapper::stopInternal -------->StandardContext::filterStop -------->StandardContext::listenerStop -------->StandardContext::resourcesStop -------->Connector::stop -------->Connector::stopInternal -------->org.apache.coyote.AbstractProtocol::stop會打印如下信息: 四月 16, 2020 4:51:53 下午 org.apache.coyote.AbstractProtocol stop 信息: 正在停止ProtocolHandler ["http-nio-8080"] -------->MapperListener::stop -------->StandardThreadExecutor::stop -------->NamingResourcesImpl::stop 8、銷毀server ------>org.apache.catalina.core.StandardServer::destroy銷毀server ------>StandardService::destroy -------->org.apache.coyote.AbstractProtocol::destroy -------->org.apache.catalina.connector.Connector::destroyInternal -------->org.apache.coyote.AbstractProtocol::destroy會打印如下信息: 四月 16, 2020 5:01:37 下午 org.apache.coyote.AbstractProtocol destroy 信息: 正在摧毀協議處理器 ["http-nio-8080"] -------->org.apache.catalina.core.StandardEngine::destroy -------->org.apache.catalina.core.ContainerBase::destroyInternal ------>NamingResourcesImpl::destroy
load:
initialize:
start:
deploy APP:
二、啟停關鍵組件
1、啟停腳本 startup.bat、startup.sh、shutdown.bat、shutdown.sh內部調用catalina.bat或者catalina.sh,catalina.bat或者catalina.sh內部調用org.apache.catalina.startup.Bootstrap,並傳入相應的參數;或者直接運行catalina.bat或者catalina.sh加入我們需要的參數 2、org.apache.catalina.startup.Bootstrap啟動器 2.1 設置catalina.home和catalina.base目錄 2.2 接收運行參數 2.3 init() --initClassLoaders()創建三個類加載器:commonLoader、catalinaLoader、sharedLoader,默認都是URLClassLoader --反射調用org.apache.catalina.startup.Catalina::setParentClassLoader設置其父加載器為sharedLoader 2.4 反射調用org.apache.catalina.startup.Catalina::setAwait、load、start、stop、stopServer方法 3、org.apache.catalina.startup.Catalina-實際server啟動者 3.1 setAwait():設置監聽終止server指令的端口 3.2 load(): --initDirs();初始化系統默認的臨時文件目錄 --initNaming();在創建Digester匹配模式之前初始化JNDI服務 --createStartDigester()創建啟動Digester匹配模式 --digester.push(this);將org.apache.catalina.startup.Catalina作為第一個元素 --digester.parse(inputSource);解析Server.xml --org.apache.catalina.core.StandardServer.setCatalina(this); --org.apache.catalina.core.StandardServer.setCatalinaHome(Bootstrap.getCatalinaHomeFile());設置catalina.home --org.apache.catalina.core.StandardServer.setCatalinaBase(Bootstrap.getCatalinaBaseFile());設置catalina.base --initStreams();重定向System.out and System.err 使用自定義的流 --org.apache.catalina.core.StandardServer.init()初始化server
3.3 start() --org.apache.catalina.core.StandardServer.start() --await()阻塞監聽 --stop()當監聽到關閉命令進行關閉 3.4 stop() --org.apache.catalina.core.StandardServer.stop();停止server --org.apache.catalina.core.StandardServer.destroy();銷毀server 3.5 stopServer()終止server --createStopDigester():創建終止Digester匹配模式 --org.apache.catalina.core.StandardServer.stop();停止server --org.apache.catalina.core.StandardServer.destroy();銷毀server
三、關閉鈎子
對java而言,虛擬機會對以下幾種操作進行關閉:
(1)系統調用System.exit()方法;
(2)程序最后一個守護線程退出時,應用程序正常退出;
(3)用戶強行中斷程序運行,比如ctrl+c等其他方式中斷java程序;
關閉鈎子是一個特殊的線程,可以人為編碼實現。java進程在停止之前,會調用已注冊關閉鈎子的run()方法。
關閉鈎子的生成:
1.創建Thread的子類;
2.重寫run方法,應用程序關閉時會調用該方法,不需要調用start方法;
3.在應用中實例化關閉鈎子類;
4.使用Runtime注冊關閉鈎子:Runtime.getRuntime().addShutdownHook(shutdownHook);
tomcat關閉鈎子舉例:
public class Catalina { .... //默認使用關閉鈎子 protected boolean useShutdownHook = true; protected Thread shutdownHook = null; public void start() { .... if (useShutdownHook) { if (shutdownHook == null) { shutdownHook = new CatalinaShutdownHook(); } Runtime.getRuntime().addShutdownHook(shutdownHook); LogManager logManager = LogManager.getLogManager(); if (logManager instanceof ClassLoaderLogManager) { ((ClassLoaderLogManager) logManager).setUseShutdownHook(false); } } .... } 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(); } } } } ...... }