这篇随笔的重点关注启动Tomcat时会用到的两个类,分别是Catalina类 和 Bootstrap类,它们都位于org.apache.catalina.startup包下,Catalina类用于启动或关闭Server对象,并负责解析Tomcat文件:server.xml文件。Bootstrap类是一个入口点,负责创建Catalina实例,并调用其process()方法,理论上这两个类可以合并为一个,但是为了支持Tomcat的多种运行模式,而提供了多种启动类,例如,上面说到的Bootstrap类是作为一个独立的应用程序运行Tomcat的,而另一个类org.apahce.catalina.starup.BootstrapService可以使Tomcat作为一个Windows NT服务来运行。

  为了使用户使用方便,Tomcat附带了批处理文件 和 shell 脚本,可以方便的启动或者关闭servlet容器,在这些批处理 文件 和  shell 脚本的帮助下, 用户无须为了执行Bootstrap类 而记下 java.exe程序的选项,相反,用户只需要运行相应的批处理文件或者 shell脚本即可,

先介绍一下Catalina类

 Catalina类

  org.apache.catalina.startup.Catalina类是启动类,它包含了一个Digester对象,用于解析位于%CATALIN_HOME%conf目录下的server.xml文件。理解了添加到Digester对象中的规则之后,就可以自行配置Tomcat了。

  Catalina类还封装了一个Server对象,该对象有一个Service对象,正如之前学习的那样,Service对象包含一个Servlet容器 和 一个 或者多个连接器,可以使用Catalina类来启动或者关闭Server对象,

  可以通过实例化Catalina类,并调用其process方法来运行Tomcat,但是在调用该方法时,需要传入适当的参数,第一个参数是start 表示要启动Tomcat,或 stop 表示要向Tomcat发送一条关闭命令,还有其他的参数可选,包括-help、-config、-debug和-nothing

  注意:当使用nothing参数时,表示将不对JNDI 命名提供支持,更多关于Tomcat中 对JNDI命名的支持 看org.apahce.naming包中的类吧

  一般情况下,及时Catalina类提供了main方法作为程序的入口点,也需要使用Bootstrap类来实例化Catalina类,并调用其process方法,下面给出procee的实现

/**
* 实例主程序。
*
* @param args Command line arguments
*/
public void process(String args[]) { setCatalinaHome();
setCatalinaBase();
try {
if (arguments(args))
execute();
} catch (Exception e) {
e.printStackTrace(System.out);
}
}

process 方法设置了两个系统属性,分别是 catalina.home 和 catalina.base,默认值均与user.dir值相同,

注意:user.dir属性的值指明了用户的工作目录,即,会从哪个目录下调用java命令,更多系统属性的列表,可以参见java.lang.System类 getProperties方法的介绍

然后procee 方法会调用arguments方法,并传入参数列表,arguments方法处理命令行参数,如果Catalina对象能够继续处理的话,arguments方法会返回true,

/**
* 处理指定的命令行参数,如果应该继续处理,则返回true;否则返回false。
*
* @param args
* Command line arguments to process
*/
protected boolean arguments(String args[]) { boolean isConfig = false; if (args.length < 1) {
usage();
return (false);
} for (int i = 0; i < args.length; i++) {
if (isConfig) {
configFile = args[i];
isConfig = false;
} else if (args[i].equals("-config")) {
isConfig = true;
} else if (args[i].equals("-debug")) {
debug = true;
} else if (args[i].equals("-nonaming")) {
useNaming = false;
} else if (args[i].equals("-help")) {
usage();
return (false);
} else if (args[i].equals("start")) {
starting = true;
} else if (args[i].equals("stop")) {
stopping = true;
} else {
usage();
return (false);
}
} return (true); }

  process方法会检查arguments方法的返回值,如果返回值为true,则调用execute方法

 /**
* 执行 命令行配置后 处理。
*
*
*/
protected void execute() throws Exception { // 如果命令启动则启动
if (starting)
start();
// 如果命令停止则停止
else if (stopping)
stop(); }

