a我考网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 92|回复: 1

[其他] JAVA技巧:Java类别载入器分析(3)

[复制链接]
发表于 2012-8-4 12:28:23 | 显示全部楼层 |阅读模式
三个载入器的层次关系可通过运行下面的例子察看:1 g8 A& C; u& I3 e- y/ O
    public class Test
! z, G0 N* W6 c: [  u    {
' V, L2 N6 }$ U9 @. J8 Z& e2 b    public static void main(String[] args)
/ {: C7 }# c+ e2 R7 F    {
3 E4 ~& F1 M6 i- l1 z' }, P    ClassLoader cl1 = Test.class.getClassLoader();# {6 v6 J: X3 [
    System.out.println(cl1);; j2 T9 |# k1 U! v5 g# S$ t
    ClassLoader cl2 = cl1.getParent();
4 x3 {2 \( v8 I, C$ z. j    System.out.println(cl2);
1 M" }( V$ c3 f( H& Q+ u( A- K    ClassLoader cl3 = cl2.getParent();$ y8 {3 a$ Z: x4 h4 s9 b
    System.out.println(cl3);
1 ], m, h$ U/ R; Y, S, H    }
! `: ?# q+ R0 ^* C! Q* B    }
4 J, L2 ~% n! d* s* `* b    运行结果:
5 p' c+ H" W4 V1 v4 X$ Y    ////////////////////////////////////////////////////////////& F. z" H1 G. A/ I5 r: V
    sun.misc.Launcher$AppClassLoader@1a0c10f
- h8 s+ h% r+ k) |. Z: w. x    sun.misc.Launcher$ExtClassLoader@e2eec8
: B" y5 G5 X6 j/ u    null
3 L  X. U) f, `% \: a+ F9 {2 p    //////////////////////////////////////////////////////////
# S* e' _+ o6 Y    如果在上述程式中,如果您使用程式码:4 d, O; L/ w( T0 |8 D" j
    cl1.getClass.getClassLoader()及cl2.getClass.getClassLoader(),您会发现印出的都是null,: N2 R% B: Q- N1 A$ E
    这代表它们都是由Bootstrap Loader 所载入。这里也再次强调,类别载入器由谁载入(这句话有点
. ]% d- ?' Y$ h* f$ H. c( W    诡异,类别载入器也要由类别载入器载入,这是因为除了Bootstrap Loader 之外,其余的类别载1 K  p" R/ e9 q! w+ X: V
    入器皆是由Java 撰写而成),和它的Parent 是谁没有关系,Parent 的存在只是为了某些特殊目的,
* N# F, U: D5 ?+ w* o& R( M    这个目的我们将在稍後作解释。
$ j% U- H5 t0 J- w* i+ N6 c    在此要请大家注意的是,AppClassLoader 和ExtClassLoader 都是URLClassLoader 的子类别。
7 C! o# K0 p: O/ S' m8 k    由於它们都是URLClassLoader 的子类别,所以它们也应该有URL 作为搜寻类别档的参考,由原始码2 ~2 m$ s+ @% X( w
    中我们可以得知,AppClassLoader 所参考的URL 是从系统参数java.class.path 取出的字串所决定,
* I( e0 S! p$ b6 `    而java.class.path 则是由我们在执行java.exe 时,利用–cp 或-classpath 或CLASSPATH 环境变: k7 k* m4 h' X9 H$ F% n- L
    数所决定。% L+ h- ~7 ~# n4 V6 b+ N1 U0 A
    用如下示例测试:4 H( u8 c* H$ G' J' c& R
    public class AppLoader
" x6 Z$ f- T6 p8 ~. z, J    {
* T- S7 |3 l- n    public static void main(String[] args)7 d% U2 `: K2 n# y( P5 M
    {$ E& o, v) k4 L- W& i6 b- j) C- }6 f$ q3 k
    String s = System.getProperty("java.class.path");
- `# W2 f- q2 {2 s4 f# C- U    System.out.println(s);1 V9 t% r7 T5 c) E! N7 }$ K
    }
