Resin服务器getResource揭秘
(感谢网友 liuxiaori 继续分享其经历)这样的详细的图文并茂的文章让我很佩服!
目录
前言调试环境开始1) this.getClass().getResource(“/”).getPath()1) this.getClass().getResourceAsStream(“/a.txt”)总结
前言
接上文“由一个问题到Resin ClassLoader的学习”,本文将以this.getClass().getResource(“/”).getPath()和this.getClass().getResourceAsStream(“/a.txt”)为例,一步步解析加载的过程。
调试环境
下载resin3.0.23的源码(http://www.caucho.com/download/resin-3.0.23-src.zip)。
部署到myeclipse中,有错误,本人忽略了。Resin可运行。
将EhCacheTestAnnotation部署到resin3.0.23中。
调试this.getClass().getResource(“/”).getPath()。
问题来了,无论如何也模拟不出来
开始
1) this.getClass().getResource(“/”).getPath()
本次调试涉及的所有类加载器为:
EnvironmentClassLoader$24156236[web-app:http://localhost:8787/EhCacheTestAnnotation]
EnvironmentClassLoader$7806641[host:http://localhost:8787]
EnvironmentClassLoader$22459270[servlet-server:]
[email protected]
[email protected]
首先进入Class的getResource(String name)方法,如下图:
图1
最后委托给ClassLoader的getResource方法。那么这个ClassLoader是哪个呢?一看下图便知:
图2
是DynamicClassLoader的getResource方法,原理上文已述。
最终会委托给[email protected]类加载器的getResource方法,返回null,然后开始回溯。
还记得吗?当java.net.URLClassLoader分支的ClassLoader的getResource方法返回值为null后,就要遍历嵌入DynamicClassLoader中的Resin的Loader(即_loaders集合)。
当然回溯到EnvironmentClassLoader$22459270[servlet-server:]中,那么它中_loaders这个集合中的Loader又有哪些呢?
以图为证,当天确实回溯到该ClassLoader,而且开始准备遍历_loaders集合。
图3
DynamicClassLoader的1306行,没问题,resin3.0.23源码截图为证:
图4
不做多余解释,那么“servlet-server”这个ClassLoader中的_loaders集合中都放了一些什么呢?
图5
存放了两个TreeLoader(Loader的子类),然未找到结果,返回null。继续回溯。
这次轮到遍历EnvironmentClassLoader$7806641[host:http://localhost:8787]的_loaders。下图为证:
图6
_loaders中的内容如下图:
图7
比较长,我贴出来:
[CompilingLoader[src:/D:/work/resin-3.0.23/webapps/WEB-INF/classes], LibraryLoader[[email protected]], CompilingLoader[src:/D:/work/resin-3.0.23/webapps/WEB-INF/classes], LibraryLoader[[email protected]], CompilingLoader[src:/D:/work/resin-3.0.23/webapps/WEB-INF/classes], LibraryLoader[[email protected]]]
注意到了吧,主角来了。那仔细调试下把。爆料一下:CompilingLoader[src:/D:/work/resin-3.0.23/webapps/WEB-INF/classes]就是主角。
图8
看到了吧,遍历时,当前的Loader为CompilingLoader[src:/D:/work/resin-3.0.23/webapps/WEB-INF/classes],而且url可是不为null了哦。再贴一张,看看url的值到底是什么!
图9
嗯,不用多做解释了吧。
最后看看程序输出是否吻合,如下图:
图10
然后修改resin.conf中的
图11
为节省篇幅,一下只关注关键位置。
首先调试到EnvironmentClassLoader$7806641[host:http://localhost:8787],我们需要停下来一下。
图12
再看一下_loaders的值。
图13
贴一个详细的:
[LibraryLoader[[email protected]], LibraryLoader[[email protected]], LibraryLoader[[email protected]]]
对比一下,在注释掉
继续,下面就轮到EnvironmentClassLoader$24156236[web-app:http://localhost:8787/EhCacheTestAnnotation]这个ClassLoader了,会是什么样子呢?
图14
进入该ClassLoader时,url值依旧为null,那_loaders会有变化吗?如下图:
图15
继续遍历_loaders。
图16
到这里就结束了,url在EnvironmentClassLoader$24156236[web-app:http://localhost:8787/EhCacheTestAnnotation]中被加载。
1) this.getClass().getResourceAsStream(“/a.txt”)
getResourceAsStream(String name)方法也是采用双亲委派的方式。在前一篇文章中提出“getResourceAsStream可是将获取路径委托给getResource,
ClassLoader中getResourceAsStream源码也确实是委托为getResource了,可是为什么呢?
getResourceAsStream(String name)方法。
public InputStream getResourceAsStream(String name) {
URL url = getResource(name);
try {
return url != null ? url.openStream() : null;
} catch (IOException e) {
return null;
}
}
其实不难解释,JVM中ClassLoader的getResourceAsStream(“/a.txt”)返回了null,然后开始回溯,与getResource方法的原理一致,直到某个ClassLoader及其子类或者Loader及其子类找到了”/a.txt”,并以流的形式返回,当然谁都没找到就返回null。
捡重点的说。
调试到[email protected],即ClassLoader的子类,情形如下图:
图17
看见getResource(name)喽,按F5进去看个究竟。如下图,其parent为:[email protected],其返回null。
图18
开始回溯到:EnvironmentClassLoader$1497769[servlet-server:],与getResource方法一致,开始遍历_loaders集合。
这样就可以解释为何
比如,下图所示:
图19
在加载”/a.txt”时,优先加载webapps/WEB-INF/classes/a.txt。
总结
EnvironmentClassLoader$7806641[host:http://localhost:8787]为Resin server的类加载器实例,EnvironmentClassLoader$24156236[web-app:http://localhost:8787/EhCacheTestAnnotation]为Web应用程序的类加载器实例。他们都属于java.net.URLClassLoader的实例。
现在
注:
转载于酷壳CoolShell 无删改 仅以此纪念陈皓(左耳朵耗子)
各位朋友,又到了介绍各种杂项的时候了,正如以前的这篇和这篇文章一样,本篇文章也给你介绍一些最近出现的一些有趣的东西。希望你能喜欢。 首先是华尔街的一篇报道,2011年最好和最…
这个是一本关于CORBA技术的中文文档,原文是Ciaran McHale《CORBA Explained Simply》,我将其翻译成中文形式,并首发在酷壳之上,现在先提供一个…
由于最近在折腾邮件服务器,正好想测试一下自己以前注册的其他几个邮箱。 发现 outlook 邮箱在发信时,居然可以把发信人设置为和 outlook 帐号关联的邮箱帐号。 比如在…