execute方法会调用start方法 启动Tomcat 或者 调用stop方法来关闭Tomcat。

注意:在Tomcat 5 以及之后,没有execute方法,会在procee方法中调用 start方法 或者 stop方法

Start方法

  start方法 会创建一个Digester实例来解析server.xml文件(Tomcat 配置文件),在解析 server.xml文件之前,start方法会调用Digester对象的push方法,传入当前Catalin对象作为参数,这样Catalina对象就成为了 Digester对象的内部栈中的第一个对象,解析Server.xml文件之后,会使变量server引用一个Server对象,默认是org.apache.catalina.core.StandardServer类型的对象,然后start方法会调用Server对象的initialize方法 和 start方法,接着,Catalina对象的start方法会调用Server对象的await反方循环等待,直到接收到正确的关闭命令,当await方法返回时,Catalina对象的start方法会调用Server对象的stop方法,从而关闭Server对象和其他的组件,此外 start方法 还会使用关闭钩子,确保用户突然退出应用程序时会执行Server对象的stop方法。

 /**
* 启动一个新的Server实例
*/
protected void start() { // 创建一个解析XML文件的Digester
Digester digester = createStartDigester();
// 包含 服务器配置文件的File引用
File file = configFile();
try {
InputSource is = new InputSource("file://" + file.getAbsolutePath());
FileInputStream fis = new FileInputStream(file);
is.setByteStream(fis);
// 将该Catalina对象压入Digester对象的内部栈中,成为内部栈中的第一个元素
digester.push(this);
digester.parse(is);
fis.close();
} catch (Exception e) {
System.out.println("Catalina.start: " + e);
e.printStackTrace(System.out);
System.exit(1);
} // 设置附加变量
if (!useNaming) {
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);
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");
}
} // If a SecurityManager is being used, set properties for
// checkPackageAccess() and checkPackageDefinition
if (System.getSecurityManager() != null) {
String access = Security.getProperty("package.access");
if (access != null && access.length() > 0)
access += ",";
else
access = "sun.,";
Security.setProperty("package.access", access + "org.apache.catalina.,org.apache.jasper.");
String definition = Security.getProperty("package.definition");
if (definition != null && definition.length() > 0)
definition += ",";
else
definition = "sun.,";
Security.setProperty("package.definition",
// FIX ME package "javax." was removed to prevent HotSpot
// fatal internal errors
definition + "java.,org.apache.catalina.,org.apache.jasper.");
} // Replace System.out and System.err with a custom PrintStream
SystemLogHandler log = new SystemLogHandler(System.out);
System.setOut(log);
System.setErr(log); // 创建一个关闭钩子
Thread shutdownHook = new CatalinaShutdownHook(); // 初始化 Server对象
if (server instanceof Lifecycle) {
try {
server.initialize();
((Lifecycle) server).start();
try {
// 注册关闭钩子
Runtime.getRuntime().addShutdownHook(shutdownHook);
} catch (Throwable t) {
// This will fail on JDK 1.2. Ignoring, as Tomcat can run
// fine without the shutdown hook.
}
// 等待关闭命令 阻塞 直到 收到正确的关闭命令
server.await();
} catch (LifecycleException e) {
System.out.println("Catalina.start: " + e);
e.printStackTrace(System.out);
if (e.getThrowable() != null) {
System.out.println("----- Root Cause -----");
e.getThrowable().printStackTrace(System.out);
}
}
} // 关闭服务器组件
if (server instanceof Lifecycle) {
try {
try {
// 首先删除关闭钩子,以便server.stop() // 不会被调用两次
Runtime.getRuntime().removeShutdownHook(shutdownHook);
} catch (Throwable t) {
// This will fail on JDK 1.2. Ignoring, as Tomcat can run
// fine without the shutdown hook.
}
// 关闭服务器组件
((Lifecycle) server).stop();
} catch (LifecycleException e) {
System.out.println("Catalina.stop: " + e);
e.printStackTrace(System.out);
if (e.getThrowable() != null) {
System.out.println("----- Root Cause -----");
e.getThrowable().printStackTrace(System.out);
}
}
} }

