Tomcat源码阅读之Bootstrap启动流程与classLoader设计
2014-02-27 12:09
543 查看
嗯,以前大体上看过jetty6.0的源码,算是对java EE应用容器有了一定的了解,但是在实际的线上环境中应用的最多的应该还是tomcat,而且据说性能方面tomcat也略好一些。。。那么就趁着现在还比较闲就看看它的源码吧。。。
首先在实际开始之前先来说说tomcat对ClassLoader方面的处理。。。当然这部分是参考了网络上其他的人的博客。。。
一般情况下java应用程序的classLoader父子关系大概如下:
具体他们之间的关系以及作用我前面的博客都有说过。。。 那么作为应用容器,因为可能要同时容纳多个应用,为了实现各个应用之间的隔离,那么就需要由专门的classLoader来加载个个应用自己的代码,这部分jetty的处理方式是为每一个web应用程序都创建一个自己的classLoader,这个web应用程序需要用到的所有代码都有这个classLoader来处理。。。其实还蛮简单的。。。但是有一个缺点就是一些公有的代码每个web应用程序都会重复加载,会造成一些浪费吧。。。。例如defaultservlet啥的。。。但是优点就是简单。。。
在这方面,tomcat的设计就稍微复杂了一点点。。。父子关系如下图:
这里可以每个web应用程序也有自己专属的classLoader,也就是最下层的AppClassLoader。。。
这里可以看到总的来说classLoader分成了两条线,左边那条线是tomcat服务器用的,右边的则是web应用程序用的。。。。。嗯,其实也不复杂。。。。
好了,上面的内容算是对tomcat的classLoader有了比较简单的了解。。那么接下来来看看整个tomcat服务器的启动类Bootstrap类型是怎么工作的吧。。。。
先来看看它的几个静态属性的定义: private static Bootstrap daemon = null; //当前类型的一个引用
//这两个其实一般都被赋值为tomcat的根路径
private static final File catalinaBaseFile;
private static final File catalinaHomeFile;
private static final Pattern PATH_PATTERN = Pattern.compile("(\".*?\")|(([^,])*)"); //正则表达式验证
前面其实就是一个本身类型对象的一个引用,后面两个file对象其实一般情况下就是tomcat服务器的根路径,如果在启动的时候没有特别指定的话。。。。
接下来来看看一段静态代码块: static { //这里主要是进行一些路径的初始化
// Will always be non-null
String userDir = System.getProperty("user.dir"); //当前tomcat的用户路径,说白了就是应用程序起点路径
// Home first
String home = System.getProperty(Globals.CATALINA_HOME_PROP);
File homeFile = null;
if (home != null) {
File f = new File(home);
try {
homeFile = f.getCanonicalFile();
} catch (IOException ioe) {
homeFile = f.getAbsoluteFile();
}
}
if (homeFile == null) {
// First fall-back. See if current directory is a bin directory
// in a normal Tomcat install
File bootstrapJar = new File(userDir, "bootstrap.jar"); //启动jar包,在eclipse里面用源码运行的时候没有它
if (bootstrapJar.exists()) {
File f = new File(userDir, "..");
try {
homeFile = f.getCanonicalFile();
} catch (IOException ioe) {
homeFile = f.getAbsoluteFile();
}
}
}
if (homeFile == null) {
// Second fall-back. Use current directory
File f = new File(userDir); //获取当前程序根路径的文件夹
try {
homeFile = f.getCanonicalFile(); //将路径保存到homeFile
} catch (IOException ioe) {
homeFile = f.getAbsoluteFile();
}
}
catalinaHomeFile = homeFile;
System.setProperty(
Globals.CATALINA_HOME_PROP, catalinaHomeFile.getPath());
// Then base
String base = System.getProperty(Globals.CATALINA_BASE_PROP);
if (base == null) {
catalinaBaseFile = catalinaHomeFile; //这里其实一般也都是应用程序的根路径
} else {
File baseFile = new File(base);
try {
baseFile = baseFile.getCanonicalFile();
} catch (IOException ioe) {
baseFile = baseFile.getAbsoluteFile();
}
catalinaBaseFile = baseFile;
}
System.setProperty(
Globals.CATALINA_BASE_PROP, catalinaBaseFile.getPath());
}
其实这里的处理基本上就是设置一些路径方面的类容,也就是在类型加载的时候这部分就设置好了。。。
好啦,。接下来来看看几个属性的定义: private Object catalinaDaemon = null; //当前tomcat的后台,org.apache.catalina.startup.Catalinad对象,它才是用于负责具体的server的启动
//3个层级的classLoader ,commonloader是下面两个loader的父loader,嗯,他们3个甚至可能引用的都是同一个classLoader
protected ClassLoader commonLoader = null; //tomcat与app都能见
protected ClassLoader catalinaLoader = null; //只有tomcat能见
protected ClassLoader sharedLoader = null; //只有app们可以看见,tomcat看不到
嗯,他们具体是干嘛的应该很清楚了吧。。。注释应该就说的比较清楚了。。。
嗯,接下来来看整个tomcat的入口吧,main函数: public static void main(String args[]) {
if (daemon == null) {
// Don't set daemon until init() has completed
Bootstrap bootstrap = new Bootstrap(); //这里创建当前Bootstarp类型的对象
try {
bootstrap.init(); //初始化,其实这里主要是创建org.apache.catalina.startup.Catalina对象并调用setParentClassLoader设置classLoader,用的是shareLoader
} 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) { //这里有可能是其他的参数,但是默认命令是start
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(); //启动当前bootstrap对象,其实主要是调用前面生成的org.apache.catalina.startup.Catalina的start方法
} 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);
}
}
这里可以看到整个main函数还是蛮简单的,这里首先是创建了一个Bootstrap类型的对象,如果只是启动的话,这里首先载入启动时候的参数,然后在执行start方法,那么接下来来看看start方法: //这里就是真正的启动tomcat
public void start()
throws Exception {
if( catalinaDaemon==null ) init(); //初始化catalinaDaemon,其实主要是初始化org.apache.catalina.startup.Catalina对象
Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);
//调用org.apache.catalina.startup.Catalina对象的start方法
method.invoke(catalinaDaemon, (Object [])null);
}
这个好像没啥意思。。。这里说白了就是先创建和初始化org.apache.catalina.startup.Catalina对象,接着在调用它的start方法。。。那么来看看这个init方法都做了啥吧: //初始化当前的tomcat后台,主要是创建org.apache.catalina.startup.Catalina对象,并且设置它的classLoader为catalinaLoader
public void init() throws Exception {
initClassLoaders(); //先初始化classLoader
Thread.currentThread().setContextClassLoader(catalinaLoader); //设置当前线程classLoader
SecurityClassLoad.securityClassLoad(catalinaLoader); //安全classLoader?
// Load our startup class and call its process() method
if (log.isDebugEnabled())
log.debug("Loading startup class");
Class<?> startupClass =
catalinaLoader.loadClass
("org.apache.catalina.startup.Catalina"); //获取org.apache.catalina.startup.Catalina类型
Object startupInstance = startupClass.newInstance(); //创建org.apache.catalina.startup.Catalina对象
// Set the shared extensions class loader
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; //传进这个classLoader
Method method =
startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues); //调用刚刚创建的org.apache.catalina.startup.Catalina对象的setParentClassLoader设置classLoader,shareloader
catalinaDaemon = startupInstance; //将这个启动的实例保存起来
}
嗯,首先初始化了classLoader,也就是上面提到的那三个classLoader,然后这里可以看到将当前的线程classLoader设置成了catalinaLoader,也就是专属于tomcat使用的。。。接着创建了org.apache.catalina.startup.Catalina对象,然后调用了它的setParentClassLoader方法,这里可以看到设置的classLoader是sharedLoader,前面已经提到它是属于web程序才能访问的。。。那么这里为啥分别这样设置这两个classLoader基本上猜也能猜出来原因了吧。。。哈哈。。。。。。
好啦,剩下来的事情就是调用org.apache.catalina.startup.Catalina对象的start方法来启动服务器了。。。
下一篇文章再来分析。。。。
首先在实际开始之前先来说说tomcat对ClassLoader方面的处理。。。当然这部分是参考了网络上其他的人的博客。。。
一般情况下java应用程序的classLoader父子关系大概如下:
具体他们之间的关系以及作用我前面的博客都有说过。。。 那么作为应用容器,因为可能要同时容纳多个应用,为了实现各个应用之间的隔离,那么就需要由专门的classLoader来加载个个应用自己的代码,这部分jetty的处理方式是为每一个web应用程序都创建一个自己的classLoader,这个web应用程序需要用到的所有代码都有这个classLoader来处理。。。其实还蛮简单的。。。但是有一个缺点就是一些公有的代码每个web应用程序都会重复加载,会造成一些浪费吧。。。。例如defaultservlet啥的。。。但是优点就是简单。。。
在这方面,tomcat的设计就稍微复杂了一点点。。。父子关系如下图:
这里可以每个web应用程序也有自己专属的classLoader,也就是最下层的AppClassLoader。。。
这里可以看到总的来说classLoader分成了两条线,左边那条线是tomcat服务器用的,右边的则是web应用程序用的。。。。。嗯,其实也不复杂。。。。
好了,上面的内容算是对tomcat的classLoader有了比较简单的了解。。那么接下来来看看整个tomcat服务器的启动类Bootstrap类型是怎么工作的吧。。。。
先来看看它的几个静态属性的定义: private static Bootstrap daemon = null; //当前类型的一个引用
//这两个其实一般都被赋值为tomcat的根路径
private static final File catalinaBaseFile;
private static final File catalinaHomeFile;
private static final Pattern PATH_PATTERN = Pattern.compile("(\".*?\")|(([^,])*)"); //正则表达式验证
前面其实就是一个本身类型对象的一个引用,后面两个file对象其实一般情况下就是tomcat服务器的根路径,如果在启动的时候没有特别指定的话。。。。
接下来来看看一段静态代码块: static { //这里主要是进行一些路径的初始化
// Will always be non-null
String userDir = System.getProperty("user.dir"); //当前tomcat的用户路径,说白了就是应用程序起点路径
// Home first
String home = System.getProperty(Globals.CATALINA_HOME_PROP);
File homeFile = null;
if (home != null) {
File f = new File(home);
try {
homeFile = f.getCanonicalFile();
} catch (IOException ioe) {
homeFile = f.getAbsoluteFile();
}
}
if (homeFile == null) {
// First fall-back. See if current directory is a bin directory
// in a normal Tomcat install
File bootstrapJar = new File(userDir, "bootstrap.jar"); //启动jar包,在eclipse里面用源码运行的时候没有它
if (bootstrapJar.exists()) {
File f = new File(userDir, "..");
try {
homeFile = f.getCanonicalFile();
} catch (IOException ioe) {
homeFile = f.getAbsoluteFile();
}
}
}
if (homeFile == null) {
// Second fall-back. Use current directory
File f = new File(userDir); //获取当前程序根路径的文件夹
try {
homeFile = f.getCanonicalFile(); //将路径保存到homeFile
} catch (IOException ioe) {
homeFile = f.getAbsoluteFile();
}
}
catalinaHomeFile = homeFile;
System.setProperty(
Globals.CATALINA_HOME_PROP, catalinaHomeFile.getPath());
// Then base
String base = System.getProperty(Globals.CATALINA_BASE_PROP);
if (base == null) {
catalinaBaseFile = catalinaHomeFile; //这里其实一般也都是应用程序的根路径
} else {
File baseFile = new File(base);
try {
baseFile = baseFile.getCanonicalFile();
} catch (IOException ioe) {
baseFile = baseFile.getAbsoluteFile();
}
catalinaBaseFile = baseFile;
}
System.setProperty(
Globals.CATALINA_BASE_PROP, catalinaBaseFile.getPath());
}
其实这里的处理基本上就是设置一些路径方面的类容,也就是在类型加载的时候这部分就设置好了。。。
好啦,。接下来来看看几个属性的定义: private Object catalinaDaemon = null; //当前tomcat的后台,org.apache.catalina.startup.Catalinad对象,它才是用于负责具体的server的启动
//3个层级的classLoader ,commonloader是下面两个loader的父loader,嗯,他们3个甚至可能引用的都是同一个classLoader
protected ClassLoader commonLoader = null; //tomcat与app都能见
protected ClassLoader catalinaLoader = null; //只有tomcat能见
protected ClassLoader sharedLoader = null; //只有app们可以看见,tomcat看不到
嗯,他们具体是干嘛的应该很清楚了吧。。。注释应该就说的比较清楚了。。。
嗯,接下来来看整个tomcat的入口吧,main函数: public static void main(String args[]) {
if (daemon == null) {
// Don't set daemon until init() has completed
Bootstrap bootstrap = new Bootstrap(); //这里创建当前Bootstarp类型的对象
try {
bootstrap.init(); //初始化,其实这里主要是创建org.apache.catalina.startup.Catalina对象并调用setParentClassLoader设置classLoader,用的是shareLoader
} 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) { //这里有可能是其他的参数,但是默认命令是start
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(); //启动当前bootstrap对象,其实主要是调用前面生成的org.apache.catalina.startup.Catalina的start方法
} 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);
}
}
这里可以看到整个main函数还是蛮简单的,这里首先是创建了一个Bootstrap类型的对象,如果只是启动的话,这里首先载入启动时候的参数,然后在执行start方法,那么接下来来看看start方法: //这里就是真正的启动tomcat
public void start()
throws Exception {
if( catalinaDaemon==null ) init(); //初始化catalinaDaemon,其实主要是初始化org.apache.catalina.startup.Catalina对象
Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);
//调用org.apache.catalina.startup.Catalina对象的start方法
method.invoke(catalinaDaemon, (Object [])null);
}
这个好像没啥意思。。。这里说白了就是先创建和初始化org.apache.catalina.startup.Catalina对象,接着在调用它的start方法。。。那么来看看这个init方法都做了啥吧: //初始化当前的tomcat后台,主要是创建org.apache.catalina.startup.Catalina对象,并且设置它的classLoader为catalinaLoader
public void init() throws Exception {
initClassLoaders(); //先初始化classLoader
Thread.currentThread().setContextClassLoader(catalinaLoader); //设置当前线程classLoader
SecurityClassLoad.securityClassLoad(catalinaLoader); //安全classLoader?
// Load our startup class and call its process() method
if (log.isDebugEnabled())
log.debug("Loading startup class");
Class<?> startupClass =
catalinaLoader.loadClass
("org.apache.catalina.startup.Catalina"); //获取org.apache.catalina.startup.Catalina类型
Object startupInstance = startupClass.newInstance(); //创建org.apache.catalina.startup.Catalina对象
// Set the shared extensions class loader
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; //传进这个classLoader
Method method =
startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues); //调用刚刚创建的org.apache.catalina.startup.Catalina对象的setParentClassLoader设置classLoader,shareloader
catalinaDaemon = startupInstance; //将这个启动的实例保存起来
}
嗯,首先初始化了classLoader,也就是上面提到的那三个classLoader,然后这里可以看到将当前的线程classLoader设置成了catalinaLoader,也就是专属于tomcat使用的。。。接着创建了org.apache.catalina.startup.Catalina对象,然后调用了它的setParentClassLoader方法,这里可以看到设置的classLoader是sharedLoader,前面已经提到它是属于web程序才能访问的。。。那么这里为啥分别这样设置这两个classLoader基本上猜也能猜出来原因了吧。。。哈哈。。。。。。
好啦,剩下来的事情就是调用org.apache.catalina.startup.Catalina对象的start方法来启动服务器了。。。
下一篇文章再来分析。。。。
相关文章推荐
- Tomcat源码解读:ClassLoader的设计
- Tomcat源码分析之ClassLoader部分的设计详细分析
- Tomcat源码阅读之Server.xml文件的处理与Catalina启动流程
- Tomcat源码阅读之Server.xml文件的处理与Catalina启动流程
- Tomcat源码分析之ClassLoader部分的设计详细分析
- tomcatPlugin启动tomcat 报错 java.lang.ClassNotFoundException: org.apache.catalina.loader.DevLoader
- 启动Tomcat 不停的报org.apache.catalina.loader.WebappClassLoader modified异常
- tomcat启动报错:java.lang.ClassNotFoundException:org.springframework.web.context.ContextLoaderListener
- 利用URLClassLoader读取Jar包并反射类(利用Tomcat源码)
- 【项目启动】 tomcat启动,项目无法启动,无法正常访问/项目可以启动,报错:java.lang.ClassNotFoundException: ContextLoaderListener
- Tomcat WebappClassLoader 类加载机制源码分析
- Netty源码阅读(一) ServerBootstrap启动
- tomcat无法启动 ClassNotFoundException org.springframework.web.context.ContextLoaderListener
- [Chrome源码阅读] 理解Chrome导航网址的流程及render进程启动模式
- [Chrome源码阅读]Chrome启动代码流程3
- Netty源码阅读(一) ServerBootstrap启动
- tomcat源码解析(一)——Bootstrap和Catalina启动部分
- Eclipse下启动tomcat报错:/bin/bootstrap.jar which is referenced by the classpath, does not exist.
- 启动tomcat的时候报错信息如下:. Could not find the main class: org.apache.catalina.startup.Bootstrap. Program wi
- Tomcat启动时classloader加载顺序