首先,我们复制启动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():初始化线程池,该线程池可以在组件中共享。
- mapperListener.init():初始化监听路由。
- connector.init():初始化连接器,connecto负责监听、读取请求,对请求进行制定协议的解析,匹配正确的处理容器,反馈响应。
由于篇幅太长,将在下一篇文章中继续Tomcat的源码解析之旅。