Tomcat启动过程相当复杂,总结起来大概分为三个阶段,分别是初始化(Init)过程,加载(load)过程和启动(start)过程。先来分析下初始化的过程。程序的入口是Bootstrap类的main方法。
public static void main(String args[]) {
if (daemon == null) {
daemon = new Bootstrap();
try {
daemon.init();
} catch (Throwable t) {
t.printStackTrace();
return;
}
}
略
。
。
。
其中init便是tomcat启动前进行一些初始化操作的方法。
public void init() throws Exception {
// Set Catalina path
setCatalinaHome();
setCatalinaBase();
initClassLoaders();
Thread.currentThread().setContextClassLoader(catalinaLoader);
SecurityClassLoad.securityClassLoad(catalinaLoader);
// 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");
Object startupInstance = startupClass.newInstance();
// 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;
Method method =
startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues);
catalinaDaemon = startupInstance;
}
setCatalinaBase和setCatalinaHome方法的作用是设置catalina.base和catalina.home的值。接下来重点阅读下initClassLoaders方法。
private void initClassLoaders() {
try {
commonLoader = createClassLoader("common", null);
if( commonLoader == null ) {
// no config file, default to this loader - we might be in a 'single' env.
commonLoader=this.getClass().getClassLoader();
}
catalinaLoader = createClassLoader("server", commonLoader);
sharedLoader = createClassLoader("shared", commonLoader);
} catch (Throwable t) {
log.error("Class loader creation threw exception", t);
System.exit(1);
}
}
方法中先实例化了commonLoader类加载器,然后再分别实例化了catalinaLoader类加载器和sharedLoader类加载器。那么是如何实例化commonLoader类加载器的呢?这三个类加载器又有什么不一样呢?
private ClassLoader createClassLoader(String name, ClassLoader parent)
throws Exception {
String value = CatalinaProperties.getProperty(name + ".loader");
if ((value == null) || (value.equals("")))
return parent;
ArrayList repositoryLocations = new ArrayList();
ArrayList repositoryTypes = new ArrayList();
int i;
略
。
。
。
try {
URL url=new URL(repository);
repositoryLocations.add(repository);
repositoryTypes.add(ClassLoaderFactory.IS_URL);
continue;
} catch (MalformedURLException e) {
// Ignore
}
if (repository.endsWith("*.jar")) {
repository = repository.substring
(0, repository.length() - "*.jar".length());
repositoryLocations.add(repository);
repositoryTypes.add(ClassLoaderFactory.IS_GLOB);
} else if (repository.endsWith(".jar")) {
repositoryLocations.add(repository);
repositoryTypes.add(ClassLoaderFactory.IS_JAR);
} else {
repositoryLocations.add(repository);
repositoryTypes.add(ClassLoaderFactory.IS_DIR);
}
}
String[] locations = (String[]) repositoryLocations.toArray(new String[0]);
Integer[] types = (Integer[]) repositoryTypes.toArray(new Integer[0]);
ClassLoader classLoader = ClassLoaderFactory.createClassLoader
(locations, types, parent);
略
。
。
。
先来看看CatalinaProperties.getProperty方法。在tomcat的conf目录下有个catalina.properties文件,默认的文件中定义了common.loader的值,也就是commonLoader加载类的路径。但是文件中却没有定义catalinaLoader和sharedLoader的值,因此在默认的情况下并不会初始化catalinaLoader和sharedLoader类加载器。获取到类加载的路径之后,会将路径按类型进行整理,比如路径为jar类型,则repositoryTypes的值就为ClassLoaderFactory.IS_JAR,路径为目录的,则repositoryTypes的值就为ClassLoaderFactory.IS_DIR。如${catalina.base}/lib这个路径就是个目录。路径整理完之后,调用ClassLoaderFactory的createClassLoader方法实例化commonLoader类加载器。
在createClassLoader方法中获取路径的URL,并保存在一个LinkedHashSet集合中,然后将集合转换为数组,作为StandardClassLoader的构造方法参数,并实例化StandardClassLoader类加载器。StandardClassLoader继承了URLClassLoader,该类加载器用于从指向 JAR 文件和目录的 URL 的搜索路径加载类和资源,也就是说,通过URLClassLoader就可以加载指定jar中的class到内存中,最终返回的类加载器为StandardClassLoader。
initClassLoaders方法执行完之后,commonLoader的实际值为StandardClassLoader。而catalinaLoader和sharedLoader由于没有配置类加载的路径,因此它们均被赋值成commonLoader的值。接着将catalinaLoader作为上下文类加载器。最后使用catalinaLoader加载Catalina类,并实例化,利用反射机制调用Catalina类的setParentClassLoader方法,将其parentClassLoader设置为StandardClassLoader类加载器,至此整个Init过程完成。