Tomcat启动流程解析
一、前言众所周知,Tomcat是作为一款优秀的web服务容器被广泛应用。Tomcat是基于J2EE规范实现了经典的双亲委派模型的类加载体系。二、相关概念1、JAVA虚拟机主要的类加载器:a、Bootstrap Loader:加载lib目录下或者System.getProperty("sun.boot.class.path")、或者-XBootclasspath所指定的路径或jar。
一、前言
众所周知,Tomcat是作为一款优秀的web服务容器被广泛应用。Tomcat是基于J2EE规范实现了经典的双亲委派模型的类加载体系。
二、相关概念
1、JAVA虚拟机主要的类加载器:
a、Bootstrap Loader:加载lib目录下或者System.getProperty("sun.boot.class.path")、或者-XBootclasspath所指定的路径或jar。
b、Extended Loader:加载lib\ext目录下或者System.getProperty("java.ext.dirs")所指定的路径或jar。在使用Java运行程序时,也可以指定其搜索路径,例如:java-Djava.ext.dirs=d:\projects\testproj\classes HelloWorld。
c、AppClass Loader:加载System.getProperty("java.class.path")所指定的路径或jar。在使用Java运行程序时,也可以加上-cp来覆盖原有的Classpath设置,例如: java -cp ./lavasoft/classes HelloWorld。
2、双亲委派模式类加载体系
双亲委派模式类加载体系概念:每个ClassLoader实例都有一个父类加载器的引用(不是继承的关系,是一个包含的关系),虚拟机内置的类加载器(Bootstrap ClassLoader)本身没有父类加载器,但可以用作其它ClassLoader实例的的父类加载器。当一个ClassLoader实例需要加载某个类时,它会试图亲自搜索某个类之前,先把这个任务委托给它的父类加载器,这个过程是由上至下依次检查的,首先由最顶层的类加载器Bootstrap ClassLoader试图加载,如果没加载到,则把任务转交给Extension ClassLoader试图加载,如果也没加载到,则转交给App ClassLoader进行加载,如果它也没有加载得到的话,则返回给委托的发起者,由它到指定的文件系统或网络等URL中加载该类。如果它们都没有加载到这个类时,则抛出ClassNotFoundException异常。否则将这个找到的类生成一个类的定义,并将它加载到内存当中,最后返回这个类在内存中的Class实例对象。
双亲委派模式类加载体系图示:
从上图中可以看出,JVM虚拟机的类加载器执行的顺序为:
BootStrap ClassLoader > Extension ClassLoader > App ClassLoader,除了上述的类加载器之外,我们也可以定义自己的类加载器,但是这些自定义的类加载器都必须继承java.lang.ClassLoader类,同样Extension ClassLoader与App ClassLoader也继承这个类。但是Bootstrap ClassLoader不继承自ClassLoader,因为它不是一个普通的Java类,底层由C++编写,已嵌入到了JVM内核当中,当JVM启动后,Bootstrap ClassLoader也随着启动,负责加载完核心类库后,并构造Extension ClassLoader和App ClassLoader类加载器。
三、tomcat类加载加载机制
1、前言中已经提到tomcat类加载机制是基于J2EE的双亲委派模式,双亲委派模式此处就不再赘述。tomcat在双亲委派模式基础上定制了自己的类加载器,其类加载体系图如下:
从上图片看出tomcat的类加载体系中包含以下几个类加载器:
a、ClassLoader:Java提供的类加载器抽象类,用户自定义的类加载器需要继承实现;
b、commonLoader:Tomcat最基本的类加载器,加载路径中的class可以被Tomcat容器本身以及各个Webapp访问;
c、catalinaLoader:Tomcat容器私有的类加载器,加载路径中的class对于Webapp不可见;
d、sharedLoader:各个Webapp共享的类加载器,加载路径中的class对于所有Webapp可见,但是对于Tomcat容器不可见;
e、WebappClassLoader:各个Webapp私有的类加载器,加载路径中的class只对当前Webapp可见;
四、tomcat启动源码分析(以tomcat8为例)
1、tomcat启动从执行 org.apache.catalina.startup.Bootstrap.main()方法开始。main方法主要实现的逻辑如下源码:
public static void main(String[] args) {
if(daemon == null) {
Bootstrap t = new Bootstrap();
try {
t.init();
} catch (Throwable var3) {
handleThrowable(var3);
var3.printStackTrace();
return;
}
daemon = t;
} else {
Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
}
try {
String t2 = "start";
if(args.length > 0) {
t2 = args[args.length - 1];
}
……
}
上叙代码主要实现的BootStrap的初始化及设置当前线程的类加载器。
2、main访问中调用的init()方法源码如下:
public void init() throws Exception {
this.initClassLoaders();
Thread.currentThread().setContextClassLoader(this.catalinaLoader);
SecurityClassLoad.securityClassLoad(this.catalinaLoader);
if(log.isDebugEnabled()) {
log.debug("Loading startup class");
}
Class startupClass = this.catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.newInstance();
if(log.isDebugEnabled()) {
log.debug("Setting startup class properties");
}
String methodName = "setParentClassLoader";
Class[] paramTypes = new Class[]{Class.forName("java.lang.ClassLoader")};
Object[] paramValues = new Object[]{this.sharedLoader};
Method method = startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues);
this.catalinaDaemon = startupInstance;
}
上述代码执行的逻辑说明如下:
a、initClassLoaders()方法中初始化了commonLoader,catalinaLoader,sharedLoader这几个类加载器,具体初始化过程可以参看源码
b、设置当前线程的类加载器setContextClassLoader,设置安全的类加载器ecurityClassLoad.securityClassLoad(this.catalinaLoader);
c、通过java反射机制初始化org.apache.catalina.startup.Catalina并执行setParentClassLoader方法
3、同时main方法中还有部分源码如下:
String t2 = "start";
if(args.length > 0) {
t2 = args[args.length - 1];
}
if(t2.equals("startd")) {
args[args.length - 1] = "start";
daemon.load(args);
daemon.start();
} else if(t2.equals("stopd")) {
args[args.length - 1] = "stop";
daemon.stop();
} else if(t2.equals("start")) {
daemon.setAwait(true);
daemon.load(args);
daemon.start();
} else if(t2.equals("stop")) {
daemon.stopServer(args);
} else if(t2.equals("configtest")) {
daemon.load(args);
if(null == daemon.getServer()) {
System.exit(1);
}
System.exit(0);
上述代码中的dameon.stopServer(),dameon.start(),dameon.load()都需要通过反射机制调用到org.apache.catalina.startup.Catalina类中相应的方法执行操作。
4、org.apache.catalina.startup.Catalina类中封装了所有的tomcat相关逻辑方法。主要的方法有:
a、load()方法,主要是加载server.xml配置及初始化相关资源部分源码如下:
public void load() {
long t1 = System.nanoTime();
this.initDirs();
this.initNaming();
Digester digester = this.createStartDigester();
InputSource inputSource = null;
Object inputStream = null;
File file = null;
label257: {
try {
try {
file = this.configFile();
其中server.xml是用Digester这个工具来parse的,这是apache的common项目。
b 、 start() 方法,主要是用于启动 tomcat 容器的。部分源码如下:public void start() {
if(this.getServer() == null) {
this.load();
}
if(this.getServer() == null) {
log.fatal("Cannot start server. Server instance is not configured.");
} else {
long t1 = System.nanoTime();
try {
this.getServer().start();
} catch (LifecycleException var7) {
c、stop()方法,主要用于停止tomcat服务,部分源码如下:
public void stop() {
try {
if(this.useShutdownHook) {
Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
LogManager e = LogManager.getLogManager();
if(e instanceof ClassLoaderLogManager) {
((ClassLoaderLogManager)e).setUseShutdownHook(true);
}
}
} catch (Throwable var3) {
ExceptionUtils.handleThrowable(var3);
}
try {
d、stopServer()方法,用于停止tomcat容器,部分源码如下:
public void stopServer(String[] arguments) {
if(arguments != null) {
this.arguments(arguments);
}
Server s = this.getServer();
if(s == null) {
Digester e = this.createStopDigester();
File file = this.configFile();
Throwable var6;
try {
FileInputStream x2 = new FileInputStream(file);
var6 = null;
更多推荐
所有评论(0)