1. Tomcat类加载器图
先给出一张Tomcat中的类加载器图:
- Common ClassLoader:负责加载Tomcat、Web应用都复用的类
- Catalina ClassLoader:负责加载Tomcat专用的类,这些类在Web应用中不可见
- Shared ClassLoader:负责加载Tomcat下所有Web应用都复用的类,这些类在Tomcat中不可见
- WebApp ClassLoader:负责加载具体某个Web应用的类,这些类在Tomcat、其他Web应用中不可见
- Jsper ClassLoader:每个jsp页面一个类加载器不同的jsp页面有不同的类加载器,方便实现jsp页面的热插拔
2. 源码分析
Bootstrap.main()
public static void main(String args[]) {
synchronized (daemonLock) {
// main第一次执行时,daemon为null,new Bootstrap(),并执行init。
if (daemon == null) {
Bootstrap bootstrap = new Bootstrap();
try {
bootstrap.init();
} catch (Throwable t) {
handleThrowable(t);
t.printStackTrace();
return;
}
// bootstrap赋给daemon
daemon = bootstrap;
} else {
Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
}
}
try {
String command = "start";
if (args.length > 0) {
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);
// load
daemon.load(args);
// start
daemon.start();
if (null == daemon.getServer()) {
System.exit(1);
}
} 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) {
if (t instanceof InvocationTargetException &&
t.getCause() != null) {
t = t.getCause();
}
handleThrowable(t);
t.printStackTrace();
System.exit(1);
}
}
main()分为两块:一块是init、一块是load、start。
接着看init():
public void init() throws Exception {
// 初始化类加载器
initClassLoaders();
Thread.currentThread().setContextClassLoader(catalinaLoader);
SecurityClassLoad.securityClassLoad(catalinaLoader);
// 使用catalinaLoader加载Catalina类,并创建Catalina类实例startupInstance
Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.getConstructor().newInstance();
// 设置Catalina类的parentClassLoader属性为sharedLoader
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
catalinaDaemon = startupInstance;
}
接下来看initClassLoaders():在其中创建了Common ClassLoader、Catalina ClassLoader、Shared ClassLoader。
private void initClassLoaders() {
try {
// 创建Common ClassLoader,创建时父加载器传入为null,最终是使用URLClassLoader创建的,因此Common ClassLoader的父加载器为AppClassLoader
commonLoader = createClassLoader("common", null);
if (commonLoader == null) {
commonLoader = this.getClass().getClassLoader();
}
// 创建Catalina ClassLoader,父加载器为CommonLoader。根据createClassLoader中server.loader为空,返回commonLoader
catalinaLoader = createClassLoader("server", commonLoader);
// 创建Shared ClassLoader,父加载器为CommonLoader。根据createClassLoader中shared.loader为空,返回commonLoader
sharedLoader = createClassLoader("shared", commonLoader);
// 因此,commonLoader、serverLoader、sharedLoader都是同一个类加载器commonLoader
} catch (Throwable t) {
handleThrowable(t);
log.error("Class loader creation threw exception", t);
System.exit(1);
}
}
接下来看SecurityClassLoad.securityClassLoad(catalinaLoader):
public final class SecurityClassLoad {
public static void securityClassLoad(ClassLoader loader) throws Exception {
securityClassLoad(loader, true);
}
static void securityClassLoad(ClassLoader loader, boolean requireSecurityManager) throws Exception {
if (requireSecurityManager && System.getSecurityManager() == null) {
return;
}
// 使用Catalina ClassLoader加载目录
loadCorePackage(loader);
loadCoyotePackage(loader);
loadLoaderPackage(loader);
loadRealmPackage(loader);
loadServletsPackage(loader);
loadSessionPackage(loader);
loadUtilPackage(loader);
loadJavaxPackage(loader);
loadConnectorPackage(loader);
loadTomcatPackage(loader);
}
}
private static final void loadCorePackage(ClassLoader loader) throws Exception {
final String basePackage = "org.apache.catalina.core.";
loader.loadClass(basePackage + "AccessLogAdapter");
loader.loadClass(basePackage + "ApplicationContextFacade$PrivilegedExecuteMethod");
loader.loadClass(basePackage + "ApplicationDispatcher$PrivilegedForward");
loader.loadClass(basePackage + "ApplicationDispatcher$PrivilegedInclude");
loader.loadClass(basePackage + "ApplicationPushBuilder");
loader.loadClass(basePackage + "AsyncContextImpl");
loader.loadClass(basePackage + "AsyncContextImpl$AsyncRunnable");
loader.loadClass(basePackage + "AsyncContextImpl$DebugException");
loader.loadClass(basePackage + "AsyncListenerWrapper");
loader.loadClass(basePackage + "ContainerBase$PrivilegedAddChild");
loadAnonymousInnerClasses(loader, basePackage + "DefaultInstanceManager");
loader.loadClass(basePackage + "DefaultInstanceManager$AnnotationCacheEntry");
loader.loadClass(basePackage + "DefaultInstanceManager$AnnotationCacheEntryType");
loader.loadClass(basePackage + "ApplicationHttpRequest$AttributeNamesEnumerator");
}
使用Catalina ClassLoader加载的目录有:
- org.apache.catalina.core.*
- org.apache.coyote.*
- org.apache.catalina.loader.*
- org.apache.catalina.realm.*
- org.apache.catalina.servlets.*
- org.apache.catalina.session.*
- org.apache.catalina.util.*
- javax.servlet.http.Cookie
- org.apache.catalina.connector.*
- 10.org.apache.tomcat.*
3. 其他源码分析
Tomcat创建类加载器时用到了createClassLoader。
private ClassLoader createClassLoader(String name, ClassLoader parent) throws Exception {
// 获取类加载器要加载的位置,如果为空,则返回父类加载器
// 就是从 catalina.properties文件里找 common.loader,shared.loader,server.loader 对应的值
// 在catalina.properties文件中,common.loader有值,shared.loader、server.loader为空
String value = CatalinaProperties.getProperty(name + ".loader");
if ((value == null) || (value.equals("")))
return parent;
value = replace(value);
List<Repository> repositories = new ArrayList<>();
String[] repositoryPaths = getPaths(value);
// 把Repository看成是一个个待加载类的位置
for (String repository : repositoryPaths) {
// Check for a JAR URL repository
try {
@SuppressWarnings("unused")
URL url = new URL(repository);
repositories.add(new Repository(repository, RepositoryType.URL));
continue;
} catch (MalformedURLException e) {
// Ignore
}
// Local repository
if (repository.endsWith("*.jar")) {
repository = repository.substring(0, repository.length() - "*.jar".length());
repositories.add(new Repository(repository, RepositoryType.GLOB));
} else if (repository.endsWith(".jar")) {
repositories.add(new Repository(repository, RepositoryType.JAR));
} else {
repositories.add(new Repository(repository, RepositoryType.DIR));
}
}
// 使用类加载器工厂创建类加载器
return ClassLoaderFactory.createClassLoader(repositories, parent);
}
catalina.properties:
common.loader="${catalina.base}/lib","${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar"
server.loader=
shared.loader=
public enum RepositoryType {
DIR,
GLOB,
JAR,
URL
}
public static class Repository {
// 表示位置
private final String location;
// 表示类型:目录、jar集合、jar、url
private final RepositoryType type;
public Repository(String location, RepositoryType type) {
this.location = location;
this.type = type;
}
public String getLocation() {
return location;
}
public RepositoryType getType() {
return type;
}
}
public static ClassLoader createClassLoader(List<Repository> repositories, final ClassLoader parent) throws Exception {
...
Set<URL> set = new LinkedHashSet<>();
if (repositories != null) {
for (Repository repository : repositories) {
if (repository.getType() == RepositoryType.URL) {
URL url = buildClassLoaderUrl(repository.getLocation());
set.add(url);
} else if (repository.getType() == RepositoryType.DIR) {
...
set.add(url);
} else if (repository.getType() == RepositoryType.JAR) {
...
set.add(url);
} else if (repository.getType() == RepositoryType.GLOB) {
...
set.add(url);
}
}
}
}
final URL[] array = set.toArray(new URL[0]);
...
return AccessController.doPrivileged(new PrivilegedAction<URLClassLoader>() {
@Override
public URLClassLoader run() {
if (parent == null)
// 创建类加载器,创建URLClassLoader时,如果parent为null,则默认parent为AppClassLoader
return new URLClassLoader(array);
else
// 创建类加载器
return new URLClassLoader(array, parent);
}
});
}