startup.bat
会最终运行Bootstrap
的main
方法,而main
会先调用init
方法。
setCatalinaHome();
setCatalinaBase();
点进它们的实现,发现这两个方法都调用到System.getProperty(Globals.CATALINA_HOME_PROP)
,其中Globals.CATALINA_HOME_PROP
为"catalina.home"
。这个调用的返回值不会是空,因为在之前的idea启动设置中:
catalina.home
环境变量设置为了tomcat的home目录。
这两个函数会设置Globals.CATALINA_BASE_PROP
和Globals.CATALINA_HOME_PROP
的系统变量。
创建commonLoader
、catalinaLoader
、sharedLoader
类加载器(默认情况下这三个类加载器指向同一个对象。它们都调用了createClassLoader
。
装载 catalina.properties
里配置的目录下的文件和jar包,后两个加载器的父加载器都是第一个,最后注册了 MBean,可以用于 JVM 监控该对象)。
String value = CatalinaProperties.getProperty(name + ".loader");
CatalinaProperties
在static代码块内会读取{home}/conf/catalina.properties
,加载到properties
变量。之后便可调用getProperty
获取其中的属性。
换句话说,CatalinaProperties的属性都可在文件catalina.properties
找到。
value = replace(value);
把value
中的${catalina.base}
和${catalina.home}
替换为实际的字符串。
中间的一段代码是根据value
分割的每个路径各自生成一个Repository
,添加到repositories
里。
最后调用
return ClassLoaderFactory.createClassLoader(repositories, parent);
其中getCanonicalPath
可查阅file的getPath getAbsolutePath和getCanonicalPath的不同。
该函数会先解析repositories
,把路径和需要添加的jar包的URL
都保存到set
中。
最后调用
return AccessController.doPrivileged(
new PrivilegedAction<URLClassLoader>() {
@Override
public URLClassLoader run() {
if (parent == null)
return new URLClassLoader(array);
else
return new URLClassLoader(array, parent);
}
});
其中AccessController.doPrivileged和URLClassLoader可看教程(这两个知识点分别能占据不少学习时间)。
总之,createClassLoader
会根据repositories
找到路径和路径下的jar包,将它们合起来,创建一个基于这些URL的URLClassLoader。
commonLoader = createClassLoader("common", null);
会将commonLoader
绑定一个URLClassLoader
,而
catalinaLoader = createClassLoader("server", commonLoader);
sharedLoader = createClassLoader("shared", commonLoader);
其实都是第一个commonLoader
,因为createClassLoader
内有:
if ((value == null) || (value.equals("")))
return parent;
Thread.currentThread().setContextClassLoader(catalinaLoader);
SecurityClassLoad.securityClassLoad(catalinaLoader);
Thread.currentThread().setContextClassLoader(catalinaLoader);
是为了设置该线程的上下文ClassLoader
,以便以后调用Class.forName(string name)
加载时,用给好的catalinaLoader
。SecurityClassLoad.securityClassLoad(catalinaLoader);
是会加载各种需要用到的类,以防以后被安全检查阻止。后面的这些代码就是用反射机制完成以下事情:
org.apache.catalina.startup.Catalina
对象public void setParentClassLoader(ClassLoader parentClassLoader)
方法,且参数传入为sharedLoader
然后执行以下代码,获取最后一个参数。
String command = "start";
if (args.length > 0) {
command = args[args.length - 1];
}
由于传来的最后一个参数是start
(startup.bat
中传入的),故会执行以下代码,也就是load
和start
else if (command.equals("start")) {
daemon.setAwait(true);
daemon.load(args);
daemon.start();
}
就是通过反射调用 catalinaDaemon 对象的 load 方法,catalinaDaemon 对象在上面的 init 方法中已经实例化过了。
start 方法与 load 方法相似,也是通过反射调用 catalinaDaemon 对象上的 start 方法
1.就是设置"catalina.home"和"catalina.base"环境变量
lib
下的jar包(源码中lib
是空的,正式软件中lib
有很多需要的jar
包)、预加载各种类。URLClassLoader
为各种loader以及线程的ClassLoaderCatalina
服务器实例,并调用其load
和start
方法。下面一篇文章将分析 catalinaDaemon 对象中的 load、start 两个方法,里面会涉及一个有趣的话题 —— Digester 的使用。