stop方法

  stop方法用来关闭Catalina 和 Server对象

 /**
* 停止现有的服务器实例。 其实就是手动 向 服务器组件负责监听关闭命令的端口发送 关闭名命令
*/
protected void stop() { // 创建一个解析服务器组件配置XML文件的Digester
Digester digester = createStopDigester();
File file = configFile();
try {
InputSource is = new InputSource("file://" + file.getAbsolutePath());
FileInputStream fis = new FileInputStream(file);
is.setByteStream(fis);
digester.push(this);
digester.parse(is);
fis.close();
} catch (Exception e) {
System.out.println("Catalina.stop: " + e);
e.printStackTrace(System.out);
System.exit(1);
} // 向服务器组件 指定监听关闭命令端口发送关闭命令
try {
Socket socket = new Socket("127.0.0.1", server.getPort());
OutputStream stream = socket.getOutputStream();
String shutdown = server.getShutdown();
for (int i = 0; i < shutdown.length(); i++)
stream.write(shutdown.charAt(i));
stream.flush();
stream.close();
socket.close();
} catch (IOException e) {
System.out.println("Catalina.stop: " + e);
e.printStackTrace(System.out);
// 如果发送命令时异常 则直接退出 使用关闭钩子来关闭服务器组件
System.exit(1);
} }

启动Digester对象

  Catalina类的createStartDigester方法创建了一个Digester实例,然后为其添加规则,以解析server.xml文件,server.xml文件用来配置Tomcat,位于%CATALINA_HOME%/conf目录下,添加到Digester对象中的规则 是理解Tomcat配置的关键,

/**
* 创建和配置我们将用于启动的Digester
*/
protected Digester createStartDigester() { // 初始化Digester
Digester digester = new Digester();
if (debug)
digester.setDebug(999);
// 不校验XML的格式内容
digester.setValidating(false); // 配置我们将要使用的操作
// 遇到Server模式时,使用Server元素的className的值来创建Server实例
digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer", "className");
// 配置Server 各种属性
digester.addSetProperties("Server");
// 将Server实例 与当前Catalina对象关联上,利用setServer方法,将Server实例传入,Server对象类型为
// "org.apache.catalina.Server"
digester.addSetNext("Server", "setServer", "org.apache.catalina.Server"); // 遇到Server/GlobalNamingResources模式时,创建
// org.apache.catalina.deploy.NamingResources实例
digester.addObjectCreate("Server/GlobalNamingResources", "org.apache.catalina.deploy.NamingResources");
// 设置模式 Server/GlobalNamingResources 生成的对象各种属性
digester.addSetProperties("Server/GlobalNamingResources");
// 将其与Server对象关联起来,利用 Server对象的setGlobalNamingResources方法传入
// GlobalNamingResources类型实例
digester.addSetNext("Server/GlobalNamingResources", "setGlobalNamingResources",
"org.apache.catalina.deploy.NamingResources");
// xml规则到这里 如果存在上面 Server/GlobalNamingResources元素 到这里可定会遇到结束元素标签
// 会将该值从内部栈中移除 // 遇到 Server/Listener 必须制定 Listener实现类
digester.addObjectCreate("Server/Listener", null, // MUST be specified
// in the element
"className");
// 配置Listener对象属性
digester.addSetProperties("Server/Listener");
// 将Listener与Server对象关联起来通过 addLifecycleListener
digester.addSetNext("Server/Listener", "addLifecycleListener", "org.apache.catalina.LifecycleListener");
// 同样 在这里 如果存在 Listener标签 也会遇到结束标签 然后将 Listener对象移除从内部栈中 // 遇到Server/Service模式 创建 Service
digester.addObjectCreate("Server/Service", "org.apache.catalina.core.StandardService", "className");
digester.addSetProperties("Server/Service");
digester.addSetNext("Server/Service", "addService", "org.apache.catalina.Service");
// 真滴累 不想在写了 如果熟悉 Digester的人其实不用再写了 如果不熟悉的话 可以参考之前写的随笔Digester
digester.addObjectCreate("Server/Service/Listener", null, // MUST be
// specified
// in the
// element
"className");
digester.addSetProperties("Server/Service/Listener");
digester.addSetNext("Server/Service/Listener", "addLifecycleListener", "org.apache.catalina.LifecycleListener"); digester.addObjectCreate("Server/Service/Connector", "org.apache.catalina.connector.http.HttpConnector",
"className");
digester.addSetProperties("Server/Service/Connector");
digester.addSetNext("Server/Service/Connector", "addConnector", "org.apache.catalina.Connector"); digester.addObjectCreate("Server/Service/Connector/Factory",
"org.apache.catalina.net.DefaultServerSocketFactory", "className");
digester.addSetProperties("Server/Service/Connector/Factory");
digester.addSetNext("Server/Service/Connector/Factory", "setFactory",
"org.apache.catalina.net.ServerSocketFactory"); digester.addObjectCreate("Server/Service/Connector/Listener", null, // MUST
// be
// specified
// in
// the
// element
"className");
digester.addSetProperties("Server/Service/Connector/Listener");
digester.addSetNext("Server/Service/Connector/Listener", "addLifecycleListener",
"org.apache.catalina.LifecycleListener"); // 为嵌套元素添加规则集
digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));
digester.addRuleSet(new EngineRuleSet("Server/Service/"));
digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Default"));
digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/DefaultContext/"));
digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/Default"));
digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/DefaultContext/"));
digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));
digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/")); digester.addRule("Server/Service/Engine", new SetParentClassLoaderRule(digester, parentClassLoader)); return (digester); }

  上述方法,会创建一个org.apache.commons.digester.Digester类的一个实例,并且为其添加规则。

  前三条规则用于解析server.xml文件的Server元素,可能你已经知道了Server元素时Server.xml文件的根元素,下面是为Server模式添加的规则;

 // 配置我们将要使用的操作
