首先,我們複製啓動Tomcat時候Console窗口打印的日誌,可以見到一些關鍵路徑,個人認爲比較重要的地方標註了出來:
八月 14, 2019 3:23:50 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: Server version: Apache Tomcat/@VERSION@
八月 14, 2019 3:23:50 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: Server built: @VERSION_BUILT@
八月 14, 2019 3:23:50 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: Server number: @VERSION_NUMBER@
八月 14, 2019 3:23:50 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: OS Name: Windows 7
八月 14, 2019 3:23:50 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: OS Version: 6.1
八月 14, 2019 3:23:50 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: Architecture: amd64
八月 14, 2019 3:23:50 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: Java Home: D:\Java\jdk1.8.0_131\jre
八月 14, 2019 3:23:50 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: JVM Version: 1.8.0_131-b11
八月 14, 2019 3:23:50 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: JVM Vendor: Oracle Corporation
八月 14, 2019 3:23:50 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: CATALINA_BASE: D:\Tomcat\apache-tomcat-8.5.43-src
八月 14, 2019 3:23:50 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: CATALINA_HOME: D:\Tomcat\apache-tomcat-8.5.43-src
八月 14, 2019 3:23:50 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: Command line argument: -agentlib:jdwp=transport=dt_socket,suspend=y,address=localhost:60317
八月 14, 2019 3:23:50 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: Command line argument: -Dfile.encoding=UTF-8
八月 14, 2019 3:23:50 下午 org.apache.catalina.core.AprLifecycleListener lifecycleEvent
信息: Loaded APR based Apache Tomcat Native library [1.2.23] using APR version [1.7.0].
八月 14, 2019 3:23:50 下午 org.apache.catalina.core.AprLifecycleListener lifecycleEvent
信息: APR capabilities: IPv6 [true], sendfile [true], accept filters [false], random [true].
八月 14, 2019 3:23:50 下午 org.apache.catalina.core.AprLifecycleListener lifecycleEvent
信息: APR/OpenSSL configuration: useAprConnector [false], useOpenSSL [true]
八月 14, 2019 3:23:50 下午 org.apache.catalina.core.AprLifecycleListener initializeSSL
信息: OpenSSL successfully initialized [OpenSSL 1.1.1c 28 May 2019]
八月 14, 2019 3:23:57 下午 org.apache.coyote.AbstractProtocol init
信息: Initializing ProtocolHandler ["http-nio-8080"]
八月 14, 2019 3:24:12 下午 org.apache.tomcat.util.net.NioSelectorPool getSharedSelector
信息: Using a shared selector for servlet write/read
八月 14, 2019 3:24:12 下午 org.apache.coyote.AbstractProtocol init
信息: Initializing ProtocolHandler ["ajp-nio-8009"]
八月 14, 2019 3:24:12 下午 org.apache.tomcat.util.net.NioSelectorPool getSharedSelector
信息: Using a shared selector for servlet write/read
八月 14, 2019 3:24:12 下午 org.apache.catalina.startup.Catalina load
信息: Initialization processed in 22907 ms
八月 14, 2019 3:24:13 下午 org.apache.catalina.core.StandardService startInternal
信息: Starting service [Catalina]
八月 14, 2019 3:24:13 下午 org.apache.catalina.core.StandardEngine startInternal
信息: Starting Servlet Engine: Apache Tomcat/@VERSION@
八月 14, 2019 3:24:13 下午 org.apache.catalina.startup.HostConfig deployDirectory
信息: Deploying web application directory [D:\Tomcat\apache-tomcat-8.5.43-src\webapps\docs]
八月 14, 2019 3:24:13 下午 org.apache.jasper.servlet.TldScanner scanJars
信息: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
八月 14, 2019 3:24:13 下午 org.apache.catalina.startup.HostConfig deployDirectory
信息: Deployment of web application directory [D:\Tomcat\apache-tomcat-8.5.43-src\webapps\docs] has finished in [597] ms
八月 14, 2019 3:24:13 下午 org.apache.catalina.startup.HostConfig deployDirectory
信息: Deploying web application directory [D:\Tomcat\apache-tomcat-8.5.43-src\webapps\examples]
八月 14, 2019 3:24:13 下午 org.apache.jasper.servlet.TldScanner scanJars
信息: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
八月 14, 2019 3:24:14 下午 org.apache.catalina.core.ApplicationContext log
信息: ContextListener: contextInitialized()
八月 14, 2019 3:24:14 下午 org.apache.catalina.core.ApplicationContext log
信息: SessionListener: contextInitialized()
八月 14, 2019 3:24:14 下午 org.apache.catalina.core.ApplicationContext log
信息: ContextListener: attributeAdded('StockTicker', 'async.Stockticker@13bace10')
八月 14, 2019 3:24:14 下午 org.apache.catalina.startup.HostConfig deployDirectory
信息: Deployment of web application directory [D:\Tomcat\apache-tomcat-8.5.43-src\webapps\examples] has finished in [362] ms
八月 14, 2019 3:24:14 下午 org.apache.catalina.startup.HostConfig deployDirectory
信息: Deploying web application directory [D:\Tomcat\apache-tomcat-8.5.43-src\webapps\host-manager]
八月 14, 2019 3:24:14 下午 org.apache.jasper.servlet.TldScanner scanJars
信息: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
八月 14, 2019 3:24:14 下午 org.apache.catalina.startup.HostConfig deployDirectory
信息: Deployment of web application directory [D:\Tomcat\apache-tomcat-8.5.43-src\webapps\host-manager] has finished in [62] ms
八月 14, 2019 3:24:14 下午 org.apache.catalina.startup.HostConfig deployDirectory
信息: Deploying web application directory [D:\Tomcat\apache-tomcat-8.5.43-src\webapps\manager]
八月 14, 2019 3:24:14 下午 org.apache.jasper.servlet.TldScanner scanJars
信息: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
八月 14, 2019 3:24:14 下午 org.apache.catalina.startup.HostConfig deployDirectory
信息: Deployment of web application directory [D:\Tomcat\apache-tomcat-8.5.43-src\webapps\manager] has finished in [60] ms
八月 14, 2019 3:24:14 下午 org.apache.catalina.startup.HostConfig deployDirectory
信息: Deploying web application directory [D:\Tomcat\apache-tomcat-8.5.43-src\webapps\ROOT]
八月 14, 2019 3:24:14 下午 org.apache.jasper.servlet.TldScanner scanJars
信息: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
八月 14, 2019 3:24:14 下午 org.apache.catalina.startup.HostConfig deployDirectory
信息: Deployment of web application directory [D:\Tomcat\apache-tomcat-8.5.43-src\webapps\ROOT] has finished in [48] ms
八月 14, 2019 3:24:14 下午 org.apache.coyote.AbstractProtocol start
信息: Starting ProtocolHandler ["http-nio-8080"]
八月 14, 2019 3:24:14 下午 org.apache.coyote.AbstractProtocol start
信息: Starting ProtocolHandler ["ajp-nio-8009"]
八月 14, 2019 3:24:14 下午 org.apache.catalina.startup.Catalina start
信息: Server startup in 1324 ms
開始讀代碼,定位到Tomcat的啓動類,Bootstrap的Main方法:
/**
* Main method and entry point when starting Tomcat via the provided
* scripts.
*
* @param args Command line arguments to be processed
*/
public static void main(String args[]) {
System.out.println("Tomcat is starting!");
if (daemon == null) {
// Don't set daemon until init() has completed
Bootstrap bootstrap = new Bootstrap();
try {
bootstrap.init();//1.初始化方法
} 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";
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);//2.讀取配置方法
daemon.start();//3.啓動方法
if (null == daemon.getServer()) {
System.exit(1);
}
} 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);
}
}
關注到bootstrap.init()是初始化方法,看看到底初始化進行了哪些操作。
在探究init()方法之前,我們先關注BootStrap的靜態代碼塊。我們知道,在類首次加載的時候,靜態代碼塊會執行,且早於main方法。因此static代碼塊亦可以當做是初始化的一種:
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());
}
static代碼塊實際上進行的是全局的兩個參數的配置:
- Globals.CATALINA_HOME_PROP: catalina.home- tomcat product installation path.
Home(安裝目錄):指向可共用目錄的父目錄,即bin和lib的父目錄。
- Globals.CATALINA_BASE_PROP:catalina.base- tomcat instance installation path
Base(工作目錄):指向不可共用目錄的父目錄,即conf、logs、temp、webapps和work的父目錄。
在Tomcat7中,這個配置還是顯示進行的,可參考博文:https://www.cnblogs.com/huanghongbo/p/6127721.html
在當前版本Tomcat8.5.43中,已經使用靜態代碼塊來初始化了。
注意一下此處,System.getProperty(Globals.CATALINA_HOME_PROP) 會首先讀取環境變量中的配置。
目前還沒有碰到多個Tomcat實例的情況,故還沒實際使用個性化配置catalina_base。默認情況下catalina_base和catalina_home是一致的。
下面繼續探究Bootstrap的init()方法:
/**
* Initialize daemon.
* @throws Exception Fatal initialization error
*/
public void init() throws Exception {
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.getConstructor().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;//通過反射創建了Catalina實例,賦值catalinaDaemon
}
init()方法爲Bootstrap初始化類加載器。Tomcat的類加載器具有隔離、共享的特性。多個類加載器各司其職,又相互隔離,使得Tomcat的類加載工作安全高效。具體的細節可參考《Tomcat架構解析》2.4.2節 Tomcat加載器一章。
initClassLoaders()方法初始化了3個類加載器,分別是:
- Common-以JVM的類加載器爲父類,位於Tomcat類加載器的頂層,負責加載Tomcat應用服務器內部和Web應用均可見類。
- Catalina-繼承Common類加載器,用於加載Tomcat應用服務器,負責加載只有Tomcat應用服務器內部可見類。
- Shared-繼承Common類加載器,是所有Web應用的父加載器,負責加載Web 應用共享的類。
之後將initClassLoaders()中創建的catalinaLoader設置爲當前線程的類加載器。
Thread.currentThread().setContextClassLoader(catalinaLoader);
通過反射創建Catalina實例,賦值給catalinaDaemon。並設置 Catalina的父加載器爲initClassLoaders()中創建的sharedLoader。
可見init()主要有兩個目的:
- 設置Tomcat應用的類加載器;
- 生成Catalina實例,並設置其父加載器。
回到main方法,在初次創建時:
/**
* Main method and entry point when starting Tomcat via the provided
* scripts.
*
* @param args Command line arguments to be processed
*/
public static void main(String args[]) {
...
try {
String command = "start";
...
} else if (command.equals("start")) {
daemon.setAwait(true);
daemon.load(args);
daemon.start();
if (null == daemon.getServer()) {
System.exit(1);
}
...
}
看名字即可知道有兩個重要的方法,load()方法和start()方法。接下來我們就查看這兩個方法分別做了什麼。
daemon.load(args)實際上通過反射調用的是Catalina類的load()方法。
/**
* Start a new server instance.
*/
public void load() {
if (loaded) {
return;
}
loaded = true;
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;
//中間的一大串都是爲了找到server.xml
try {
try {
file = configFile();
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) {
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);//解析server.xml
} 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
}
}
}
getServer().setCatalina(this);//StandardServer賦值Catalina
getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
// 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");
}
}
- initDirs()-加載臨時文件夾地址
- initNaming()-加載useNaming屬性,命名上下文
- createStartDigester()-重點方法!構建Digester。後續通過Digester解析server.xml,創建Server、Service、Engine、Host、Context等容器的屬性及關係。參考《Tomcat架構解析》3.3章。
解析完Servel.xml之後,getServer()取到的就是createStartDigester()中的org.apache.catalina.core.StandardServer的實例了。
接着是另一個重點方法:
getServer().init()
StandardServer繼承自LifecycleBase這個抽象類。LifecycleBase是生命週期管理的重要抽象類。其init()方法:
public final synchronized void init() throws LifecycleException {
if (!state.equals(LifecycleState.NEW)) {
invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
}
try {
setStateInternal(LifecycleState.INITIALIZING, null, false);
initInternal();
setStateInternal(LifecycleState.INITIALIZED, null, false);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
setStateInternal(LifecycleState.FAILED, null, false);
throw new LifecycleException(
sm.getString("lifecycleBase.initFail",toString()), t);
}
}
其中Lifecycle.BEFORE_INIT_EVENT爲生命週期中的事件;LifecycleState.INITIALIZING爲生命週期的階段。兩者的關係可以從以下代碼得到:
NEW(false, null),
INITIALIZING(false, Lifecycle.BEFORE_INIT_EVENT),
INITIALIZED(false, Lifecycle.AFTER_INIT_EVENT),
STARTING_PREP(false, Lifecycle.BEFORE_START_EVENT),
STARTING(true, Lifecycle.START_EVENT),
STARTED(true, Lifecycle.AFTER_START_EVENT),
STOPPING_PREP(true, Lifecycle.BEFORE_STOP_EVENT),
STOPPING(false, Lifecycle.STOP_EVENT),
STOPPED(false, Lifecycle.AFTER_STOP_EVENT),
DESTROYING(false, Lifecycle.BEFORE_DESTROY_EVENT),
DESTROYED(false, Lifecycle.AFTER_DESTROY_EVENT),
FAILED(false, null);
setStateInternal方法中,會把生命週期事件通知給註冊的監聽器,在LifeCycleBase.fireLifecycleEvent中體現:
/**
* Allow sub classes to fire {@link Lifecycle} events.
*
* @param type Event type
* @param data Data associated with event.
*/
protected void fireLifecycleEvent(String type, Object data) {
LifecycleEvent event = new LifecycleEvent(this, type, data);
for (LifecycleListener listener : lifecycleListeners) {
listener.lifecycleEvent(event);
}
}
回到LifecycleBase.init()方法,可以看到生命週期的通知事件都由LifecycleBase完成了。那麼繼承自LifecycleBase的類只需要關注自己的業務邏輯即可,實現抽象函數initInternal(),這裏相當於實現了一個切片。具體的方式,以此處爲例,getServer().init()就會執行StandardServer的initInternal():
/**
* Invoke a pre-startup initialization. This is used to allow connectors
* to bind to restricted ports under Unix operating environments.
*/
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
// Register global String cache
// Note although the cache is global, if there are multiple Servers
// present in the JVM (may happen when embedding) then the same cache
// will be registered under multiple names
onameStringCache = register(new StringCache(), "type=StringCache");
// Register the MBeanFactory
MBeanFactory factory = new MBeanFactory();
factory.setContainer(this);
onameMBeanFactory = register(factory, "type=MBeanFactory");
// Register the naming resources
globalNamingResources.init();
// Populate the extension validator with JARs from common and shared
// class loaders
if (getCatalina() != null) {
ClassLoader cl = getCatalina().getParentClassLoader();
// Walk the class loader hierarchy. Stop at the system class loader.
// This will add the shared (if present) and common class loaders
while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
if (cl instanceof URLClassLoader) {
URL[] urls = ((URLClassLoader) cl).getURLs();
for (URL url : urls) {
if (url.getProtocol().equals("file")) {
try {
File f = new File (url.toURI());
if (f.isFile() &&
f.getName().endsWith(".jar")) {
ExtensionValidator.addSystemResource(f);
}
} catch (URISyntaxException e) {
// Ignore
} catch (IOException e) {
// Ignore
}
}
}
}
cl = cl.getParent();
}
}
// Initialize our defined Services
for (int i = 0; i < services.length; i++) {
services[i].init();
}
}
這裏的重點方法爲
// Initialize our defined Services
for (int i = 0; i < services.length; i++) {
services[i].init();
}
即StandardServer會遍歷執行Servel.xml中配置的Service,也就是StandardService類的init()方法。
同樣的StandardService也繼承自LifecycleBase,因此也只要關注其initInternal()方法即可:
/**
* Invoke a pre-startup initialization. This is used to allow connectors
* to bind to restricted ports under Unix operating environments.
*/
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
if (engine != null) {
engine.init();
}
// Initialize any Executors
for (Executor executor : findExecutors()) {
if (executor instanceof JmxEnabled) {
((JmxEnabled) executor).setDomain(getDomain());
}
executor.init();
}
// Initialize mapper listener
mapperListener.init();
// Initialize our defined Connectors
synchronized (connectorsLock) {
for (Connector connector : connectors) {
try {
connector.init();
} catch (Exception e) {
String message = sm.getString(
"standardService.connector.initFailed", connector);
log.error(message, e);
if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
throw new LifecycleException(message);
}
}
}
}
StandardService的重點方法有三個:
- engine.init(); 初始化Servlet引擎,引擎只負責請求的處理不需要考慮請求連接,協議等等。
- executor.init(); 初始化線程池,該線程池可以在組件中共享。
- connector.init(); 初始化連接器,connecto負責監聽、讀取請求,對請求進行制定協議的解析,匹配正確的處理容器,反饋響應。
由於篇幅太長,將在下一篇文章中繼續Tomcat的源碼解析之旅。