Class类和ClassLoader类下的同名方法getResourceAsStream

在读取本地资源的时候我们经常需要用到输入流,典型的场景就是使用Druid连接池时读取连接池的配置文件。Java为我们提供了读取资源的方法getResourceAsStream(),该方法有三种:

  • Class类中的
  • ClassLoader类中的
  • ServletContext接口中的

本文主要利用Class类和ClassLoader类中的方法进行对比

一、API

  • Class类中的:

1644205-20190427221346561-1145627540.jpg

根据指定的名称查找资源,查找资源的规则是由定义此类的类加载器实现的,这个方法委托此对象的类加载器.如果这个对象是由bootstrap加载器加载的,这个方法就委托给ClassLoader.getSystemResourceAsStream(java.lang.String)

  • ClassLoader类中的:
    1644205-20190427221356505-212961866.jpg

返回读取指定资源的输入流

根据API的介绍我们知道了,其实Class类中的getResourceAsStream方法其实最终是执行了ClassLoader中的同名方法。这点下文会根据源码分析。

关于类加载器的相关知识建议大家阅读深入理解类加载器

二、具体分析

API中的介绍到Class类的getResourceAsStream方法是委托了类加载器实现的,那么这个方法还有什么存在的意义呢,直接调用类加载器中的方法不就得了吗? 当然,这两个方法不是完全相同的,区别在于二者搜索的范围不同。下面我们就具体的演示一下。

测试类:

public class Demo {
    public static void main(String[] args) {
        System.out.println(Demo.class.getResource(""));
        System.out.println(Demo.class.getClassLoader().getResource(""));
        //getResourceAsStream返回的流对象的地址值,打印出来不够直观,此处使用getResource方法代替
        //URL路径更直观,要表达的意思也是一样的.
    }
}
输出结果:
file:/D:/IdeaProjects/Test/out/production/resource/com/test/
file:/D:/IdeaProjects/Test/out/production/resource/

不难发现Class对象的搜索路径就是当前类所在的路径,而ClassLoader对象的搜索路径是src目录(也就是classpath)

而如果我们做一个小小的调整:

public class Demo {
    public static void main(String[] args) {
        System.out.println(Demo.class.getResource("/"));
        System.out.println(Demo.class.getClassLoader().getResource(""));
    }
}
输出结果就会变为:
file:/D:/IdeaProjects/Test/out/production/resource/
file:/D:/IdeaProjects/Test/out/production/resource/

两者的查找路径相同了,也就是class.getResource("/")的查找路径和classLoader.getResource("")的路径相同,同理getResourceAsStream也是一样。

对于Class.getResourceAsStream

  • 当传入的path不含"/"时,查找资源是在该类所在的包下查找的
  • 当传入的path含有"/"是,查找资源是从src目录,也即classpath下查找的
  • "/"在这里就代表classpath

对于ClassLoader.getResourceAsStream

  • 当传入的path不含"/"时,查找资源是从src目录下查找的
  • 当传入的path有"/"时,结果为null

结果是null是因为path代表的是类加载器的加载范围,由于类加载器的委派机制,会层层委托到BootStrap类加载器,而“/”就代表BootStrap的加载范围,由于BootStrap类加载器是由C实现的而并非Java,所以加载范围是null

看到这里,大家肯定又有疑问,既然"/"代表BootStrap的加载范围,结果是null,为什么class类的方法中传入"/"没有出现null呢?这是因为在源码中对Class类中方法传入的path进行了处理,请看源码:

  • Class类中的getResourceAsStream方法
    public InputStream getResourceAsStream(String name) {
        name = resolveName(name);//对传入的path进行处理
        ClassLoader cl = getClassLoader0();//获取当前类的类加载器对象
        if (cl==null) {
            // A system class.
            return ClassLoader.getSystemResourceAsStream(name);
        }
        return cl.getResourceAsStream(name);//调用ClassLoader类的同名方法
    }
  • Class类中的resolveName方法
    private String resolveName(String name) {
        if (name == null) {
            return name;
        }
        if (!name.startsWith("/")) {//传入的path不是以"/"开头的情况
            Class<?> c = this;
            while (c.isArray()) {//数组类型的处理方法
                c = c.getComponentType();
            }
            String baseName = c.getName();
            int index = baseName.lastIndexOf('.');
            if (index != -1) {
                name = baseName.substring(0, index).replace('.', '/')
                    +"/"+name;
            }
        } else {//如果path以"/"开头,就把"/"截掉
            name = name.substring(1);
        }
        return name;
    }

至此,大家应该懂了Class和ClassLoader类中的getResourceAsStream方法,本质上来看其实是一样的,都是调用ClassLoader的getResourceAsStream方法.

而路径的区别是因为Class类的方法对传入的path进行了处理:如果"/"开头就从相对于classpath的绝对路径下查找资源;如果不以"/"开头,那就从当前类所在的相对路径下查找资源。

三、补充

接下来简要介绍下ServletContext接口中的getResourceAsStream方法。
显然,这个方法是作用在web项目中的,所以这个方法的搜索路径根路径就不是src目录了,而是web目录。以IDEA的项目结构为例
1644205-20190428144636572-1652911772.jpg
在Demo1中我们测试一下:

@WebServlet(urlPatterns="/demo1", name="Demo1")
public class Demo1 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ServletContext servletContext = getServletContext();
        System.out.println(servletContext.getRealPath(""));
    }
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}
//通过浏览器访问此servlet时,得到控制台输出为:
//D:\IdeaProjects\Test\out\artifacts\test_war_exploded\

也就是默认的搜索路径是web项目下,此时要想访问src的资源怎么办呢?
答案是访问WEB-INF文件夹下的classes文件夹,也就是web项目中,classpath不再是普通java项目中的编译后的out\production\test了, 而是在WEB-INF/classes
因为IDEA会在我们的项目编译时帮我们把开发工具中的目录结构进行调整,转换为服务器可以进行部署的标准目录结构,也就是把src下目录结构 在编译后放进了classes文件夹中
所以在web项目中我们要访问资源就可以写相对于web文件夹的相对路径,只需注意src中的资源在WEB-INF/classes下就行了.

The end.

转载于:https://www.cnblogs.com/story-xc/p/10780709.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值