ClassLoader与资源文件加载

深刻理解java的配置环境以及java的执行过程对做好开发是十分重要的。
类加载器ClassLoader 便是其中非常重要的概念。

本文简单并演示Java类加载器的一些特点,不妥之处,敬请指出。

背景:
A.java 引用 了B.jar 中的一个文件,B.jar 包中的C.class 使用了C.class.getResourceAsStream("/jdbc.xml")获取资源文件。 B.jar 做一个独立的功能的工具,使用方只要提供对应的jdbc.xml 配置文件即可在dev qa prod 各个环境使用啦。
但是问题来了,在web下和java命令行情况下,jdbc.xml 文件放置的位置大不相同,因为web 和java命令下 B.jar 中加载C.class的类加载器不同!


类加载器的分类:
1、启动类加载器(Bootstrap ClassLoader) 这个类有c++语言实现,是虚拟机的一部分。
2、所有其他类加载器。其他类加载器都继承抽象类java.lang.ClassLoader。


做为java的大部分项目,都是有以下三类类加载器加载的:
1、启动类加载器。加载的内容为:<JAVA_HOME/lib 目录中的jar包。
2、扩展类加载器。加载的内容为:java.ext.dirs 系统变量指定的路径中所有类库。实现为:sun.misc.Launcher$ExtClassLoader 实现。
3、应用类加载器。实现为:sun.misc.Launcher$AppClassLoader。负责加载用户类路径上指定的类库。

下面我们看个例子:


package org.job.user;

import org.apache.commons.lang.StringUtils;

public class Test {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getContextClassLoader());
System.out.println(StringUtils.class.getClassLoader());

System.out.println(StringUtils.class.getResource("/"));
System.out.println(Test.class.getResource("/"));

System.out.println(StringUtils.class.getResourceAsStream("/jdbc.xml"));
System.out.println(Test.class.getResourceAsStream("/jdbc.xml"));
}
}



此时如果大家到Test.java 所在的目录,执行javac Test.java 会报错:
[img]http://dl2.iteye.com/upload/attachment/0098/7781/ec7b61a5-5a40-3248-a431-9c1a47baff0d.png[/img]

为什会报错,因为org.apache.commons.lang.StringUtils 找不到,当前AppClassLoader 找不到StringUtils 类的路径。

找到StringUtils 可以通过指定-classpath .;D:\DevPlatform\m2localRepository\commons-lang\commons-lang\2.5\commons-lang-2.5.jar 来找到。
[img]http://dl2.iteye.com/upload/attachment/0098/7783/9fb78ff4-a4a8-3d99-992c-ea9bc1343514.png[/img]

大家注意到输出:
sun.misc.Launcher$AppClassLoader@19821f
sun.misc.Launcher$AppClassLoader@19821f
file:/E:/spring/spring-aop-aspectj/target/test-classes/
file:/E:/spring/spring-aop-aspectj/target/test-classes/
java.io.BufferedInputStream@1fb8ee3
java.io.BufferedInputStream@61de33
Test.class 和StringUtils.class 都是通过同一个类加载器(AppClassLoader)加载的。另外 通过Test.class.getResourceAsStream("/jdbc.xml") 找到了jdbc.xml 文件,jdbc.xml 文件在E:\spring\spring-aop-aspectj\src\test\java 也即是我执行java 和 javac 的位置。
这说明:AppClassLoader 的根目录在执行环境的目录。


下一步我们演示通过ExtClassLoader 加载,即通过-Djava.ext.dirs=D:\DevPlatform\m2localRepository\commons-lang\commons-lang\2.5 来加载commons-lang-2.5.jar包。

[img]http://dl2.iteye.com/upload/attachment/0098/7787/5265a039-abb6-3cb7-a8d6-0e3f3a52c891.png[/img]

从上面的输出我们看到Test.class 和 StringUtils.class 的类加载器不同了,另外 System.out.println(StringUtils.class.getResourceAsStream("/jdbc.xml")); //没有找到对应的资源文件
System.out.println(Test.class.getResourceAsStream("/jdbc.xml")) //找到了对应的资源文件

为什么StringUtils.class 找不到对应的资源文件了,因为getResourceAsStream 是通过委托对应的类加载器来加载的,"/jdbc.xml" 通过不同的类加载器来加载,对于的资源文件的位置是不同的。

我在commons-lang-2.5.jar 包所在目录创建com 并在其下建立jdbc.xml 文件,继续运行结果如下:

[img]http://dl2.iteye.com/upload/attachment/0098/7789/4cadbbd1-9a83-3c65-9ed1-a127cc074b16.png[/img]

看到上面的输出,一切都不言而喻。

总结:
对于Class.getResourceAsStream("/*.*") 是通过委托给对应的ClassLoader来加载的,其对应的资源文件的根目录为:
ExtClassLoader: 为java.ext.dirs 指定的目录下的所以目录。优先使用相应jar包的路径对应的根目录。比如java.ext.dirs=/home/q/,那么其根目录为/home/q/*dir。

AppClassLoader: 执行java javac 命令的目录。

注意:AppClassLoader寻找跟资源文件优先使用java.ext.dirs 指定的资源文件,如果没有才会到 命令执行的目录下寻找。 另外java.ext.dirs 指定的资源文件 最好不要有多个重名的文件,否则将不确定使用的是哪一个。
验证留给大家吧
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值