关于Spring Boot中的getResources

本文详细探讨了Spring Boot中关于`getResources`的使用,分析了`Class.getResource`、`ClassLoader.getResource`以及`Class.getClassLoader().getResource`的区别和行为。通过实例代码展示了如何在不同情况下获取类路径下的资源文件,并解释了为何在路径前加'/'会导致不同的结果。文章还提到了类加载器的双亲委派机制在资源加载过程中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关于Spring Boot中的getResources

在前一篇讲Spring Boot启动流程的文章中,在获取Initializers和Listeners的时候都会通过ClassLoader的getResources方法获取相关文件的全限定名。

今天着重分析一下ClassLoader.getResources,ClsasLoader.getResource,Class.getResource

1.Class.getResource

看下面一段代码

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

返回结果:

null
file:/Users/xujingzhou/IdeaProjects/test-resources/target/classes/Test.xml

我们先看现象,同样都是调用调用class.getResource方法,为什么会有不同的结果呢。

首先看一下class.getResource()

public java.net.URL getResource(String name) {
    name = resolveName(name);
    //获取当前的类加载器
    ClassLoader cl = getClassLoader0();
    if (cl==null) {
        // A system class.
        //调用系统类加载器
        return ClassLoader.getSystemResource(name);
    }
    //调用classLoader的getResource方法
    return cl.getResource(name);
}

首先调用的是resolveName(),根据我们传入的参数一个加‘/’一个没加,刚好在resolveName中存在判断。

private String resolveName(String name) {
    if (name == null) {
        return name;
    }
    //如果name的开头不包含'/'
    if (!name.startsWith("/")) {
        //获取当前类
        Class<?> c = this;
        while (c.isArray()) {
            c = c.getComponentType();
        }
        //获取当前类名
        String baseName = c.getName();
        //获取最后一个'.'出现的坐标
        int index = baseName.lastIndexOf('.');
        if (index != -1) {
            //将当前传入的name拼接到当前类路径下
            name = baseName.substring(0, index).replace('.', '/')
                +"/"+name;
        }
    } else {
        //返回去除‘/’之后的名字。
        name = name.substring(1);
    }
    return name;
}

看到这里,我们应该去看看ClassLoader中的getResource是如何实现的了,先做个小实验吧。

System.out.println(TestResources.class.getClassLoader().getResource("Test.xml"));
System.out.println(TestResources.class.getClassLoader().getResource("/Test.xml"));

输出结果:

file:/Users/xujingzhou/IdeaProjects/test-resources/target/classes/Test.xml
null

先看结论,当传入的name为文件名的时候,是可以找到当前文件的全限定名的。但是为路径的时候就找不到了,接下来就看看是如何实现的。

public URL getResource(String name) {
    URL url;
    //如果父加载器不为null,则调用父加载器,符合双亲委派机制
    if (parent != null) {
        url = parent.getResource(name);
    //这其实是一个递归调用,最终都会走到这一步调用系统启动类加载器
    } else {
        url = getBootstrapResource(name);
    }
    //一个递归回退的过程,都会走一遍findResource方法
    if (url == null) {
        url = findResource(name);
    }
    return url;//如果最后都没加载到,双亲委派机制失败,加载应用自身的类加载器
}

可是这样做的目的是为了什么呢

现在主要分为两种情况

  1. name前加‘/’
    1. TestClassLoader.class.getResource("/")
      1. Class类中的getResource方法返回的是""
      2. ClassLoader类中的getResource方法返回的是 路径
    2. TestClassLoader.class.getClassLoader().getResource("/")
      1. ClassLoader类中的getResource方法返回的是 null
  2. name前不加‘/’
    1. TestClassLoader.class.getResource("")
      1. Class类中的getResource方法返回的是 路径
      2. ClassLoader类中的getResource方法返回的是 路径
    2. TestClassLoader.class.getClassLoader().getResource("")
      1. ClassLoader类中的getResource方法返回的是 路径
  • class的getResource

    • 当path不以‘/’开头我们就可以获得和当前类在路径相同的文件,也就是我们说的同级
    • 当path以‘/’开头时,这样就可以获取到classpath下任意路径下的资源
  • classLoader的getResource

    • 当path不以‘/’开头的时候,首先通过双亲委派机制,逐级向上委托的形式加载,最后发现双亲没有加载,然后通过当前类加载classpath下资源文件
    • 当path以‘/’开头时,‘/’表示boot classLoader中的加载范围,这个类是CPP写的,所以加载范围是null。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值