在读取本地资源的时候我们经常需要用到输入流,典型的场景就是使用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. C++的拷贝构造函数、operator=运算符重载,深拷贝和浅拷贝、explicit关键字

    原文地址:https://blog.csdn.net/shine_journey/article/details/53081523 1.在C++编码过程中,类的创建十分频繁. 简单的功能,当然不用考虑 ...

  2. 基于jQuery的AJAX实现三级联动菜单

    最近学习jQuery,所以就写了一个关于中国省市县/区的三级联动菜单,权当相互学习,相互促进,特此记录. 下面是嵌套js的html文件: <!DOCTYPE html> <html ...

  3. 使用xshell链接虚拟机的方法

    给大家介绍一下虚拟机和Xshell5连接的基本配置1.安装虚拟机,跟着提示一步一步安装即可,注意添加镜像文件,虚拟机就完成了.2.下载一个Xshell5,安装好之后.要修改虚拟机的网卡状态    1) ...

  4. Jvm虚拟机结构与机制

    JVM(Java Virtual Machine)在研究JVM的过程中会发现,其实JVM本身就是一个计算机体系结构,很多原理和我们平时的硬件.微机原理.操作系统都有十分相似的地方,所以学习JVM本身也 ...

  5. JQUERY获取loaded 宽高这么变态

    JQUERY获取loaded 宽高这么变态: $('<img/>').attr('src',img.src).load(function() { img.Owidth = $(this). ...

  6. 20.QT-Qpixmap实现图片鼠标缩放,鼠标拖动示例(详解)

    通过 QPainter 绘画实现,以本地图片985*740为例 如下图所示: 效果如下所示: 实现原理 主要通过以下函数实现: , ); //平铺显示pixmap //x y w h :表示绘画区域 ...

  7. (三)Maven使用入门之Hello World

    主要内容 编写POM 编写主代码 编写测试代码 打包和运行 到目前为止,已经大概了解并安装好了Maven,现在开始创建一个最简单的HelloWorld项目. 编写POM 就像Make的Makefile ...

  8. selenium 定位元素成功, 但是输入失败 (textarea)

    问题描述 UI页面功能测试中, 定位元素并输入(通过sendKey()方法输入), 显示输入失败. 根本原因 为了修复一个bug, 这个元素从input改成了textarea, 而textarea是有 ...

  9. 在echarts里在geojson绘制的地图上展示散点图(气泡)、线集。

    先来要实现的效果图: 下方图1是官网的案例:http://www.echartsjs.com/gallery/editor.html?c=scatter-map 下图2是展示气泡类型为pin的效果: ...

  10. in_flight_pqueue.go

    // right child         }         if (*pq)[j].pri >= (*pq)[i].pri {             break         }    ...