// 遇到Server模式时,使用Server元素的className的值来创建Server实例
digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer", "className");
// 配置Server 各种属性
digester.addSetProperties("Server");
// 将Server实例 与当前Catalina对象关联上,利用setServer方法,将Server实例传入,Server对象类型为
// "org.apache.catalina.Server"
digester.addSetNext("Server", "setServer", "org.apache.catalina.Server");

  第一条规则:在遇到Server元素时,Digester对象要创建 org.apahce.catalina.core.StandardServer类的一个实例,例外是,如果Server元素有一个名为 className的属性,那么className属性的值就是必须实例化类的名称。

  第二条规则:指明要对Server对象的指定的属性名设置同名的java属性值,

  第三条规则将:Server对象压入到Digester对象内部栈中,并与栈中的下一个对象相关联,就是上一个被添加的Catalina对象,调用其setServer方法与Server对象相关联,那Catalina实例是如何放到Digester对象的内部栈中的呢,在Start方法的开始部分,在解析xml文件之前会调用Digester对象的push方法将Catalina对象压入栈

// 将该Catalina对象压入Digester对象的内部栈中,成为内部栈中的第一个元素
digester.push(this);

现在你应该可以理解 后面规则的意思了 如果你还理解不了的话,看下前面的Digester随笔的内容。

关闭Digester对象

  createStopDigester方法返回一个Digester对象来关闭Server对象

     /**
* 创建和配置我们将用于关闭的Digester。 因为我们只是为了获取到Server对象的 元素配置属性,目前用到的是 监听关闭命令的端口 以及
* 关闭命令,所以只要解析Server元素 标签就可以了
*
*/
protected Digester createStopDigester() { // Initialize the digester
Digester digester = new Digester();
if (debug)
digester.setDebug(999); // 配置关闭所需的规则
digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer", "className");
digester.addSetProperties("Server");
digester.addSetNext("Server", "setServer", "org.apache.catalina.Server"); return (digester); }

  与启动Digester对象不同,关闭Digester对象只是对XML文档的根元素感兴趣 因为只是为了为了关闭时用到的Server的 监听关闭命令端口 以及 关闭命令

