您的位置:首页 > 运维架构 > Tomcat

tomcat系列之类加载器

2015-01-02 18:42 393 查看
jvm中有三种类加载器,好吧,为了介绍tomcat中的类加载器,先来普及下类加载器的概念,参见这篇文章——JVM类加载器 。

就像JVM的类加载器一样,为了安全tomcat中自己扩展了几种类加载器,分别来加载不同路径下的lib库,因为一个tomcat可以部署不同的应用,如果各个应用的lib库由相同的类加载器加载那么带来的安全隐患不言而喻 。来看一下标准的tomcat目录:



lib目录下的类文件是整个tomcat需要的,webapp下所有应用是不应该有权限访问到lib下的类的。我们通过源码来一步步看tomcat是如何处理类加载问题的,先来看下Bootstrap的主函数:

public static void main(String args[]) {

if (daemon == null) {
daemon = new Bootstrap();
try {
daemon.init();
} catch (Throwable t) {
t.printStackTrace();
return;
}
}

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);
daemon.load(args);
daemon.start();
} else if (command.equals("stop")) {
daemon.stopServer(args);
} else {
log.warn("Bootstrap: command \"" + command + "\" does not exist.");
}
} catch (Throwable t) {
t.printStackTrace();
}

}

首先要的逻辑是init初始化,init的代码就不贴了,主要的逻辑讲一下,初始化catalinahome和catalineabase,可以将catalinahome简单理解为tomcat的安装目录,catalinabase目录为tomcat的工作目录,默认这两个目录是一致的。紧接着是初始化类加载器的动作,重点看下这里的代码:

private void initClassLoaders() {
try {
//commonLoader,专门负责加载catalina.properties配置文件里的common.loader=后面的类库
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专门负责加载server.loader=后面的类库
catalinaLoader = createClassLoader("server", commonLoader);
//专门加载shared.loader=后面的类库
sharedLoader = createClassLoader("shared", commonLoader);
} catch (Throwable t) {
log.error("Class loader creation threw exception", t);
System.exit(1);
}
}

三种类加载器,其中commonLoader是另外两个的父加载器,这三种类加载器都是继承自URLClassLoader。实际上还有一种类加载器,WebAppClassLoader专门负责加载webapp中的类库。初始化方法中有这么一句代码:Thread.currentThread().setContextClassLoader(catalinaLoader);这就是JVM类加载中讲到过的线程上下文加载器,目前还不知道这里设置的目的是什么。来看看WebappClassLoader的源码,其主要是覆写了loadClass方法:

// (0) Check our previously loaded local class cache
clazz = findLoadedClass0(name);
if (clazz != null) {
if (log.isDebugEnabled())
log.debug("  Returning class from cache");
if (resolve)
resolveClass(clazz);
return (clazz);
}

// (0.1) Check our previously loaded class cache
clazz = findLoadedClass(name);
if (clazz != null) {
if (log.isDebugEnabled())
log.debug("  Returning class from cache");
if (resolve)
resolveClass(clazz);
return (clazz);
}

// (0.2) Try loading the class with the system class loader, to prevent
//       the webapp from overriding J2SE classes
try {
clazz = system.loadClass(name);
if (clazz != null) {
if (resolve)
resolveClass(clazz);
return (clazz);
}
} catch (ClassNotFoundException e) {
// Ignore
}

首先去本地的缓存查找,如果没有找到则去父类的缓存查找,如果还没找到则根据用户的配置决定是由本加载器加载或者由父类加载器加载:

boolean delegateLoad = delegate || filter(name);

// (1) Delegate to our parent if requested
if (delegateLoad) {
if (log.isDebugEnabled())
log.debug("  Delegating to parent classloader1 " + parent);
//由父加载器去加载,也就是sharedClassLoader
ClassLoader loader = parent;
if (loader == null)
loader = system;
try {
clazz = loader.loadClass(name);
if (clazz != null) {
if (log.isDebugEnabled())
log.debug("  Loading class from parent");
if (resolve)
resolveClass(clazz);
return (clazz);
}
} catch (ClassNotFoundException e) {
;
}
}

// (2) Search local repositories
if (log.isDebugEnabled())
log.debug("  Searching local repositories");
try {
//否则的话从本地仓库查找
clazz = findClass(name);
if (clazz != null) {
if (log.isDebugEnabled())
log.debug("  Loading class from local repository");
if (resolve)
resolveClass(clazz);
return (clazz);
}
} catch (ClassNotFoundException e) {
;
}

// (3) Delegate to parent unconditionally
if (!delegateLoad) {
if (log.isDebugEnabled())
log.debug("  Delegating to parent classloader at end: " + parent);
ClassLoader loader = parent;
if (loader == null)
loader = system;
try {
clazz = loader.loadClass(name);
if (clazz != null) {
if (log.isDebugEnabled())
log.debug("  Loading class from parent");
if (resolve)
resolveClass(clazz);
return (clazz);
}
} catch (ClassNotFoundException e) {
;
}
}

throw new ClassNotFoundException(name);

比如WEB-INF\classes或WEB-INF\lib下面的类库通常需要从findClass中获取。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息