1.入口类及main方法
tomcat的入口类是org.apache.catalina.startup.BootStrap,进入其main方法,可以看到它首先实例化,然后进行初始化。
- if (daemon == null) {
-
daemon = new Bootstrap();
-
try {
-
- daemon.init();
-
} catch (Throwable t) {
- t.printStackTrace();
-
return;
- }
if (daemon == null) { daemon = new Bootstrap(); try { //因为在启动时设置了catalina.home属性,所以System.getProperty("catalina.home")可以直接获取到这个属性值. daemon.init(); } catch (Throwable t) { t.printStackTrace(); return; } }
2.init方法
init方法首先设置catalina的家目录,这里调用了两个方法
setCatalinaHome();
setCatalinaBase();
- private void setCatalinaHome() {
-
-
if (System.getProperty("catalina.home") != null)
-
return;
- File bootstrapJar =
-
new File(System.getProperty("user.dir"), "bootstrap.jar");
-
if (bootstrapJar.exists()) {
-
try {
- System.setProperty
-
("catalina.home",
-
(new File(System.getProperty("user.dir"), ".."))
- .getCanonicalPath());
-
} catch (Exception e) {
-
-
System.setProperty("catalina.home",
-
System.getProperty("user.dir"));
- }
-
} else {
-
System.getProperty("catalina.config");
- }
-
- }
private void setCatalinaHome() { if (System.getProperty("catalina.home") != null) return; File bootstrapJar = new File(System.getProperty("user.dir"), "bootstrap.jar"); if (bootstrapJar.exists()) { try { System.setProperty ("catalina.home", (new File(System.getProperty("user.dir"), "..")) .getCanonicalPath()); } catch (Exception e) { // Ignore System.setProperty("catalina.home", System.getProperty("user.dir")); } } else { System.getProperty("catalina.config"); } }
在前一篇文件中我讲到在启动tomcat时,会添加一个参数-Dcatalina.home="**************************",这个的意思是说为系统添加一个属性,相当于System.setProperty("catalina.home","**************************"),可见此方法在执行到第一句时无意外的话就会返回了,因为catalina.home己经有值了。对于另一个方法,也很明确
- private void setCatalinaBase() {
-
-
if (System.getProperty("catalina.base") != null)
-
return;
-
if (System.getProperty("catalina.home") != null)
-
System.setProperty("catalina.base",
-
System.getProperty("catalina.home"));
-
else
-
System.setProperty("catalina.base",
-
System.getProperty("user.dir"));
-
- }
private void setCatalinaBase() { if (System.getProperty("catalina.base") != null) return; if (System.getProperty("catalina.home") != null) System.setProperty("catalina.base", System.getProperty("catalina.home")); else System.setProperty("catalina.base", System.getProperty("user.dir")); }
如果catalina.home有值的话就把它给catalina.base
设置好家目录后,就开始初始化类加载器,这个时候前面设置的家目录就开始起作用了。
这个方法会被调用,initClassLoaders()它本身没什么,它又接着调用createClassLoader()方法,在这个方法里会去读conf/catalina.properties里面的内容
- String value = CatalinaProperties.getProperty(name + ".loader");
String value = CatalinaProperties.getProperty(name + ".loader");
这里的CatalinaProperties会首先执行一个static方法loadProperties(),用于加载conf/catalina.properties里面的内容,
先得到catalina.properties的输入流,然后加载,最后将所有的属性添加到系统属性中
- File home = new File(getCatalinaBase());
-
File conf = new File(home, "conf");
-
File properties = new File(conf, "catalina.properties");
-
is = new FileInputStream(properties);
-
-
-
properties = new Properties();
- properties.load(is);
- is.close();
File home = new File(getCatalinaBase()); File conf = new File(home, "conf"); File properties = new File(conf, "catalina.properties"); is = new FileInputStream(properties); properties = new Properties(); properties.load(is); is.close();
- Enumeration enumeration = properties.propertyNames();
-
while (enumeration.hasMoreElements()) {
- String name = (String) enumeration.nextElement();
- String value = properties.getProperty(name);
-
if (value != null) {
-
System.setProperty(name, value);
- }
- }
Enumeration enumeration = properties.propertyNames(); while (enumeration.hasMoreElements()) { String name = (String) enumeration.nextElement(); String value = properties.getProperty(name); if (value != null) { System.setProperty(name, value);//一个一个添加到系统属性当中 } }
接下来通过反射机制得到Catalina类的实例:
- Class startupClass =
- catalinaLoader.loadClass
-
("org.apache.catalina.startup.Catalina");
- Object startupInstance = startupClass.newInstance();
Class startupClass = catalinaLoader.loadClass ("org.apache.catalina.startup.Catalina"); Object startupInstance = startupClass.newInstance();
3.开启执行启动方法
找到main方法中的
- daemon.setAwait(true);
- daemon.load(args);
- daemon.start();
daemon.setAwait(true); daemon.load(args); daemon.start();
这几个方法内部都是通过反射机制调用Catalina的相关方法实现,这里我们主要看start方法,
- public void start()
-
throws Exception {
-
if( catalinaDaemon==null ) init();
-
-
Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);
-
method.invoke(catalinaDaemon, (Object [])null);
-
- }
public void start() throws Exception { if( catalinaDaemon==null ) init(); //这里的catalinaDaemon是一个Catalina类的实例,在init方法中可以看出来。 Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null); method.invoke(catalinaDaemon, (Object [])null); }
可以看出此方法最后调用了Catalina实例的start方法。
进入start方法,可以看到它首先会执行load方法,load方法首先会去获取conf/server.xml配置文件的路径,
- file = configFile();
-
inputStream = new FileInputStream(file);
-
inputSource = new InputSource("file://" + file.getAbsolutePath());
-
-
- inputSource.setByteStream(inputStream);
-
digester.push(this);
- digester.parse(inputSource);
- inputStream.close();
-
file = configFile(); inputStream = new FileInputStream(file); inputSource = new InputSource("file://" + file.getAbsolutePath()); //上面得到路径 inputSource.setByteStream(inputStream); digester.push(this); digester.parse(inputSource); inputStream.close(); //下面负责解析server.xml并同时实例化Server
实例化是通过Digester类完成,它可以实现xml与javaBean之间的转化,想了解的可以参考这里
http://software.ccidnet.com/art/322/20021125/31671_2.html
最后调用server的start方法启动tomcat
- if (server instanceof Lifecycle) {
-
try {
- ((Lifecycle) server).start();
-
} catch (LifecycleException e) {
-
log.error("Catalina.start: ", e);
- }
- }
总结:
1.反射的大量使用
无论是classloader,还是具体的某个类,都是通过反射实现初始化和方法调用的。思路是:配置文件->系统属性->解析属性字符串->得到类路径->反射,得到类->方法调用
这样可以使服务器的实现类很容易被替换,降低耦合性,减少将来代码的改动,的确是一种优秀的设计模式思想
启动类的配置文件比较简单,都是简单的“键-值”,所以直接用了StringToken解析字符串。后面的代码牵涉到xml配置文件时,必定会换成xml解析器。但总体思路应该类似
2.JMX的使用
Bootstrap的JMX用得很少,仅仅对类装载器进行mbean注册,相信后面会有更多JMX的身影
org.apache.catalina.startup.ClassLoaderFactory
顾名思义,是tomcat专门用来产生classloader的工厂类
包括三个重载方法createClassLoader,内容都大同小异,无非是根据传入的类路径参数,产生一个相应的类装载器
tomcat的类路径分为:包含class文件的目录、jar文件、包含jar的目录和URL
以Bootstrap类用到的createClassLoader方法为例
参数为:(String locations[],Integer types[], ClassLoader parent)
locations即为类路径的字符串表示,types为对应的类路径的类型,这两个数组的长度肯定是相同的,parant即为父classloader
方法的处理逻辑很简单,即根据每个location的类型,对location字符串做处理,如取得绝对路径、遍历目录、判断是不是jar文件等等
从代码可以看到,ClassLoaderFactory产生的classloader其实都是org.apache.catalina.loader.StandardClassLoader,这个类装载器留待以后研究
org.apache.catalina.startup.Catalina
Bootstrap中,是通过start方法启动Catalina的
Catalina中包含一个server实例,对应server.xml中的server元素。如果server实现了Lifecycle,start首先将server实例作为生命周期对象进行启动。然后,在JDK的Runtime中注册一个Shutdownhook。最后,如果指定了await参数,则让Catalina停止
start方法代码如下:
public void start() {
if (server == null) {
load();
}
long t1 = System.nanoTime();
// Start the new server
if (server instanceof Lifecycle) {
try {
((Lifecycle) server).start();
} catch (LifecycleException e) {
log.error("Catalina.start: ", e);
}
}
long t2 = System.nanoTime();
if(log.isInfoEnabled())
log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
try {
// Register shutdown hook
if (useShutdownHook) {
if (shutdownHook == null) {
shutdownHook = new CatalinaShutdownHook();
}
Runtime.getRuntime().addShutdownHook(shutdownHook);
}
} catch (Throwable t) {
// This will fail on JDK 1.2. Ignoring, as Tomcat can run
// fine without the shutdown hook.
}
if (await) {
await();
stop();
}
}
Shutdownhook是一个Thread对象,用于给JVM在收到停止命令时(例如Ctrl C),运行这些Thread执行收尾工作,具体用法可以参考JAVA文档
CatalinaShutdownHook在这里就是执行Catalina.stop方法,所以在Catalina.stop方法里面,会手动删除Runtime中的CatalinaShutdownHook,以防止调用两次stop,如下:
// Remove the ShutdownHook first so that server.stop()
// doesn't get invoked twice
if (useShutdownHook) {
Runtime.getRuntime().removeShutdownHook(shutdownHook);
}
Catalina也有个main函数,不过里头调用的方法都是已废弃的,估计是遗留代码
main函数主要是对命令行参数进行解析,比如可以取代默认的server.xml路径,其余工作主要交给load和start方法
load方法
按以下顺序读取配置文件server.xml,通过Digester类进行解析
1.configFile字段指明的配置文件“conf/server.xml”
2.Catalina的类装载器的getResource方法(具体原理不清楚,似乎很神奇。和装载类的机制一样,propagate方式,父装载器优先)
3.最后是从内置的catalina.jar的server-embed.xml读入配置
一旦读取成功,则后面的不读取,优先级依次降低
load方法最后进行配置和server的初始化,调用server.initialize方法
其中,Digester类似乎很神奇很强大,可以根据一定的规则,对一个xml源文件进行解析,然后执行相应的动作,留待以后研究。这里有一篇文章可以参考
http://software.ccidnet.com/pub/article/c322_a31671_p2.html
stop方法
// Shut down the server
if (server instanceof Lifecycle) {
try {
((Lifecycle) server).stop();
} catch (LifecycleException e) {
log.error("Catalina.stop", e);
}
}
stopserver方法
比起stop方法要复杂得多,不是简单地调用server.stop,而是通过创建一个Digester,根据配置文件的定义进行动作;然后,以非常“原始”的方法:建立到server的socket连接,以字节流的方式,一个个字符地发送tomcat的“SHUTDOWN”指令,如下:
// Stop the existing server
try {
String hostAddress = InetAddress.getByName("localhost").getHostAddress();
Socket socket = new Socket(hostAddress, 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) {
log.error("Catalina.stop: ", e);
System.exit(1);
}
补充
在看完StandardServer后,回到Catalina,现在对于Catalina的stop和stopServer方法的区别又多了一些理解。
关于StandardServer的笔记:
Catalina的stop方法,其实是调用StandardServer.stop,进而停止StandardServer内部的各个Service,是名副其实的真正“停止”服务器
而stopServer方法,其实是构造一个socket连接到StandardServer的await方法打开的端口上,并发送SHUTDOWN命令。如前面所述,await只是起到阻塞作用,接受到SHUTDOWN命令后则await结束阻塞,并返回。具体要做哪些操作,还得由调用await的代码来决定。
现在先姑且认为,Catalina的stop方法是单纯的、Catalinal自己定义的一个停止服务器的函数,里面的实现都是封装在Catalina内部的,完成的工作就是停止服务器的socket和相关的Service服务,外人无法改动。
假如有第三方希望在停止server之前做点别的事情(比如释放特定的资源、或者通知其他组件),那么可以利用stopServer方法,待到await返回后,先完成自己要做的事情,最后再回头调用Catalina.stop(),完成最终的“停止” 。
通过这样的设计,tomcat给了外部代码,或者说基于tomcat的二次开发人员很大的灵活性,不必拘泥于tomcat本身的实现,也有利于tomcat自身的功能扩展。
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/wangchengsi/archive/2008/09/20/2966850.aspx
相关推荐
【标题】:Tomcat启动管理工具 在Java Web开发领域,Tomcat是一个广泛使用的开源应用服务器,主要用于部署和运行Servlet和JSP应用。Tomcat以其轻量级、易配置和高性能的特点深受开发者喜爱。"Tomcat启动管理工具"指...
Tomcat启动调试功能详解 Tomcat启动调试功能是一种非常实用的功能,它允许开发者在Tomcat服务器上调试Java应用程序。该功能可以帮助开发者快速地查找和修复程序中的错误,从而提高开发效率。 什么是Tomcat启动调试...
### tomcat启动不了问题处理 在IT领域,Tomcat服务器是一款非常流行的开源Web服务器,它主要用来部署Java Web应用程序。然而,在使用过程中,用户可能会遇到Tomcat无法启动的问题。本文将详细探讨导致Tomcat启动...
### 关于 Quartz 随 Tomcat 启动执行一次的知识点 #### 1. Quartz 概述 Quartz 是一个开源的作业调度框架,能够触发在指定时间执行的任务(也称为作业)。它提供了丰富的调度器功能,如支持复杂的循环调度、支持...
【标题】:Tomcat启动顺序 【描述】:Tomcat作为Apache软件基金会的开源Java Servlet容器,其启动过程是理解其工作原理的关键部分。Tomcat的启动顺序涉及到多个层次的加载,从Bootstrap类开始,逐步加载系统配置、...
**Tomcat启动原理解析** Tomcat作为一款广泛使用的开源Java应用服务器,其启动过程涉及了众多关键步骤和组件的协同工作。理解Tomcat的启动原理对于开发者来说至关重要,不仅有助于提升性能优化的能力,还能在遇到...
Tomcat启动分析与配置详解 Tomcat是一款广泛使用的开源Java Servlet容器,它实现了Java EE的Web应用程序规范。理解Tomcat的启动过程和配置对于优化服务器性能和管理Web应用至关重要。本文将深入解析Tomcat的组成...
"Tomcat启动停止脚本"提供了这样的便利,使得操作过程自动化,特别是当需要将这些操作集成到服务器的计划任务中时。 一、Tomcat启动脚本 启动脚本通常名为`startup.sh`(在Unix/Linux环境)或`startup.bat`(在...
tomcat启动脚本tomcat启动脚本tomcat启动脚本tomcat启动脚本tomcat启动脚本tomcat启动脚本tomcat启动脚本tomcat启动脚本tomcat启动脚本tomcat启动脚本tomcat启动脚本tomcat启动脚本tomcat启动脚本
tomcat启动时执行java自定义方法,一般用于启动时赋值情况。
tomcat 开机启动,dos窗口去掉,tomcat 开机启动,dos窗口去掉
在本教程中,我们将深入探讨如何使用Maven来配置和启动一个内嵌式的Tomcat服务器,以便运行Web工程。 首先,我们需要在项目中添加Tomcat内嵌库。在Maven的`pom.xml`文件中,我们需要引入`tomcat7-maven-plugin`或`...
### Tomcat 启动参数设置详解 #### 一、背景介绍 在部署Java Web应用程序时,经常使用Apache Tomcat作为应用服务器。然而,在高并发或大数据量处理的应用场景下,Tomcat可能会出现内存溢出等问题。为了提升Tomcat的...
tomcat-juli.jar 用于在tomcat启动报错时除错
tomcat启动|退出执行事件类: import java.io.File; import java.io.FileWriter; import java.io.IOException; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import ...
Tomcat启动后的截图,我上传然后应用
1. **配置Tomcat环境变量**: 首先,我们需要在Tomcat的`bin`目录下的`catalina.sh`文件中设置`JAVA_HOME`和`JRE_HOME`环境变量。打开`catalina.sh`,找到`# OS specific support. $var _must_ be set to either ...
### CentOS 设置 Tomcat 开机自启动 #### 一、前言 在生产环境中,为了确保应用服务器能够稳定运行,我们通常需要配置应用服务器在系统重启后自动启动。本篇文章将详细介绍如何在CentOS环境下配置Tomcat服务器实现...
然而,在实际操作过程中,不少用户会遇到Eclipse中Tomcat启动失败的问题,这不仅影响了开发效率,也可能导致项目延期。本文将深入探讨这一问题,并提供一系列可能的解决方案,帮助开发者们有效应对Eclipse中Tomcat...
Java 代码启动 Tomcat 在 Java 中,使用 Tomcat 服务器来发布 Web 应用程序是一种常见的做法。下面,我们将详细介绍如何使用 Java 代码来启动 Tomcat 服务器,并实现远程控制 Tomcat。 标题: Java 代码启动 ...