Bootstrap类

  有一些类提供了启动Tomcat的入口点,其中之一是org.apache.startup.Bootstrap类。当运行startup.bat文件或者startup.sh文件时,实际上就是调用了该类的main方法。main方法会创建3个类载入器,并实例化Catalina类,然后它调用Catalina的process方法。

 package org.apache.catalina.startup;

 import java.io.File;
import java.lang.reflect.Method; /**
*
* <p>
* <b>Title:Bootstrap.java</b>
* </p>
* <p>
* Copyright:ChenDong 2018
* </p>
* <p>
* Company:仅学习时使用
* </p>
* <p>
* 类功能描述:Catalina 的Boostrap装载类。这个应用程序构造了一个类加载器,用于加载Catalina内部类(通过
* “catalina.home”下的
* “server”目录中找到的所有JAR文件),并开始容器的常规执行。这种迂回方法的目的是将Catalina内部类(以及它们依赖的任何其他类,
* 例如XML解析器)排除在系统类路径之外,因此应用程序级类不可见。
* </p>
*
* @author 陈东
* @date 2018年12月25日 下午9:42:57
* @version 1.0
*/
public final class Bootstrap { // ------------------------------------------------------- Static Variables /**
* 调试用于处理启动的细节级别。
*
*
*/
private static int debug = 0; // ----------------------------------------------------------- Main Program /**
* 引导程序的主程序。
*
* @param args
* Command line arguments to be processed
*/
public static void main(String args[]) { // Set the debug flag appropriately
for (int i = 0; i < args.length; i++) {
if ("-debug".equals(args[i]))
debug = 1;
} // 如果尚未设置,则从catalina.home配置catalina.base
if (System.getProperty("catalina.base") == null)
System.setProperty("catalina.base", getCatalinaHome());
/**
* 使用多个类载入器的母的是为了防止应用程序中类(包括 servlet类 和 Web应用程序中的其他辅助类)
* 使用WEB-INF/classes目录 和 WEB-INF/lib目录之外的类。
* 部署到%CATALINA_HOME%/common/lib目录下的jar文件的类文件是可用的
*/ // 构造我们需要的类装入器 ClassLoader commonLoader = null;
ClassLoader catalinaLoader = null;
ClassLoader sharedLoader = null;
/**
* 对于每个类载入器都会制定一条可以访问的路径,
*/
try { File unpacked[] = new File[1];
File packed[] = new File[1];
File packed2[] = new File[2];
ClassLoaderFactory.setDebug(debug); unpacked[0] = new File(getCatalinaHome(), "common" + File.separator + "classes");
packed2[0] = new File(getCatalinaHome(), "common" + File.separator + "endorsed");
packed2[1] = new File(getCatalinaHome(), "common" + File.separator + "lib");
/**
* commonLoader 类载入器,载入 <code>%CATALINA_HOME%/common/lib</code>、
* <code>%CATALINA_HOME%/common/classes</code>、
* <code>%CATALINA_HOME%/common/endorsed</code>目录下的java类
*/
commonLoader = ClassLoaderFactory.createClassLoader(unpacked, packed2, null); unpacked[0] = new File(getCatalinaHome(), "server" + File.separator + "classes");
packed[0] = new File(getCatalinaHome(), "server" + File.separator + "lib");
/**
* catalinaLoaderb 类载入 负责 载入 运行 Catalina Servlet容器 所需要类,它可以载入
* <code>%CATALINA_HOME%/server/classes</code>、
* <code>%CATALINA_HOME%/server/lib</code> 以及 commonLoader
* 类载入器可以载入的类
*/
catalinaLoader = ClassLoaderFactory.createClassLoader(unpacked, packed, commonLoader); unpacked[0] = new File(getCatalinaBase(), "shared" + File.separator + "classes");
packed[0] = new File(getCatalinaBase(), "shared" + File.separator + "lib");
/**
* sharedLoader 类载入器可以载入
* <code>%CATALINA_HOME%/shared/classes</code>、
* <code>%CATALINA_HOME%/shared/lib</code> 以及 commonLoader
* 类载入器可以载入的类。
*
* 在Tomcat 中,每个Web应用程序中与Context 容器相关联的每个类载入器的 父类载入器都是 sharedLoader
* 类载入器
*
* sharedLoader 类载入器 并不能访问Catalina的内部类,或CLASSPATH环境变量指定的类路径中的类
*/
sharedLoader = ClassLoaderFactory.createClassLoader(unpacked, packed, commonLoader);
} catch (Throwable t) { log("Class loader creation threw exception", t);
System.exit(1); } Thread.currentThread().setContextClassLoader(catalinaLoader); // 加载启动类并调用它的process()方法
try { SecurityClassLoad.securityClassLoad(catalinaLoader); // 实例化一个启动类Catalina类
if (debug >= 1)
log("Loading startup class");
Class startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.newInstance(); // 设置共享扩展类加载器
if (debug >= 1)
log("Setting startup class properties");
// 调用Catalina的setParentClassLoader方法
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); // 调用Catalina的process
if (debug >= 1)
log("Calling startup class process() method");
methodName = "process";
paramTypes = new Class[1];
paramTypes[0] = args.getClass();
paramValues = new Object[1];
paramValues[0] = args;
method = startupInstance.getClass().getMethod(methodName, paramTypes);
// 将参数args 传入Catalina对象的 process方法中
method.invoke(startupInstance, paramValues); } catch (Exception e) {
System.out.println("Exception during startup processing");
e.printStackTrace(System.out);
System.exit(2);
} } /**
* 获取catalina.home环境变量的值。 如果之前没有设置过{@code catalina.home}的值,就会返回 user.dir属性的值
*/
private static String getCatalinaHome() {
return System.getProperty("catalina.home", System.getProperty("user.dir"));
} /**
* 获取catalina.base环境变量的值。
* 如果属性{@code catalina.base }的属性值为空,则返回{@code catalina.home}的值
*/
private static String getCatalinaBase() {
return System.getProperty("catalina.base", getCatalinaHome());
} /**
* 记录调试详细信息。
*
*
*
* @param message
* 要被记录的信息
*/
private static void log(String message) { System.out.print("Bootstrap: ");
System.out.println(message); } /**
* 记录异常的调试详细信息。
*
*
*
* @param message
* 要被记录的信息
* @param exception
* 记录时遇到异常
*/
private static void log(String message, Throwable exception) { log(message);
exception.printStackTrace(System.out); } }

  Bootstrap 类 有四个静态方法, 分别是两个log方法、getCatalinaHome方法 和 getCatalinaBase()方法,getCatalinaHome方法的额实现如下

