tomcat在启动的时候使用了三个类加载器
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);
}
}
三个类加载器加载了catalina.properties中的jar
common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar
server.loader=
shared.loader=
由此可见三个类加载器加载的jar是一样的。为什么要用三个类加载器,从名称看应该是有分别的作用考虑,负载的考虑也许吧。
此外 private ClassLoader createClassLoader(String name, ClassLoader parent)中调用 ClassLoader classLoader = ClassLoaderFactory.createClassLoader
(locations, types, parent);
tomcat自定义了一个类加载器,并且定义了ClassLoaderFactory根据参数定义返回classloader。总体上ClassLoaderFactory就是根据catalina.properties中的jar的位置,做了一些字符串的处理。
public static ClassLoader createClassLoader(String locations[],
Integer types[],
ClassLoader parent)
throws Exception {
if (log.isDebugEnabled())
log.debug("Creating new class loader");
// Construct the "class path" for this class loader
Set<URL> set = new LinkedHashSet<URL>();
if (locations != null && types != null && locations.length == types.length) {
for (int i = 0; i < locations.length; i++) {
String location = locations[i];
if ( types[i] == IS_URL ) {
URL url = new URL(location);
if (log.isDebugEnabled())
log.debug(" Including URL " + url);
set.add(url);
} else if ( types[i] == IS_DIR ) {
File directory = new File(location);
directory = new File(directory.getCanonicalPath());
if (!directory.exists() || !directory.isDirectory() ||
!directory.canRead())
continue;
URL url = directory.toURI().toURL();
if (log.isDebugEnabled())
log.debug(" Including directory " + url);
set.add(url);
} else if ( types[i] == IS_JAR ) {
File file=new File(location);
file = new File(file.getCanonicalPath());
if (!file.exists() || !file.canRead())
continue;
URL url = file.toURI().toURL();
if (log.isDebugEnabled())
log.debug(" Including jar file " + url);
set.add(url);
} else if ( types[i] == IS_GLOB ) {
File directory=new File(location);
if (!directory.exists() || !directory.isDirectory() ||
!directory.canRead())
continue;
if (log.isDebugEnabled())
log.debug(" Including directory glob "
+ directory.getAbsolutePath());
String filenames[] = directory.list();
for (int j = 0; j < filenames.length; j++) {
String filename = filenames[j].toLowerCase(Locale.ENGLISH);
if (!filename.endsWith(".jar"))
continue;
File file = new File(directory, filenames[j]);
file = new File(file.getCanonicalPath());
if (!file.exists() || !file.canRead())
continue;
if (log.isDebugEnabled())
log.debug(" Including glob jar file "
+ file.getAbsolutePath());
URL url = file.toURI().toURL();
set.add(url);
}
}
}
}
// Construct the class loader itself
URL[] array = set.toArray(new URL[set.size()]);
if (log.isDebugEnabled())
for (int i = 0; i < array.length; i++) {
log.debug(" location " + i + " is " + array[i]);
}
StandardClassLoader classLoader = null;
if (parent == null)
classLoader = new StandardClassLoader(array);
else
classLoader = new StandardClassLoader(array, parent);
return (classLoader);
}
定义了类加载器后,在init()中加载
Class<?> startupClass =
catalinaLoader.loadClass
("org.apache.catalina.startup.Catalina");
这个才是tomcat的守护进程。
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;
这里要说的是
Class.forName()和ClassLoader.loadClass()的区别?
这两个方法由给类名作为参数,动态的定位并且加载类。然而,两者行为的区别在于用哪个加载器(java.lang.ClassLoader)去加载和加载完后的类是否就已经初始化。
对于Class.forName()最常见的形式就是用一个单独的String为参数,使用当前调用者的类加载器。这个类加载器加载代码执行forName()方法。比较ClassLoader.loadClass(),它是一个实例方法,需要你去选择确定的classloader。这个类加载器可以是也可以不是当前加载器。如果选择一个特定的类加载器对你的设计尤为重要,你可以使用ClassLoader.loadClass()或者三个参数的forName()。
更进一步,Class.forName()的常见形式会初始化加载的类。这样做的就是执行了类的静态初始化方法,也就是byte代码对应的所有静态初始化表达式。这和ClassLoader.loadClass()是不同的,ClassLoader.loadClass()直到类第一次使用的时候才初始化。