Java 中的 getResource 方法

getResource 的作用

getResourceClass类中的一个方法
作用:配置文件的读取

Class 的概念

首先我们得知道,在new一个对象的时候,会在java堆中生成一个代表这个类的java.lang.Class对象,(Class 对象不能主动创建,只能获取)作为该类的数据访问入口
那么我们如何使用对应的Class对象呢?
三种方法 ,先看看我的目录结构
在这里插入图片描述

Resource.class     //直接使用类名.class
Class.forName("zhang.Resource")  //包名.类名   需要捕获异常
new Resource().getClass()    //先获取一个对象  再调用对象的 getClass()方法

现在我们写6个语句,前3个没/ ,后3个有/ (强烈建议自己试一试)

public class Resource {
    public static void main(String[] args) throws IOException {
        System.out.println("------------------------------getResource(\"\")测试---------------------");
        // 1 返回 target/classes/package
        System.out.println(Resource.class.getResource(""));
        // 2 同级(编译路径)下,是否存在对应文件,存在则返回该地址
        System.out.println(Resource.class.getResource("Resource.class"));
        // 3 不存在,返回 null
        System.out.println(Resource.class.getResource("NoResource.class") + "\n\n");

        System.out.println("------------------------------getResource(\"/\")测试---------------------");
        // 4 返回 target/classes/
        System.out.println(Resource.class.getResource("/"));
        // 5 target/classes/ 目录下不存在 Resource.class
        System.out.println(Resource.class.getResource("/Resource.class"));
        // 6 target/classes/ 目录下存在 cn/zhang
        System.out.println(Resource.class.getResource("/cn/zhang"));
    }
}

结果如下,返回的地址都是绝对地址

------------------------------getResource("")测试---------------------
file:/E:/zy-source-learn/spring-family/springboot3-source-learn/target/classes/cn/zhang/springboot3sourcelearn/demo/
file:/E:/zy-source-learn/spring-family/springboot3-source-learn/target/classes/cn/zhang/springboot3sourcelearn/demo/Resource.class
null


------------------------------getResource("/")测试---------------------
file:/E:/zy-source-learn/spring-family/springboot3-source-learn/target/classes/
null
file:/E:/zy-source-learn/spring-family/springboot3-source-learn/target/classes/cn/zhang

区别如下

  • /target/classes/package
  • /target/classes/

/,有点类似于 linux 中的 / —— 根路径 (而无 / 相当于相对路径)

getResource 源码分析

现在我们来分析:按ctrl + 鼠标左键,点进去查看源码

public java.net.URL getResource(String name) {
    name = resolveName(name);
    ClassLoader cl = getClassLoader0();   
    if (cl==null) {
        // A system class.
        return ClassLoader.getSystemResource(name);
    }
    return cl.getResource(name);
}

对传入的name(想要加载的文件的相对地址)进行解析,并再次赋值给name

继续分析resolveName方法

resolveName 方法(JDK17)

private String resolveName(String name) {
    if (!name.startsWith("/")) {
	    String baseName = getPackageName();
	     if (!baseName.isEmpty()) {
	         int len = baseName.length() + 1 + name.length();
	         StringBuilder sb = new StringBuilder(len);
	         name = sb.append(baseName.replace('.', '/'))   // <---- package 中的 . 替换为 /
	             .append('/')
	             .append(name)   // <----- package/name
	             .toString();
	     }
    } else {
        name = name.substring(1);   // 以 "/" 开头,去掉 "/"
    }
    return name;    // 最后的返回结果都是去掉"/"的
}

验证了之前的结论,不加 / ,会 append getPackageName,即:以当前类所在的 package 为基准

classpath: 指的是编译后的class文件、xml、properties等配置文件所在的目录,如下图
在这里插入图片描述
因此,""相对地址"/"绝对地址

getResource 之后的代码

接下来再来看看后面的代码

public java.net.URL getResource(String name) {
    name = resolveName(name);
    ClassLoader cl = getClassLoader0();   // 获取加载该 Class 的 ClassLoader
    if (cl==null) {		// 如果加载该 Class 的 ClassLoader 为 null,则表示这是一个系统 class
        // A system class.		系统类加载器
        return ClassLoader.getSystemResource(name); // 调用 ClassLoader 的 getSystemResource 方法
    }
    return cl.getResource(name);  // 调用 ClassLoader 的 getResource 方法
}

getResource最终调用的是ClassLoadergetSystemResource方法或者getResource方法
现在来分析ClassLoader类里的getResource方法

 public URL getResource(String name) {
     URL url;
     if (parent != null) {
         url = parent.getResource(name);	 //递归调用
     } else {
         url = getBootstrapResource(name);   //启动类加载器
     }
     if (url == null) {
         url = findResource(name);   // <----- return null
     }
     return url;
 }

这里涉及到一个知识点 :双亲委派机制
就是如果一个类加载器(用来加载 class 文件)收到了类加载的请求,首先不会自己尝试去加载这个类,而是将请求委派给父类加载器去完成
只有当父类加载器无返完成这个加载请求,子加载起才会尝试自己去完成加载在这里插入图片描述
Java中的类加载器主要分为以下四类:

(1)根类加载器(BootStrapClassLoader), 主要负责加载jre/lib/rt.jar相关的字节码文件的。

(2)扩展类加载器(ExtensionClassLoader), 主要负载加载 jre/lib/ext/*.jar 这些jar包的。 该类加载器在JDK1。9的时候更名为: Platform Class Loader, 其父类加载器为: null。

(3)应用程序类加载器(ApplicationClassLoader), 主要负责加载用户自定义的类以及classpath环境变量所配置的jar包的。 该类加载器在JDK1.9的时候更名为: System ClassLoader, 其父类加载器为: ExtensionClassLoader。

(4)自定义类加载器(UserClassLoader), 负责加载程序员指定的特殊目录下的字节码文件的。大多数情况下,自定义类加载器只需要继承ClassLoader这个抽象类,重写findClass()和loadClass()两个方法即可。
在这里插入图片描述

Class.getResourceClassLoader.getResource的区别

Class.getResource:对传入的 name 进行了处理(多了resolveName方法

  • Class.getResource最后返回的name都是去掉/
  • / 会添加 package

ClassLoader.getResource方法,只能处理无 / 的路径,如果一开始加了 /,返回结果为 null

System.out.println(Resource.class.getResource(""));
System.out.println(Resource.class.getResource("/"));

System.out.println(ClassLoader.getSystemResource(""));
System.out.println(ClassLoader.getSystemResource("/"));
file:/E:/zy-source-learn/spring-family/springboot3-source-learn/target/classes/cn/zhang/springboot3sourcelearn/demo/
file:/E:/zy-source-learn/spring-family/springboot3-source-learn/target/classes/
file:/E:/zy-source-learn/spring-family/springboot3-source-learn/target/classes/
null

参考
彻底搞懂Class.getResource和ClassLoader.getResource的区别和底层原理
java有几种类加载器?工作原理是什么?

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值