/**
* 获取catalina.home环境变量的值。 如果之前没有设置过{@code catalina.home}的值,就会返回 user.dir属性的值
*/
private static String getCatalinaHome() {
return System.getProperty("catalina.home", System.getProperty("user.dir"));
}

  getCatalinaBase方法实现如下

/**
* 获取catalina.base环境变量的值。
* 如果属性{@code catalina.base }的属性值为空,则返回{@code catalina.home}的值
*/
private static String getCatalinaBase() {
return System.getProperty("catalina.base", getCatalinaHome());
}

此外  main方法 还会为不同目的而创建三个类载入器

    /**
* 使用多个类载入器的母的是为了防止应用程序中类(包括 servlet类 和 Web应用程序中的其他辅助类)
* 使用WEB-INF/classes目录 和 WEB-INF/lib目录之外的类。
* 部署到%CATALINA_HOME%/common/lib目录下的jar文件的类文件是可用的
*/ // 构造我们需要的类装入器 ClassLoader commonLoader = null;
ClassLoader catalinaLoader = null;
ClassLoader sharedLoader = null;
/**
* 对于每个类载入器都会制定一条可以访问的路径,
*/
try { File unpacked[] = new File[1];
File packed[] = new File[1];
File packed2[] = new File[2];
ClassLoaderFactory.setDebug(debug); unpacked[0] = new File(getCatalinaHome(), "common" + File.separator + "classes");
packed2[0] = new File(getCatalinaHome(), "common" + File.separator + "endorsed");
packed2[1] = new File(getCatalinaHome(), "common" + File.separator + "lib");
/**
* commonLoader 类载入器,载入 <code>%CATALINA_HOME%/common/lib</code>、
* <code>%CATALINA_HOME%/common/classes</code>、
* <code>%CATALINA_HOME%/common/endorsed</code>目录下的java类
*/
commonLoader = ClassLoaderFactory.createClassLoader(unpacked, packed2, null); unpacked[0] = new File(getCatalinaHome(), "server" + File.separator + "classes");
packed[0] = new File(getCatalinaHome(), "server" + File.separator + "lib");
/**
* catalinaLoaderb 类载入 负责 载入 运行 Catalina Servlet容器 所需要类,它可以载入
* <code>%CATALINA_HOME%/server/classes</code>、
* <code>%CATALINA_HOME%/server/lib</code> 以及 commonLoader
* 类载入器可以载入的类
*/
catalinaLoader = ClassLoaderFactory.createClassLoader(unpacked, packed, commonLoader); unpacked[0] = new File(getCatalinaBase(), "shared" + File.separator + "classes");
packed[0] = new File(getCatalinaBase(), "shared" + File.separator + "lib");
/**
* sharedLoader 类载入器可以载入
* <code>%CATALINA_HOME%/shared/classes</code>、
* <code>%CATALINA_HOME%/shared/lib</code> 以及 commonLoader
* 类载入器可以载入的类。
*
* 在Tomcat 中,每个Web应用程序中与Context 容器相关联的每个类载入器的 父类载入器都是 sharedLoader
* 类载入器
*
* sharedLoader 类载入器 并不能访问Catalina的内部类,或CLASSPATH环境变量指定的类路径中的类
*/
sharedLoader = ClassLoaderFactory.createClassLoader(unpacked, packed, commonLoader);
} catch (Throwable t) { log("Class loader creation threw exception", t);
System.exit(1); } Thread.currentThread().setContextClassLoader(catalinaLoader);