4 h! r  J4 X* @# X    }
, u% W' |/ S! D2 J+ f    /////////////////////////////////////////////////////////////////! Q9 {; S- e- i7 X
    D:\myapp\classload>java AppLoader9 s5 L) \8 e9 L: k1 s: g
    .;D:\myjava\Tomcat5.0\webapps\axis\WEB-INF\lib\axis.jar;D:\myjava\Tomcat5.0\weba! T8 {4 [6 J7 k) u
    pps\axis\WEB-INF\lib\commons-logging.jar;D:\myjava\Tomcat5.0\webapps\axis\WEB-IN* T; w( m7 `2 L- c+ C! W5 _/ t
    F\lib\commons-discovery.jar;C:\oracle\ora81\jdbc\lib\classes12.zip;D:\myjava\JDB
# A2 J/ R; \7 j: @8 h    CforSQLserver\lib\mssqlserver.jar;D:\myjava\JDBCforSQLserver\lib\msbase.jar;D:\m
/ O# r* I6 l9 S/ ]$ Z    yjava\JDBCforSQLserver\lib\msutil.jar;D:\myjava\Tomcat5.0\common\lib\servlet-api! T! q% t- O$ g  C
    .jar;D:\myjava\j2sdk1.4.2_04\jre\lib\rt.jar;C:\sun\appserver\lib\j2ee.jar;D:\myj, G! p; \4 f! V3 l  B, [
    ava\j2sdk1.4.2_04\lib\jaxp.jar;D:\myjava\j2sdk1.4.2_04\lib\sax.jar;$ m& R& M; P3 y  V
    D:\myapp\classload>java -classpath .;d:\myapp AppLoader( f. l2 {# G( s; x- g! B+ @# V+ e
    .;d:\myapp( ^3 r; Z0 W5 c- w
    /////////////////////////////////////////////////////////////////
+ U. t8 k! E! X* R    从这个输出结果,我们可以看出,在预设情况下,AppClassLoader 的搜寻路径为”.”(目前所在目
7 U( n6 @" E. J: V: {1 ?7 y" ~    录),如果使用-classpath 选项(与-cp 等效),就可以改变AppClassLoader 的搜寻路径,如果没有
( g7 s8 ^/ Z; q( K  N  C$ }8 l9 s    指定-classpath 选项,就会搜寻环境变数CLASSPATH。如果同时有CLASSPATH 的环境设定与/ R+ ~3 J4 g+ {8 x% H2 r
    -classpath 选项,则以-classpath 选项的内容为主,CLASSPATH 的环境设定与-classpath 选项两者3 m) {1 T% B2 u" p/ u2 W/ X+ f
    的内容不会有加成的效果。
" E: h  k9 c4 g  x0 ~. `9 S    至於ExtClassLoader 也有相同的情形,不过其搜寻路径是参考系统参数java.ext.dirs。$ v( x& J& o, I! \. h4 f0 `1 y
    系统参数java.ext.dirs 的内容,会指向java.exe 所选择的JRE 所在位置下的\lib\ext 子目录。Java.exe使用的JRE是在系统变量path里指定的,可以通过修改path从而修改ExtCLassLoader的搜寻路径,也可以如下命令参数来更改,, y. _" y# ~5 Y. S6 n8 q" t
    java –Djava.ext.dirs=c:\winnt\ AppLoader //注意 =号两边不能有空格。-D也不能和java分开。
# K7 u1 k0 h* o. C: X    ////////////////////////////////////////////////////////////////  _* l% F' ?( E# q6 P
    D:\myapp\classload>java ExtLoader
/ L2 C! \8 j! r6 o& B6 }    D:\myjava\j2sdk1.4.2_04\jre\lib\ext/ ]5 F. A% U! Q6 M) Y
    D:\myapp\classload>java -Djava.ext.dirs=c:\winnt\ ExtLoader8 ]1 }1 l+ f3 i& I8 A
    c:\winnt\+ s9 a, m5 `  @4 I
    ////////////////////////////////////////////////////////////////
回复

使用道具 举报

 楼主| 发表于 2012-8-4 12:28:24 | 显示全部楼层

JAVA技巧:Java类别载入器分析(3)

最後一个类别载入器是Bootstrap Loader , 我们可以经由查询由系统参数sun.boot.class.path 得知Bootstrap Loader 用来搜寻类别的路径。该路径的修改与ExtClassLoader的相同。但修改后不影响Bootstrap的搜寻路径。
0 I' k, U8 |/ W* T6 j    在命令列下参数时,使用–classpath / -cp / 环境变数CLASSPATH 来更改AppClassLoader! |# G( Q# X- A
    的搜寻路径,或者用–Djava.ext.dirs 来改变ExtClassLoader 的搜寻目录,两者都是有意义的。
. @, X: a4 A1 [* r6 z1 `. J2 j0 f    可是用–Dsun.boot.class.path 来改变Bootstrap Loader 的搜寻路径是无效。这是因为, x$ I- Y! L% U& k5 U  s  y' ~
    AppClassLoader 与ExtClassLoader 都是各自参考这两个系统参数的内容而建立,当您在命令列下
% s. u9 s0 x0 v4 |" c    变更这两个系统参数之後, AppClassLoader 与ExtClassLoader 在建立实体的时候会参考这两个系0 n1 E! k' U& x5 g/ s) S
    统参数,因而改变了它们搜寻类别档的路径;而系统参数sun.boot.class.path 则是预设与
, s$ l/ D, j7 ~1 Z# J8 ]    Bootstrap Loader 的搜寻路径相同,就算您更改该系统参与,与Bootstrap Loader 完全无关。
0 H  \- D* O9 O' m- w- i5 K    改变java.exe所使用的jre会改变Bootstrap Loader的搜寻路径。: H' R  |3 {( _$ E0 v
    Bootstrap Loader的搜寻路径一般如下:
( w5 z0 z) J& H; ~8 B    ///////////////////////////////////////////////////////////////////////////////////
& a7 o: P* o: |! \9 g' R) I0 X    D:\myjava\j2sdk1.4.2_04\jre\lib\rt.jar;D:\myjava\j2sdk1.4.2_04\jre\lib\i18n.jar;# Z# b$ }6 }. f6 O. h! I7 U, `
    D:\myjava\j2sdk1.4.2_04\jre\lib\sunrsasign.jar;D:\myjava\j2sdk1.4.2_04\jre\lib\j( {( I; |7 \% X9 `
    sse.jar;D:\myjava\j2sdk1.4.2_04\jre\lib\jce.jar;D:\myjava\j2sdk1.4.2_04\jre\lib\
, ?) X3 C* I; d$ f# g3 X    charsets.jar;D:\myjava\j2sdk1.4.2_04\jre\classes
5 {7 x# g; P$ A7 t    ///////////////////////////////////////////////////////////////////////////////////////  }8 D" t( ^. I. C2 n
    更重要的是,AppClassLoader 与ExtClassLoader 在整个虚拟机器之中只会存有一份,一旦建
( d. |3 ]8 ]7 ?, K2 J, V( A    立了,其内部所参考的搜寻路径将不再改变,也就是说,即使我们在程式里利用System.setProperty()
4 {, p5 R0 a* M% O    来改变系统参数的内容,仍然无法更动AppClassLoader 与ExtClassLoader 的搜寻路径。因此,执
- a7 _: G3 N, Z. S( E, K9 r    行时期动态更改搜寻路径的设定是不可能的事情。如果因为特殊需求,有些类别的所在路径并非在
+ E: z' E7 M2 ?' r    一开始时就能决定,那麽除了产生新的类别载入器来辅助我们载入所需的类别之外,没有其他方法了。' X1 f( V* c' J" y3 B+ r0 r) \1 \
    下面我们将看一下载入器的委派模型
# F6 W# \2 j8 L% k    所谓的委派模型,用简单的话来讲,就是「类别载入器有载入类别的需求时,会先请示其Parent 使用其搜寻路径帮忙载入,如果Parent 找不到,那麽才由自己依照自己的搜寻路径搜寻类别」。$ A9 X; Q5 L8 T! k! p& Q
    下面我们看一下小的示例:2 a7 S. F8 V  [0 `3 Q( C# q% _
    public class Test
% l9 q* O* E2 V2 H2 n1 j    {. B# P1 q1 R* c8 l+ E
    public static void main(String[] args)
: a9 O1 H) _% c# n4 ^6 p3 H) ?    {
  Z  A2 o( c0 w4 w( l3 _; K    System.out.println(Test.class.getClassLoader());2 B7 \4 `7 q/ w: Q& Q& H8 k% M
    TestLib tl = new TestLib();7 P# K& z6 t: @6 }) {. [1 M
    tl.start();
) i; J% K& X' \) k1 n$ e- p) m    }
! k  e, W- H3 t$ B, u4 d    }
; Z% [; K9 c. c& [0 C    public class TestLib, h& }  e; h1 Z2 U1 J: A/ G( J
    {  l8 y+ I- N: R: p- }
    public void start()" a2 Q( t$ w) q% _- ?4 j$ W5 S
    {3 g0 B* L7 G/ d
    System.out.println(this.getClass().getClassLoader());
% W9 O; y! y* J9 O+ ?+ s  W    }3 O4 |* ]. T$ p
    }
; U5 I) ^* p# f3 D' c1 D6 x    如果这两个类仅放在dos命令提示符的当前目录下,则输出结果如下:: R0 {8 ?9 v. j8 D6 K! K5 T
    //////////////////////////////////////////////////////
; v' U1 U5 h7 e2 ]    sun.misc.Launcher$AppClassLoader@1a0c10f/ z% @, r4 z( t' M- l$ H5 M
    sun.misc.Launcher$AppClassLoader@1a0c10f
1 W+ L+ q3 D" e' ~    //////////////////////////////////////////////////////
& m( ^2 ?! M1 y% k    如果这两个类同时又放在\lib\ext\classes 底下(在我的机器上是:D:\myjava\j2sdk1.4.2_04\jre\lib\ext\classes,classes没有,需要自己建),输出结果如下:
3 |5 v. B" d* d    /////////////////////////////////7 A7 c1 Z( \. g
    sun.misc.Launcher$ExtClassLoader@e2eec8% B" f# K- X) y! r0 G4 U
    sun.misc.Launcher$ExtClassLoader@e2eec8' S, T/ e& Z% h2 i( k9 s, q* d( \
    ////////////////////////////////////# `* w: ]9 k" `
    最后如果在\classes下放入这两个类,则输出结果为8 o. L( ?1 A1 Y$ U+ y. r
    /////////////////////////////////
- ?2 i# S" o; n1 v    null3 J- V6 x" h& Q
    null% _  E  x- `, `# {8 L1 ]% T# g2 l
    ////////////////////////////////////5 s: Y' t$ V. q+ N2 W
    如果把\classes下的TestLib删去,则输出入下:
. K- b, C7 n! c& M7 {  w8 Z    //////////////////////////////////////
" b; S7 N" M9 j* Q* d    null
" c( W) E& x: \( v  [( T+ x: B! y    Exception in thread "main" java.lang.NoClassDefFoundError: TestLib5 R1 B' Z2 Z+ g' t
    at Test.main(Test.java:7)3 c: w/ `) `6 N) W- [6 k
    //////////////////////////////////////
" q' Z. Q7 {  `; X! T6 ]    这是因为Test的classLoader是Bootstrap Loader ,因此TestLib的也默认为是Bootstrap Loader。Bootstrap Loader搜寻路径下的TestLib被删去了,Bootstrap Loader又没有parent,所以提示找不到。* M0 K, b: W0 j& v  L
    其他的情况可以自己逐个添加或删除文件,然后执行java Test进行测试,察看输出结果。3 o3 N- ~$ D' o
    AppClassLoader 与Bootstrap Loader会搜寻它们所指定的位置(或JAR 档),如果找不到就找不到了,AppClassLoader 与Bootstrap Loader不会递回式地搜寻这些位置下的其他路径或其他没有被指定的JAR 档。反观ExtClassLoader,所参考的系统参数是java.ext.dirs,意思是说,他会搜寻底下的所有JAR 档以及classes 目录,作为其搜寻路径。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Woexam.Com ( 湘ICP备18023104号 )

GMT+8, 2024-5-22 04:03 , Processed in 0.212343 second(s), 23 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表