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

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

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

一、API

  • Class类中的:

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

  • ClassLoader类中的:

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

根据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的项目结构为例



在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.

分析Class类和ClassLoader类下的同名方法getResourceAsStream的更多相关文章

  1. Class类和ClassLoader类的简单介绍

    反射机制中的Class Class内部到底有什么呢?看下图! 代码: Class cls=Person.class; 1.Class类: 1. 对象照镜子后可以得到的信息:某个类的数据成员名,方法和构 ...

  2. 反射(学习整理)----Class类和加载器ClassLoader类的整理

    1.学习反射的时整理的笔记!Class类和ClassLoader类的简单介绍 反射机制中的Class Class内部到底有什么呢?看下图! 代码: Class cls=Person.class; .C ...

  3. Spring Boot 2.x 启动全过程源码分析(上)入口类剖析

    Spring Boot 的应用教程我们已经分享过很多了,今天来通过源码来分析下它的启动过程,探究下 Spring Boot 为什么这么简便的奥秘. 本篇基于 Spring Boot 2.0.3 版本进 ...

  4. JAVA基础加强(张孝祥)_类加载器、分析代理类的作用与原理及AOP概念、分析JVM动态生成的类、实现类似Spring的可配置的AOP框架

    1.类加载器 ·简要介绍什么是类加载器,和类加载器的作用 ·Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:BootStrap,ExtClassLoader ...

  5. java笔记--理解java类加载器以及ClassLoader类

    类加载器概述: java类的加载是由虚拟机来完成的,虚拟机把描述类的Class文件加载到内存,并对数据进行校验,解析和初始化,最终形成能被java虚拟机直接使用的java类型,这就是虚拟机的类加载机制 ...

  6. List 接口以及实现类和相关类源码分析

    List 接口以及实现类和相关类源码分析 List接口分析 接口描述 用户可以对列表进行随机的读取(get),插入(add),删除(remove),修改(set),也可批量增加(addAll),删除( ...

  7. [Java]类的生命周期(下)类的初始化[转]

    上接深入java虚拟机——深入java虚拟机(二)——类加载器详解(上),在上一篇文章中,我们讲解了类的生命周期的加载和连接,这一篇我们接着上面往下看. 类的初始化:在类的生命周期执行完加载和连接之后 ...

  8. Java运行时动态加载类之ClassLoader

    https://blog.csdn.net/fjssharpsword/article/details/64922083 *************************************** ...

  9. ClassLoader 提供了两个方法用于从装载的类路径中取得资源:

    转:http://cheneyph.iteye.com/blog/831721 ClassLoader 提供了两个方法用于从装载的类路径中取得资源: public URL  getResource ( ...

随机推荐

  1. 技术人应该学习的行话--UML统一建模语言

    新生代码农如何在硝烟弥漫的商业丛林中生存和崛起? 洞见,让一部分先遇见未来. 最近公司技术部在组织架构师培训,有幸参与.导师老刘特别推荐了UML语言的学习.回想多年来,自己习惯做一些流程图,框图或者所 ...

  2. 服务器禁止ping

    禁止ping后,不让别人通过域名ping到你的ip, 如果禁用后,你在ping自己的域名会给你返回服务商的IP并提示超时, 这样你就可以减少IP暴露,增加一点安全. 禁止方法: 编辑 /etc/sys ...

  3. Kudu vs HBase

    本文由  网易云发布. 背景 Cloudera在2016年发布了新型的分布式存储系统--kudu,kudu目前也是apache下面的开源项目.Hadoop生态圈中的技术繁多,HDFS作为底层数据存储的 ...

  4. Maven的使用

    在项目中,我们通常会为项目添加多种多样的依赖包(jar包),去网上下载,然后引入到项目中,很是麻烦. 但是用maven后,这一切都将变得简单起来.由于我的MyEclipse已经集成了maven插件,这 ...

  5. C#学习笔记 day_two

    C#学习笔记 day two Chapter 2 c#基本概念 2.1编译与运行hello world应用程序 点击f5或者vs2010中的运行图标即可 2.3C#的概念拓展 (1)继承性:一个类含有 ...

  6. Python_字符串的映射与可变字符串的应用

    ''' maketrans().translate() maketrans()方法用来生成字符映射表,而translate()方法则按映射表中定义的对应关系转换并替换其中的字符,使用这两个方法的组合可 ...

  7. linux下activemq安装与配置

    一.下载:apache-activemq-5.14.0-bin.tar.gz http://activemq.apache.org/activemq-5140-release.html   二.安装a ...

  8. linux简单内核链表排序

    #include <stdio.h> #include <stdlib.h> #define container_of(ptr, type, mem)(type *)((uns ...

  9. 【手记】解决启动SQL Server Management Studio 17时报Cannot find one of more components...的问题

    刚装好SSMS 17.1准备体验,弹出: 一番搜索,普遍办法都是安装VS2015独立shell.删除某个注册表项什么的,没用,首先这个shell我是装了的,然后也没有那个注册表项.我自己尝试过重装sh ...

  10. php对接微信小程序支付

    前言:这里我就假装你已经注册了微信小程序,并且基本的配置都已经好了.注: 个人注册小程序不支持微信支付,所以我还是假装你是企业或者个体工商户的微信小程序,其他的商户号注册,二者绑定,授权,支付开通,就 ...