在创建了三个类载入器之后,main方法会载入Catalina类并创建它的一个实例,然后再将其赋值给 startupInstance变量

Class startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.newInstance();

然后它调用setParentClassLoader方法,并将sharedLoader类载入器作为参数传入

 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);

最后main方法会调用Catalina对象的process方法

 // 调用Catalina的process
if (debug >= 1)
log("Calling startup class process() method");
methodName = "process";
paramTypes = new Class[1];
paramTypes[0] = args.getClass();
paramValues = new Object[1];
paramValues[0] = args;
method = startupInstance.getClass().getMethod(methodName, paramTypes);
// 将参数args 传入Catalina对象的 process方法中
method.invoke(startupInstance, paramValues);

启动Tomcat的更多相关文章

  1. eclipse启动tomcat无法访问

    eclipse启动tomcat无法访问 症状: tomcat在eclipse里面能正常启动,而在浏览器中访问http://localhost:8080/不能访问,且报404错误.同时其他项目页面也不能 ...

  2. Error configuring application listener of class。。。NoClassDefFoundError。。某Listener 之启动tomcat报错

    当你启动tomcat的时候如果报类似下面的错误: WARNING: [SetContextPropertiesRule]{Context} Setting property 'source' to ' ...

  3. eclipse启动tomcat, http://localhost:8080无法访问

    原地址 症状: tomcat在eclipse里面能正常启动,而在浏览器中访问http://localhost:8080/不能访问,且报404错误.同时其他项目页面也不能访问. 关闭eclipse里面的 ...

  4. Eclipse启动Tomcat时发生java.lang.IllegalArgumentException: <session-config> element is limited to 1 occurrence

    在学习struts 2时,为了方便,直接从下载的struts的apps目录下的struts2-blank.war压缩包下的WEB-INF\复制的web.xml,当我启动Tomcat时,发生 java. ...

  5. Mac下没有权限启动tomcat的解决办法

    问题描述 在Mac中通过./startup.sh执行启动脚本文件,启动tomcat时报如下错误: -bash: ./startup.sh: Permission denied 解决方法 错误信息说明了 ...

  6. maven 项目启动tomcat报错 java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderListener

    maven项目启动tomcat报错: java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderLi ...

  7. 直接启动tomcat时为tomcat指定JDK

    第一种: 在windows环境下以批处理文件方式启动tomcat,只要运行<CATALINA_HOME>/bin/startup.bat这个文件,就可以启动Tomcat.在启动时,star ...

  8. [转]Eclipse启动Tomcat时45秒超时解决方法

    原文地址:http://it.oyksoft.com/post/6577/ Eclipse启动Tomcat时,默认配置的启动超时时长为45秒.假若项目启动超过45秒将会报错.两种解决方法:1.改XML ...

  9. 启动Tomcat报异常host-manager does not exist or is not a readable directory

    前几天重新安装了Tomcat6,安装完Tomcat6后在wepapps下面会有一些tomcat自带的项目(root.manager.host- manager等几个),这几天项目没什么用我就删掉了,后 ...

  10. 启动Tomcat服务器报错

    启动Tomcat服务器报错: Several ports (8005, 8080, 8009) required by Tomcat v5.5 Server at localhost are alre ...

