|
锁定老贴子 主题:class loader的问题
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
|---|---|
| 作者 | 正文 |
|
最后更新时间:2005-07-09
一般来说,ClassLoader.loadClass会先delegate给parent来查找某个类,如果parent找到了这个类,就会把这个类直接返回。
但是,这个加载的类的getClassLoader()就会是那个parent, 随后,如果这个类里面的代码要用Class.forName加载别的类,就会使用这个parent classloader。 这个父子关系在我看来有点奇怪,不太符合一般OO意义上的父子关系(儿子可以override父亲,儿子不行了才去找老爸) 在实际中,我发现调用ant就有这个问题,我的子classloader L1 本来是可以找到某个类的,但是因为先load了一个父亲L2可以找到的类B,在这个插在中间的类B的代码中,要load类C,而父亲L2找不到类C,只有L1能找到 画个关系图: L1 <: L2 (L1是L2的儿子) L1: B, C(L1可以加载B, C) L2: B (L2只能加载B,而找不到C) B -> C (B的代码试图加载C) 现在,我从L1加载B,L1虽然自己可以找到B,但是还是先交给L2来找, L2找到了B,把B的getClassLoader()设置成L2。 下面调用B.getC(),B会用它自己的class loader,也就是L2来加载C。 可惜L2找不到C,而能找到C的L1却被丢掉了。 怎么回事呢?是不是我有什么地方弄错了? 声明:JavaEye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
|
|
| 返回顶楼 | |
|
最后更新时间:2005-07-09
ajoo 写道 一般来说,ClassLoader.loadClass会先delegate给parent来查找某个类,如果parent找到了这个类,就会把这个类直接返回。
但是,这个加载的类的getClassLoader()就会是那个parent, 随后,如果这个类里面的代码要用Class.forName加载别的类,就会使用这个parent classloader。 这个父子关系在我看来有点奇怪,不太符合一般OO意义上的父子关系(儿子可以override父亲,儿子不行了才去找老爸) 在实际中,我发现调用ant就有这个问题,我的子classloader L1 本来是可以找到某个类的,但是因为先load了一个父亲L2可以找到的类B,在这个插在中间的类B的代码中,要load类C,而父亲L2找不到类C,只有L1能找到 画个关系图: L1 <: L2 (L1是L2的儿子) L1: B, C(L1可以加载B, C) L2: B (L2只能加载B,而找不到C) B -> C (B的代码试图加载C) 现在,我从L1加载B,L1虽然自己可以找到B,但是还是先交给L2来找, L2找到了B,把B的getClassLoader()设置成L2。 下面调用B.getC(),B会用它自己的class loader,也就是L2来加载C。 可惜L2找不到C,而能找到C的L1却被丢掉了。 怎么回事呢?是不是我有什么地方弄错了? 嗯,是该到了少谈些主义,多解决些问题的时候啦! 要解决这个问题的办法有几个: 1) 避免L1所有上一级加载器找到 B 2) 修改L1的实现,对于你的应用类,首先从L1装载。 |
|
| 返回顶楼 | |
|
最后更新时间:2005-07-09
ajoo 写道 画个关系图: L1 <: L2 (L1是L2的儿子) L1: B, C(L1可以加载B, C) L2: B (L2只能加载B,而找不到C) B -> C (B的代码试图加载C) 现在,我从L1加载B,L1虽然自己可以找到B,但是还是先交给L2来找, L2找到了B,把B的getClassLoader()设置成L2。 下面调用B.getC(),B会用它自己的class loader,也就是L2来加载C。 可惜L2找不到C,而能找到C的L1却被丢掉了。 怎么回事呢?是不是我有什么地方弄错了? 你没有错,现象的确如你所说。我在用WebLogic Server遇到过同样的问题。 WLS中每个Ear部署会有独立的Classloader,按照你的说法就是Ear用L1,WLS用L2。 因为WLS附带了一些开源软件,但你自己的EAR也用到这些开源软件的话,就必须和WLS的版本一致。因为尽管你把这些软件打包在自己的Ear中,依然会使用WLS的版本。 但是这样的好处是对于一些class(只要不是WLS自身附带的),不同的Ear可以用不同的版本。 firebody 写道 要解决这个问题的办法有几个:
1) 避免L1所有上一级加载器找到 B 2) 修改L1的实现,对于你的应用类,首先从L1装载。 基本上也就是这两个方法了 |
|
| 返回顶楼 | |
|
最后更新时间:2005-07-09
修改L1比较有吸引力。不过我不想真改代码。继承把
[code:1]class MyClassLoader extends URLClassLoader{ public Class loadClass(String name){ try{ return findClass(name); } catch(ClassNotFoundException e){ return super.loadClass(name);//????????? } } }[/code:1] 问题是,在调用super.loadClass的时候,它如果找不到,仍然会调用findClass(),这不是重复做无用功吗? 有没有办法解决? |
|
| 返回顶楼 | |
|
最后更新时间:2005-07-09
失败。实现了一个,结果找不到class Object。似乎没法先在儿子处查找。
有办法吗? |
|
| 返回顶楼 | |
|
最后更新时间:2005-07-10
ajoo 写道 失败。实现了一个,结果找不到class Object。似乎没法先在儿子处查找。
有办法吗? 最好是这样 ,除了你自己写得应用类 ,其它java.lang ,以及 别的 类都 通过 上一级装载器来装载 ! 具体的实现可以 参照CGLIB或者 别的 项目 的 扩展 ClassLoader的例子 。你 只要保证你的应用类的 当前装载器 是 你的 自定义装载器即可 。 |
|
| 返回顶楼 | |
|
最后更新时间:2005-07-11
ajoo 写道 一般来说,ClassLoader.loadClass会先delegate给parent来查找某个类,如果parent找到了这个类,就会把这个类直接返回。
但是,这个加载的类的getClassLoader()就会是那个parent, 随后,如果这个类里面的代码要用Class.forName加载别的类,就会使用这个parent classloader。 这个父子关系在我看来有点奇怪,不太符合一般OO意义上的父子关系(儿子可以override父亲,儿子不行了才去找老爸) 在实际中,我发现调用ant就有这个问题,我的子classloader L1 本来是可以找到某个类的,但是因为先load了一个父亲L2可以找到的类B,在这个插在中间的类B的代码中,要load类C,而父亲L2找不到类C,只有L1能找到 画个关系图: L1 <: L2 (L1是L2的儿子) L1: B, C(L1可以加载B, C) L2: B (L2只能加载B,而找不到C) B -> C (B的代码试图加载C) 现在,我从L1加载B,L1虽然自己可以找到B,但是还是先交给L2来找, L2找到了B,把B的getClassLoader()设置成L2。 下面调用B.getC(),B会用它自己的class loader,也就是L2来加载C。 可惜L2找不到C,而能找到C的L1却被丢掉了。 怎么回事呢?是不是我有什么地方弄错了? 這個關係的確不是 OO 意義上的父子關係,這麼設計是為了安全性的考量。Java 為了動態性所以把 class loader 設計成階層式,但如果 class loader 不採用這種方式的話,如果 B, C 是我寫的程式,如果 B 要 load C 是使用 L1 的話,任何人都可以外加 class loader 改變 B 所 load 到的 C ,進而替換成惡意的 C ,ex 改變 app server 的行為。 但這個規則好像也只是建議而已,所以如果你能修改 L1 的話,就可以先讓 L1 load B ,這樣 B 就 load 得到 C 了。但 java 開頭的 class 你根本就不能另外 load ,而如果是繼承自 URLClassLoader 還會有 sealed jar 的限制。 |
|
| 返回顶楼 | |
|
最后更新时间:2005-07-11
wctang 写道 這個關係的確不是 OO 意義上的父子關係,這麼設計是為了安全性的考量。Java 為了動態性所以把 class loader 設計成階層式,但如果 class loader 不採用這種方式的話,如果 B, C 是我寫的程式,如果 B 要 load C 是使用 L1 的話,任何人都可以外加 class loader 改變 B 所 load 到的 C ,進而替換成惡意的 C ,ex 改變 app server 的行為。 我感觉这儿安全方面应该是最大的考量,不然我们很容易改变核心类的了...... 引用 类加载器是沙箱的第一道防线,毕竟代码都是由它装入jvm中的,其中也包括有危险的代码。它的安全作用有三点: 一 保护善意代码不受恶意代码的干扰 二 保护已验证的类库 三 代码放入有不同的行为限制的各个保护域中 类加载体系通过使用不同的类加载器把类放入不同的名字空间中从而保护善意代码不受恶意代码的干扰。 |
|
| 返回顶楼 | |
|
最后更新时间:2005-07-11
我上次也被这个类加载郁闷了一把
我在一次用EditPlus写了个JasperReport的简单测试类,因为要用JasperReport包,就随便把相关的jar放到jdk\jre\lib\ext 下,测试完之后没有删除JasperReport的包,过了一天,我IDEA下的工程一运行报表,就说找不到Apache commons 的类,这些jar都在工程底下,偏偏就是加载不了,搞的我模不着头脑,最后才想起来,从ext 下删掉jasper的jar,一下就OK了. jar 放在jdk的ext下,它是由启动类加载器加载的,其他的apache的commoms在工程的lib下是由应用程序类加载器加载的,Jasper需要用到commons的类它只会到当前类加载器或者上级类加载器中找,如果找不到就会抛出类找不到的异常了. PS:类加载器的名字记不太清楚了.. |
|
| 返回顶楼 | |
|
最后更新时间:2005-07-15
有道理,但还是不太明白,望指教。
|
|
| 返回顶楼 | |












