启动脚本
# ./startup.sh
或者
# ./catalina.sh run
整体流程
可以先看下整体流程,启动的入口是从Bootstrap类中的main函数发起,逐步int和start
代码浅析
Bootstrap入口
Tomcat源码就从它的main方法开始。Tomcat的main方法在org.apache.catalina.startup.Bootstrap 里。 如下代码我们就是创建一个 Bootstrap 对象,调用它的 init 方法初始化,然后根据启动参数,分别调用 Bootstrap 对象的不同方法。
public final class Bootstrap {
ClassLoader commonLoader = null;
ClassLoader catalinaLoader = null;
ClassLoader sharedLoader = null;
/**
* 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[]) {
synchronized (daemonLock) {
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;
} 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);
daemon.start();
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中我们可以看到有如下三个classloader
ClassLoader commonLoader = null;
ClassLoader catalinaLoader = null;
ClassLoader sharedLoader = null;
这几个类加载器(核心的catalinaLoad)都会在后面的init方法中初始化,往下看下Bootstrap的init方法是如何初始化catalina的
/**
* 初始化守护进程
* @throws Exception Fatal initialization error
*/
public void init() throws Exception {
// 初始化classloader(包括catalinaLoader),往下看
initClassLoaders();
// 设置当前的线程的contextClassLoader为catalinaLoader
Thread.currentThread().setContextClassLoader(catalinaLoader);
SecurityClassLoad.securityClassLoad(catalinaLoader);
// 通过catalinaLoader加载Catalina,并初始化startupInstance对象
if (log.isDebugEnabled()) {
log.debug("Loading startup class");
}
Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.getConstructor().newInstance();
// 通过反射调用setParentClassLoader
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;
}
主要的代码在
// 初始化classloader(包括catalinaLoader),往下看
initClassLoaders();
如何初始化Classloader
可以看出,catalinaLoader和sharedLoader的父classLoader是commonLoader
private void initClassLoaders() {
try {
commonLoader = createClassLoader("common", null);
if (commonLoader == null) {
// no config file, default to this loader - we might be in a 'single' env.
commonLoader = this.getClass().getClassLoader();
}
catalinaLoader = createClassLoader("server", commonLoader);
sharedLoader = createClassLoader("shared", commonLoader);
} catch (Throwable t) {
handleThrowable(t);
log.error("Class loader creation threw exception", t);
System.exit(1);
}
}
如何创建classLoader
private ClassLoader createClassLoader(String name, ClassLoader parent)
throws Exception {
String value = CatalinaProperties.getProperty(name + ".loader");
if ((value == null) || (value.equals(""))) {
return parent;
}
value = replace(value);
List<Repository> repositories = new ArrayList<>();
String[] repositoryPaths = getPaths(value);
for (String repository : repositoryPaths) {
// Check for a JAR URL repository
try {
@SuppressWarnings("unused")
URL url = new URL(repository);
repositories.add(new Repository(repository, RepositoryType.URL));
continue;
} catch (MalformedURLException e) {
// Ignore
}
// Local repository
if (repository.endsWith("*.jar")) {
repository = repository.substring
(0, repository.length() - "*.jar".length());
repositories.add(new Repository(repository, RepositoryType.GLOB));
} else if (repository.endsWith(".jar")) {
repositories.add(new Repository(repository, RepositoryType.JAR));
} else {
repositories.add(new Repository(repository, RepositoryType.DIR));
}
}
return ClassLoaderFactory.createClassLoader(repositories, parent);
}
上面方法的逻辑从catalina.property文件里找到common.loader,shared.loader,server.loader对应的值,然后构造Responsitory列表,再将Responsitory列表传入ClassLoaderFactory.createClassLoader方法,返回的是URLClassLoader,在catalina.property文件里
common.loader="${catalina.base}/lib","${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar"
server.loader=
shared.loader=
其中 shared.loader, server.loader 是没有值的,createClassLoader 方法里如果没有值的话,就返回传入的 parent ClassLoader,也就是说,commonLoader,catalinaLoader,sharedLoader 其实是一个对象
初始化完这三个ClassLoader对象后,init()方法就可以使用catalinaClassLoader加载org.apache.catelina.startup.Catalina类,并创建一个对象,然后通过反射调用这个对象的setParentClassLoader方法,传入的参数是sharedClassLoader,最后把这个Catalina对象赋值给Bootstrap的catalinaDaemon属性,如下
Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.getConstructor().newInstance();
// 通过反射调用setParentClassLoader
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;
org.apache.catelina.startup.Catalina类初始化
前面分析了Bootstrap类中执行bootstrap.init()后,通过Boostrasp里面的执行命令
if (command.equals("startd")) {
args[args.length - 1] = "start";
daemon.load(args);
daemon.start();
}
daemon.load
我们先看下里面的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);
}
知道进入Catalina类的load是通过反射进入的
/*
* Load using arguments
*/
public void load(String args[]) {
try {
if (arguments(args)) {
load();
}
} catch (Exception e) {
e.printStackTrace(System.out);
}
}
load的加载本质是初始化Server实例
/**
* Start a new server instance.
*/
public void load() {
// 如果已经加载则推出
if (loaded) {
return;
}
loaded = true;
long t1 = System.nanoTime();
// Before digester - it may be needed
initNaming();
// 解析 server.xml
parseServerXml(true);
Server s = getServer();
if (s == null) {
return;
}
getServer().setCatalina(this);
getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
// Stream redirection
initStreams();
// 初始化Server
try {
getServer().init();
} catch (LifecycleException e) {
if (throwOnInitFailure) {
throw new java.lang.Error(e);
} else {
log.error(sm.getString("catalina.initError"), e);
}
}
if(log.isInfoEnabled()) {
log.info(sm.getString("catalina.init", Long.toString(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t1))));
}
}
总体流程如下:
initNaming
设置额外的系统变量
protected void initNaming() {
// Setting additional variables
if (!useNaming) {
log.info(sm.getString("catalina.noNaming"));
System.setProperty("catalina.useNaming", "false");
} else {
System.setProperty("catalina.useNaming", "true");
String value = "org.apache.naming";
String oldValue =
System.getProperty(javax.naming.Context.URL_PKG_PREFIXES);
if (oldValue != null) {
value = value + ":" + oldValue;
}
System.setProperty(javax.naming.Context.URL_PKG_PREFIXES, value);
if( log.isDebugEnabled() ) {
log.debug("Setting naming prefix=" + value);
}
value = System.getProperty
(javax.naming.Context.INITIAL_CONTEXT_FACTORY);
if (value == null) {
System.setProperty
(javax.naming.Context.INITIAL_CONTEXT_FACTORY,
"org.apache.naming.java.javaURLContextFactory");
} else {
log.debug("INITIAL_CONTEXT_FACTORY already set " + value );
}
}
}
parseServerXml
server.xml解析,这几分了三个部分
protected void parseServerXml(boolean start) {
// Set configuration source
ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(Bootstrap.getCatalinaBaseFile(), getConfigFile()));
File file = configFile();
if (useGeneratedCode && !Digester.isGeneratedCodeLoaderSet()) {
// Load loader
String loaderClassName = generatedCodePackage + ".DigesterGeneratedCodeLoader";
try {
Digester.GeneratedCodeLoader loader = (Digester.GeneratedCodeLoader)
Catalina.class.getClassLoader().loadClass(loaderClassName).getDeclaredConstructor().newInstance();
Digester.setGeneratedCodeLoader(loader);
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.info(sm.getString("catalina.noLoader", loaderClassName), e);
} else {
log.info(sm.getString("catalina.noLoader", loaderClassName));
}
// No loader so don't use generated code
useGeneratedCode = false;
}
}
// 初始化server.xml
File serverXmlLocation = null;
String xmlClassName = null;
if (generateCode || useGeneratedCode) {
xmlClassName = start ? generatedCodePackage + ".ServerXml" : generatedCodePackage + ".ServerXmlStop";
}
if (generateCode) {
if (generatedCodeLocationParameter != null) {
generatedCodeLocation = new File(generatedCodeLocationParameter);
if (!generatedCodeLocation.isAbsolute()) {
generatedCodeLocation = new File(Bootstrap.getCatalinaHomeFile(), generatedCodeLocationParameter);
}
} else {
generatedCodeLocation = new File(Bootstrap.getCatalinaHomeFile(), "work");
}
serverXmlLocation = new File(generatedCodeLocation, generatedCodePackage);
if (!serverXmlLocation.isDirectory() && !serverXmlLocation.mkdirs()) {
log.warn(sm.getString("catalina.generatedCodeLocationError", generatedCodeLocation.getAbsolutePath()));
// Disable code generation
generateCode = false;
}
}
// 解析xml,解析完之后,xml里面定义的各种标签就有对应的实现类了
ServerXml serverXml = null;
if (useGeneratedCode) {
serverXml = (ServerXml) Digester.loadGeneratedClass(xmlClassName);
}
if (serverXml != null) {
serverXml.load(this);
} else {
try (ConfigurationSource.Resource resource = ConfigFileLoader.getSource().getServerXml()) {
// Create and execute our Digester
Digester digester = start ? createStartDigester() : createStopDigester();
InputStream inputStream = resource.getInputStream();
InputSource inputSource = new InputSource(resource.getURI().toURL().toString());
inputSource.setByteStream(inputStream);
digester.push(this);
if (generateCode) {
digester.startGeneratingCode();
generateClassHeader(digester, start);
}
digester.parse(inputSource);
if (generateCode) {
generateClassFooter(digester);
try (FileWriter writer = new FileWriter(new File(serverXmlLocation,
start ? "ServerXml.java" : "ServerXmlStop.java"))) {
writer.write(digester.getGeneratedCode().toString());
}
digester.endGeneratingCode();
Digester.addGeneratedClass(xmlClassName);
}
} catch (Exception e) {
log.warn(sm.getString("catalina.configFail", file.getAbsolutePath()), e);
if (file.exists() && !file.canRead()) {
log.warn(sm.getString("catalina.incorrectPermissions"));
}
}
}
}
initStreams
替换掉System.out、System.err 为自定义的PrintStream
protected void initStreams() {
// Replace System.out and System.err with a custom PrintStream
System.setOut(new SystemLogHandler(System.out));
System.setErr(new SystemLogHandler(System.err));
}
getServer().init()
初始化一系列组件和服务
daemon.start()
前面讲到了一系列组件的初始化是通过daemon.load()执行的
接着调用start方法进行启动,调用到了Catalina.start()方法
public void start() {
if (getServer() == null) {
load();
}
if (getServer() == null) {
log.fatal(sm.getString("catalina.noServer"));
return;
}
long t1 = System.nanoTime();
// Start the new server
try {
getServer().start();
} catch (LifecycleException e) {
log.fatal(sm.getString("catalina.serverStartFail"), e);
try {
getServer().destroy();
} catch (LifecycleException e1) {
log.debug("destroy() failed for failed Server ", e1);
}
return;
}
if (log.isInfoEnabled()) {
log.info(sm.getString("catalina.startup", Long.toString(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t1))));
}
if (generateCode) {
// Generate loader which will load all generated classes
generateLoader();
}
// 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();
}
}
上面的代码,逻辑非常简单,整段代码的核心就是getServer().start() 方法,调用Server对象的start方法来启动Tomcat
StandardServer
前面已经介绍了Catalina通过调用Server类的.init() 和start() 来启动Tomcat。Server是Tomcat配置文件的server.xml的顶层元素,这个时候就进入了Tomcat内部组件的详解,在此先抛开组件生命周期管理的相关细节。
对应的组件如下图红框
StandardServer整体类依赖结构如下
server.xml如下
这里需要了解的有四个部分
<?xml version="1.0" encoding="UTF-8"?>
<!--1.属性说明:port:指定端口,这个端口负责监听关闭tomcat的请求
shutdown:向以上端口发送的不关闭服务器的命令字符串
-->
<Server port="8005" shutdown="SHUTDOWN">
<!--2.Listener 相关 -->
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<!-- Prevent memory leaks due to use of particular java/javax APIs-->
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<!-- 3.GlobalNamingResources相关-->
<GlobalNamingResources>
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<!-- 4.service相关-->
<Service name="Catalina">
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
maxThreads="150" minSpareThreads="4"/>
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.authenticator.SingleSignOn" />
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
</Server>
对应的Server中接口
属性说明
/**
* @return the port number we listen to for shutdown commands.
*
* @see #getPortOffset()
* @see #getPortWithOffset()
*/
public int getPort();
/**
* Set the port number we listen to for shutdown commands.
*
* @param port The new port number
*
* @see #setPortOffset(int)
*/
public void setPort(int port);
/**
* Get the number that offsets the port used for shutdown commands.
* For example, if port is 8005, and portOffset is 1000,
* the server listens at 9005.
*
* @return the port offset
*/
public int getPortOffset();
/**
* Set the number that offsets the server port used for shutdown commands.
* For example, if port is 8005, and you set portOffset to 1000,
* connector listens at 9005.
*
* @param portOffset sets the port offset
*/
public void setPortOffset(int portOffset);
/**
* Get the actual port on which server is listening for the shutdown commands.
* If you do not set port offset, port is returned. If you set
* port offset, port offset + port is returned.
*
* @return the port with offset
*/
public int getPortWithOffset();
/**
* @return the address on which we listen to for shutdown commands.
*/
public String getAddress();
/**
* Set the address on which we listen to for shutdown commands.
*
* @param address The new address
*/
public void setAddress(String address);
/**
* @return the shutdown command string we are waiting for.
*/
public String getShutdown();
/**
* Set the shutdown command we are waiting for.
*
* @param shutdown The new shutdown command
*/
public void setShutdown(String shutdown);
/**
* Get the utility thread count.
* @return the thread count
*/
public int getUtilityThreads();
*/
public Object getNamingToken();
/**
* @return the utility executor managed by the Service.
*/
public ScheduledExecutorService getUtilityExecutor();
NamingResources
/**
* @return the global naming resources.
*/
public NamingResourcesImpl getGlobalNamingResources();
/**
* Set the global naming resources.
*
* @param globalNamingResources The new global naming resources
*/
public void setGlobalNamingResources
(NamingResourcesImpl globalNamingResources);
/**
* @return the global naming resources context.
*/
public javax.naming.Context getGlobalNamingContext();
Service相关
包括添加Service、查询Service、删除Service等
/**
* Add a new Service to the set of defined Services.
*
* @param service The Service to be added
*/
public void addService(Service service);
/**
* Wait until a proper shutdown command is received, then return.
*/
public void await();
/**
* Find the specified Service
*
* @param name Name of the Service to be returned
* @return the specified Service, or <code>null</code> if none exists.
*/
public Service findService(String name);
/**
* @return the set of Services defined within this Server.
*/
public Service[] findServices();
/**
* Remove the specified Service from the set associated from this
* Server.
*
* @param service The Service to be removed
*/
public void removeService(Service service);
StandardServer实现
详细的代码见org.apache.catalina.core.StandardService中的实现方法
线程池
// 此service中用于各种实用程序任务(包括重复执行的线程)的线程数
@Override
public int getUtilityThreads() {
return utilityThreads;
}
/**
* 获取内部进程数计算逻辑:
* > 0时,即utilityThreads的值。
* <=0时,Runtime.getRuntime().availableProcessors() + result...
*/
private static int getUtilityThreadsInternal(int utilityThreads) {
int result = utilityThreads;
if (result <= 0) {
result = Runtime.getRuntime().availableProcessors() + result;
if (result < 2) {
result = 2;
}
}
return result;
}
@Override
public void setUtilityThreads(int utilityThreads) {
// Use local copies to ensure thread safety
int oldUtilityThreads = this.utilityThreads;
if (getUtilityThreadsInternal(utilityThreads) < getUtilityThreadsInternal(oldUtilityThreads)) {
return;
}
this.utilityThreads = utilityThreads;
if (oldUtilityThreads != utilityThreads && utilityExecutor != null) {
reconfigureUtilityExecutor(getUtilityThreadsInternal(utilityThreads));
}
}
// 线程池
private synchronized void reconfigureUtilityExecutor(int threads) {
// The ScheduledThreadPoolExecutor doesn't use MaximumPoolSize, only CorePoolSize is available
if (utilityExecutor != null) {
utilityExecutor.setCorePoolSize(threads);
} else {
ScheduledThreadPoolExecutor scheduledThreadPoolExecutor =
new ScheduledThreadPoolExecutor(threads,
new TaskThreadFactory("Catalina-utility-", utilityThreadsAsDaemon, Thread.MIN_PRIORITY));
scheduledThreadPoolExecutor.setKeepAliveTime(10, TimeUnit.SECONDS);
scheduledThreadPoolExecutor.setRemoveOnCancelPolicy(true);
scheduledThreadPoolExecutor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
utilityExecutor = scheduledThreadPoolExecutor;
utilityExecutorWrapper = new org.apache.tomcat.util.threads.ScheduledThreadPoolExecutor(utilityExecutor);
}
}
/**
* Get if the utility threads are daemon threads.
* @return the threads daemon flag
*/
public boolean getUtilityThreadsAsDaemon() {
return utilityThreadsAsDaemon;
}
/**
* Set the utility threads daemon flag. The default value is true.
* @param utilityThreadsAsDaemon the new thread daemon flag
*/
public void setUtilityThreadsAsDaemon(boolean utilityThreadsAsDaemon) {
this.utilityThreadsAsDaemon = utilityThreadsAsDaemon;
}
Service相关方法实现
/**
* Add a new Service to the set of defined Services.
*
* @param service The Service to be added
*/
@Override
public void addService(Service service) {
service.setServer(this);
synchronized (servicesLock) {
Service results[] = new Service[services.length + 1];
System.arraycopy(services, 0, results, 0, services.length);
results[services.length] = service;
services = results;
if (getState().isAvailable()) {
try {
service.start();
} catch (LifecycleException e) {
// Ignore
}
}
// Report this property change to interested listeners
support.firePropertyChange("service", null, service);
}
}
@Override
public Service findService(String name) {
if (name == null) {
return null;
}
synchronized (servicesLock) {
for (Service service : services) {
if (name.equals(service.getName())) {
return service;
}
}
}
return null;
}
/**
* @return the set of Services defined within this Server.
*/
@Override
public Service[] findServices() {
return services;
}
/**
* @return the JMX service names.
*/
public ObjectName[] getServiceNames() {
ObjectName onames[]=new ObjectName[ services.length ];
for( int i=0; i<services.length; i++ ) {
onames[i]=((StandardService)services[i]).getObjectName();
}
return onames;
}
@Override
public void removeService(Service service) {
synchronized (servicesLock) {
int j = -1;
for (int i = 0; i < services.length; i++) {
if (service == services[i]) {
j = i;
break;
}
}
if (j < 0)
return;
try {
services[j].stop();
} catch (LifecycleException e) {
// Ignore
}
int k = 0;
Service results[] = new Service[services.length - 1];
for (int i = 0; i < services.length; i++) {
if (i != j)
results[k++] = services[i];
}
services = results;
// Report this property change to interested listeners
support.firePropertyChange("service", service, null);
}
}
StandardService实现
standardService中实现了Engine、Connectors、Executor、Lifecycle模版相关的代码
详细在org.apache.catalina.core.StandardService中,这里就不再赘述。