随机推荐

  1. Leetcode题目121.买卖股票的最佳时机(简单)

    题目描述: 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格. 如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润. 注意你不能在买入股票前卖出 ...

  2. Flutter常见数据类型及数据类型转换

    简介 既然 Dart 是一门语言,那么就和我们所知道语言一样,有基本的数据类型以及常见的流程处理语法,那么我们就来了解下. Dart 的所有东西都是对象,包括数字,函数等.它们都继承自 Object ...

  3. data binding 优缺点

    文章: 1. [译文] 我不使用Android Data Binding的四个理由 https://www.jianshu.com/p/559adeaaeffd 2. 原文:https://blog. ...

  4. 2.3 Go语言基础之数组

    本文主要介绍Go语言中数组(array)及它的基本使用. 一.Array(数组) 数组是同一种数据类型元素的集合. 在Go语言中,数组从声明时就确定,使用时可以修改数组成员,但是数组大小不可变化. 基 ...

  5. 配置 admin 页面

    创建 blog 的管理后台 首先是 blog 这个 App,其中定义了 3个 Model,分别是 Category.Post 和 Tag.先创建 admin 页面,其代码需要写到 blog/admin ...

  6. 链接Linux工具(SecureCRT)

    SecureCRT下载 点我下载 http://download.csdn.net/download/weixin_39549656/10207279 安装 先运行注册机 链接 输入密码 出现以下界面 ...

  7. DPDK 网络加速在 NFV 中的应用

    目录 文章目录 目录 前文列表 传统内核协议栈的数据转发性能瓶颈是什么? DPDK DPDK 基本技术 DPDK 架构 DPDK 核心组件 应用 NUMA 亲和性技术减少跨 NUMA 内存访问 应用 ...

  8. VUE中v-for和v-if不能同时使用的问题

    摘抄自:https://www.cnblogs.com/showjs/p/11376446.html 在开发时候碰到了一个问题:v-if和v-for不能同时使用: <h-tab-pane v-f ...

  9. kettle在windows下面部署定时任务

    KETTLE有三大块: Spoon:转换/工作(transform/job)设计工具,主要是GUI方式. Kitchen:工作(job)执行器,是一个作业执行引擎,通过命令行的方式带参数执行,参数说明 ...

  10. MVC自定义视图

    编写自定义模板,以单选按钮为例   1.在Shared新建模板视图(文件夹名必须为EditorTemplates)       2.编写模板代码   @model bool   <table&g ...