解决 Java/Kotlin 资源加载问题

在对 Jar 的 Resources 目录下的资源加载时候有很多方法,一般这个方法不行就换个方法。在下一个常见就发生这个方法不行上个方法可以,造成这个结果的原因就是,资源是由类加载器进行加载的,如果当前的类加载器无法加载到资源就需要使用其他的类加载器进行加载。

类加载器区分

通常使用以下的类加载器

  • 加载当前类的类加载器
    • 用于加载当前类所在模块的资源
  • 线程上下文类加载器(TCCL)
    • 用于加载当前类所在模块的资源
    • 加载其他模块/应用的资源
    • 加载框架 SPI 服务绑定的资源
    • 动态加载的外部资源
  • 系统类加载器
    • 加载当前类所在模块的资源
    • 加载其他模块/应用的资源

大部分造成资源无法加载的场景为负责加载的类加载器和负责加载资源的类加载器不是同一个

路径格式区分

资源路径在查找时有"相对路径"和"绝对路径"的区别。

  • 以斜杠开头:例如"/A.txt",表示从类路径(classpath)的根目录开始查找该资源。
  • 不以斜杠开头:例如"A.txt",则被解释为相对于调用该方法的类所在包的路径。例如,如果你的类在包 com.example 下,那么它会查找 com/example/A.txt。

使用各种类加载器进行加载(JAVA)

java 复制代码
public static InputStream getResource(String path) {
    InputStream resource = null;
    
    // Try class-based resource loading
    Class<?> clazz = this.getClass;
    resource = clazz.getResourceAsStream(path);
    if (resource != null) return resource;
    
    resource = clazz.getResourceAsStream("/" + path);
    if (resource != null) return resource;
    
    resource = clazz.getResourceAsStream("./" + path);
    if (resource != null) return resource;

    // Try thread context class loader
    ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
    if (contextClassLoader != null) {
        resource = contextClassLoader.getResourceAsStream(path);
        if (resource != null) return resource;
        
        resource = contextClassLoader.getResourceAsStream("/" + path);
        if (resource != null) return resource;
        
        resource = contextClassLoader.getResourceAsStream("./" + path);
        if (resource != null) return resource;
    }

    // Try path's class loader (String.class loader)
    ClassLoader pathClassLoader = path.getClass().getClassLoader();
    if (pathClassLoader != null) {
        resource = pathClassLoader.getResourceAsStream(path);
        if (resource != null) return resource;
        
        resource = pathClassLoader.getResourceAsStream("/" + path);
        if (resource != null) return resource;
        
        resource = pathClassLoader.getResourceAsStream("./" + path);
        if (resource != null) return resource;
    }

    // Try system class loader
    resource = ClassLoader.getSystemResourceAsStream(path);
    if (resource != null) return resource;
    
    resource = ClassLoader.getSystemResourceAsStream("/" + path);
    if (resource != null) return resource;
    
    resource = ClassLoader.getSystemResourceAsStream("./" + path);
    if (resource != null) return resource;

    // Try direct URL loading
    try {
        return new URL(path).openStream();
    } catch (Exception ignored) {
        // Ignore exceptions
    }

    return null;
}

Kotlin

kotlin 复制代码
fun getResource(path: String): InputStream? {
    var resource = this.javaClass.getResourceAsStream(path)
    if (resource == null) resource = this.javaClass.getResourceAsStream("/$path")
    if (resource == null) resource = this.javaClass.getResourceAsStream("./$path")
    if (resource == null) resource = Thread.currentThread().contextClassLoader.getResourceAsStream(path)
    if (resource == null) resource = Thread.currentThread().contextClassLoader.getResourceAsStream("/$path")
    if (resource == null) resource = Thread.currentThread().contextClassLoader.getResourceAsStream("./$path")
    if (resource == null) resource = path.javaClass.classLoader?.getResourceAsStream(path)
    if (resource == null) resource = path.javaClass.classLoader?.getResourceAsStream("/$path")
    if (resource == null) resource = path.javaClass.classLoader?.getResourceAsStream("./$path")
    if (resource == null) resource = ClassLoader.getSystemResourceAsStream(path)
    if (resource == null) resource = ClassLoader.getSystemResourceAsStream("/$path")
    if (resource == null) resource = ClassLoader.getSystemResourceAsStream("./$path")
    if (resource == null) kotlin.runCatching { resource = URL(path).openStream() }
    return resource
}
相关推荐
gugucoding21 小时前
55. 【Java】Maven:项目构建的“管家”
java·maven
zmzb010321 小时前
Python课后习题训练记录Day194
python·opencv·计算机视觉
benchmark_cc21 小时前
批量获取量化数据时,如何设置合理的超时和重试机制?——QuantDash 高性能实战指南
开发语言·人工智能·python·pandas·量化·quantdash
2601_9669496521 小时前
使用 Pandas 读取批量数据时如何避免内存溢出?QuantDash 量化数据工程师避坑指南
开发语言·python·pandas·tushare·akshare·quantdash
65岁退休Coder1 天前
LangChain v1.3.4 笔记 - 08 MCP & 相关概念
后端·python·langchain
郑州光合科技余经理1 天前
海外版多语言团购系统架构:主数据互通与核销边界
java·开发语言·前端·后端·系统架构·php·ai编程
wdloumiga1 天前
使用 Construct 3零基础做一些2D小游戏
python·游戏·游戏2d
Jesse_EC1 天前
为什么我设置的 contentLength 到了消费端变成了 0?
java
vipxieliang1 天前
ValidX 的 Date 和 DateTime vs JPA 的 Temporal 对比
java·后端