启动Tomcat
这篇随笔的重点关注启动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的更多相关文章
- eclipse启动tomcat无法访问
eclipse启动tomcat无法访问 症状: tomcat在eclipse里面能正常启动,而在浏览器中访问http://localhost:8080/不能访问,且报404错误.同时其他项目页面也不能 ...
- Error configuring application listener of class。。。NoClassDefFoundError。。某Listener 之启动tomcat报错
当你启动tomcat的时候如果报类似下面的错误: WARNING: [SetContextPropertiesRule]{Context} Setting property 'source' to ' ...
- eclipse启动tomcat, http://localhost:8080无法访问
原地址 症状: tomcat在eclipse里面能正常启动,而在浏览器中访问http://localhost:8080/不能访问,且报404错误.同时其他项目页面也不能访问. 关闭eclipse里面的 ...
- 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. ...
- Mac下没有权限启动tomcat的解决办法
问题描述 在Mac中通过./startup.sh执行启动脚本文件,启动tomcat时报如下错误: -bash: ./startup.sh: Permission denied 解决方法 错误信息说明了 ...
- maven 项目启动tomcat报错 java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderListener
maven项目启动tomcat报错: java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderLi ...
- 直接启动tomcat时为tomcat指定JDK
第一种: 在windows环境下以批处理文件方式启动tomcat,只要运行<CATALINA_HOME>/bin/startup.bat这个文件,就可以启动Tomcat.在启动时,star ...
- [转]Eclipse启动Tomcat时45秒超时解决方法
原文地址:http://it.oyksoft.com/post/6577/ Eclipse启动Tomcat时,默认配置的启动超时时长为45秒.假若项目启动超过45秒将会报错.两种解决方法:1.改XML ...
- 启动Tomcat报异常host-manager does not exist or is not a readable directory
前几天重新安装了Tomcat6,安装完Tomcat6后在wepapps下面会有一些tomcat自带的项目(root.manager.host- manager等几个),这几天项目没什么用我就删掉了,后 ...
- 启动Tomcat服务器报错
启动Tomcat服务器报错: Several ports (8005, 8080, 8009) required by Tomcat v5.5 Server at localhost are alre ...
随机推荐
- ANDROID_ID
在设备首次启动时,系统会随机生成一个64位的数字,并把这个数字以16进制字符串的形式保存下来,这个16进制的字符串就是ANDROID_ID,当设备被wipe后该值会被重置.可以通过下面的方法获取: i ...
- 【零基础】简单说说一键果体APP的AI
参考: https://www.jianshu.com/p/8c7a7cb7198c https://blog.csdn.net/gdymind/article/details/82696481 零. ...
- HTTP之缓存技术
1. 缓存简介 缓存是位于服务器和客户端的中间单元,主要根据用户代理发送过来的请求,向服务器请求相关内容后提供给用户,并保存内容副本,例如 HTML 页面.图片.文本文件或者流媒体文件.然后,当下一个 ...
- ffmpeg编码h264设置规格
ffmpeg -i demo.ts -profile:v baseline -vcodec h264 -acodec aac -f flv demo.flv
- [java]取当前平台默认字符集,取字符串长度
public class TimestampLength { public static void main(String[] args) { System.out.println(java.nio. ...
- Android跨进程通信Content Provider
Content Provider ContentProvider在android中的作用是对外共享数据,也就是说你可以通过ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通 ...
- 记录一个微信网页授权中不小心踩到的坑(Curl请求返回false)
原文章地址在这里 这个问题是file_get_contents不能获取https的内容引起的.这样的情况下,我们一般会采用curl拓展来模拟请求. 代码demo(当然这是错误的示范): functio ...
- redis数据类型及订阅操作
Redis数据类型详解 Redis键/值介绍 Redis key值是二进制安全的,这意味着可以用任何二进制序列作为key值,从形如“foo”的简单字符串到一个JPG文件的内容都可以.空字符串也是有效k ...
- java.io.IOException: Cleartext HTTP traffic to xxx.xxx.xxx.xxx not permitted
java.io.IOException: Cleartext HTTP traffic to xxx.xxx.xxx.xxx not permitted 转 https://blog.csdn.net ...
- consul ocelot
consul配置完成后 新建.netcoreapi项目, nuget安装ocelot 添加多个配置文件,.netcore中会自动合并为一个文件,global配置总的配置,其他为各个项目的配置 Serv ...