内容简介:【Tomcat学习笔记】9-ClassLoader
一直在入门,但从未精通,所以不敢说深入分析 XXX,顶多就是一个学习笔记,但是是原创,转载请注明出处,好吧,说的好像有人会看一样。
说明,为了简洁,这里贴的代码都有所删减。
先来张图:
ClassLoader基础
Java 自带了三个类加载器,BootstrapClassLoader、ExtClassLoader 和 AppClassLoader,它们之间的层级关系如图,上面的 ClassLoader 是下面 ClassLoader 的 parent.不同的 ClassLoader 有不同的职责:
- BootstrapClassLoader, 引导类加载器,加载JDK最核心的类,比如 rt.jar, java.lang.String, java.lang.Integer 这些类都在这个 JAR 中
- ExtClassLoader, 扩展类加载器,加载 jre/lib/ext 目录下的类
- AppClassLoader, 系统类加载器,加载应用程序 classpath 目录下的所有jar和class文件
如果想看 ClassLoader 具体加载了哪些类,可以 getURLs, 然后将它们打印出来
URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs(); URL[] urls = ((URLClassLoader)Thread.currentThread().getContextClassLoader()).getURLs();
应用程序也可以自己 创建 ClassLoader, 指定它的 parent, 指定它要加载的 类。那么,类的加载过程是怎样的呢,这里有个 双亲委派模型(parent delegation model) 的概念。即,某 ClassLoader XXX 加载一个类的时候,一直向上委托,直到最顶端,然后开始加载,如果顶端找不到该类,再依次往下,最后回到 XXX , 如果 XXX 自己还是找不到该类,就报 ClassNotFoundException . 双亲委派模型这种机制解决了什么问题呢,如果不用这种机制,用户可以自己定义一个JDK中的类(package也要一样)替换掉JDK中类。(这里有个问题,如果一个类已经被系统加载过了,再次加载的时候是会报错呢,还是以第一次加载的为准呢,还是以最后一次加载的为准呢?你这么聪明,肯定不用我说了)。上面大概就是 Java 自带 ClassLoader 的一些关键知识了,主要就是一个层级关系和双亲委托模型。
Tomcat的三大ClassLoader
So, 为什么 Tomcat 里要自定义 ClassLoader 呢,先来考虑一个问题:一个Tomcat 部署两个应用,App1 和 App2, App1 里定义了一个 com.fdx.AAA 类,App2 也定义了一个 com.fdx.AAA 类,但是里面的实现是不一样的,如果不自定义 ClassLoader,
而都用 AppClassLoader 来加载的话,你让它加载哪一个呢,一个 ClassLoader 是不能加载两个一样的类的。所以,ClassLoader 最重要的一个功能就是 类隔离。
接下来看看 Tomcat 是如何弄的. Bootstrap#initClassLoaders 方法中会去创建 commonLoader(对应配置common.loader)、catalinaLoader(对应配置server.loader) 和 sharedLoader(对应配置shared.loader),每个 loader 要加载哪些路径下载的类会在 conf/catalina.properties 中配置,默认配置是:
common.loader="${catalina.base}/lib","${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar"
server.loader=
shared.loader=
在server.loader和shared.loader为空的情况下,这三个其实是同一个 ClassLoader, 不为空的情况下,commonLoader是它们的parent. 所以我图中将它们画在了一个层级。
Tomcat的WebappClassLoader创建过程
WebappClassLoader 和 StandardContext 是一一对应的,StandardContext 和 应用又是一一对应的。每个 WebappClassLoader 只加载对应应用的类,这样应用之间的类就隔离了。来看 StandardContext 启动过程中的一段代码。
protected synchronized void startInternal()throwsLifecycleException{
if (getLoader() == null) {
WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
webappLoader.setDelegate(getDelegate());
setLoader(webappLoader);
}
ClassLoader oldCCL = bindThread();
Loader loader = getLoader();
if ((loader != null) && (loader instanceof Lifecycle))
((Lifecycle) loader).start();
unbindThread(oldCCL);
oldCCL = bindThread();
}
3、4、5 行 创建了一个 WebappLoader(这个并不是ClassLoader,它的作用是来管理WebappClassLoader),设置 delegate(delegate为true是,就是正常的加载顺序,为false,就不是标准的双亲委派了,而是WebappClassLoader自己先加载,加载不到再往上委派,默认是false)。11行启动了 WebappLoader, WebappLoader#startInternal 方法里会去创建 WebappClassLoader,设置它的 classPath, 安全权限,启动WebappClassLoader(Tomcat里基本上是个东西都实现了Lifecycle接口)。最后通过ubind, bind操作将当前线程的ClassLoader 设置为 WebappClassLoader。 WebappClassLoader的启动过程值得关注下,这里将应用的 /WEB-INF/classes 和 /WEB-INF/lib 加到了 localRepositories 中,这两个目录一个是打包后应用自身代码所在目录,一个是应用依赖的jar所以的目录。
public void start()throwsLifecycleException{
WebResource classes = resources.getResource("/WEB-INF/classes");
if (classes.isDirectory() && classes.canRead()) {
localRepositories.add(classes.getURL());
}
WebResource[] jars = resources.listResources("/WEB-INF/lib");
for (WebResource jar : jars) {
if (jar.getName().endsWith(".jar") && jar.isFile() && jar.canRead()) {
localRepositories.add(jar.getURL());
jarModificationTimes.put(
jar.getName(), Long.valueOf(jar.getLastModified()));
}
}
}
默认情况都是使用lWebappClassLoader, 在 Tomcat 8+ 中,还加入了 ParallelWebappClassLoader,可以并行的加载类。用并行加载,需要在conf/context.xml中配置loaderClass,但是一般情况下,这种需求不大,得要多大的项目才有那么多类需要并行来加载啊。关于并行类加载器,有兴趣可以看 Tomcat8+引入的并发ClassLoader
Tomcat的WebappClassLoader加载类的过程
WebappClassLoaderBase#loadClass 方法实现了整个加载过程,主要有以下操作
- 检查该类是否有被加载过,如果加载过了,直接返回
- 使用 system classLoader 加载该类,如果加载到了,直接返回。(这一步主要是为了防止应用实现JDK里的类)
- 用SecurityManager进行安全检查,如果不通过,就抛出ClassNotFoundException, 这个安全过滤机制,是可以配置的,后面有空在细说
- 接下来有两个分支,如果delegate是true, 则先用parent加载,加载不到再自己加载。如果是false,则自己先加载,加载不到再用parent加载
那么 WebappClassLoader 自己在加载类的时候是如何绕过双亲委派机制的呢,代码好多,说不清楚,自己看去吧。
最后,留个问题, 为什么Tomcat要打破这种双亲委派机制呢?
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Coming of Age in Second Life
Tom Boellstorff / Princeton University Press / 2008-04-21 / USD 29.95
The gap between the virtual and the physical, and its effect on the ideas of personhood and relationships, is the most interesting aspect of Boellstorff's analysis... Boellstorff's portrayal of a virt......一起来看看 《Coming of Age in Second Life》 这本书的介绍吧!