如下两个包是同一个代码打包的,执行里面的主类io.github.kimmking.Main分别输出v0.0.1版本和v0.0.2版本。
-
versions-0.0.1-SNAPSHOT.jar
-
versions-0.0.2-SNAPSHOT.jar
$ java -cp versions-0.0.1-SNAPSHOT.jar:versions-0.0.2-SNAPSHOT.jar io.github.kimmking.Main
v0.0.1
$ java -cp versions-0.0.2-SNAPSHOT.jar:versions-0.0.1-SNAPSHOT.jar io.github.kimmking.Main
v0.0.2
这就说明了,谁放在classpath的前面就会执行谁。
$ java -cp versions-0.0.1-SNAPSHOT.jar io.github.kimmking.Main
v0.0.1
$ mkdir /tmp/1 && cp versions-0.0.2-SNAPSHOT.jar /tmp/1
$ java -cp versions-0.0.1-SNAPSHOT.jar -Djava.ext.dirs=/tmp/1 io.github.kimmking.Main
v0.0.2
这就说明了,通过java.ext.dirs指定的扩展类加载器加载的优先级比较高。
如果我们在代码里加上打印类的classloader,就会发现:
$java -cp versions-0.0.1-SNAPSHOT.jar io.github.kimmking.Main
sun.misc.Launcher$AppClassLoader@4e25154f
v0.0.1
$java -cp versions-0.0.1-SNAPSHOT.jar -Djava.ext.dirs=/tmp/1 io.github.kimmking.Main
sun.misc.Launcher$ExtClassLoader@7852e922
v0.0.2
这就验证了确实是通过ExtClassLoader加载的。
使用场景:做外挂包机制的两种方式:
- 通过把外挂包所在文件夹里的所有jar,拼成字符串,放到classpath的前面。 app classloader加载的。
- 通过java.ext.dirs指定扩展类加载器加载的jar。
- 运行期通过反射,在appClassloader.ucp.path这个ArrayList的最前面加上我们自定义的jar的URL。(必须在要被覆盖的类被加载之前做这个事儿。)
两个可行办法:好的后面都没有问题我就接着讲下一个