解决 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
}
相关推荐
clk66072 分钟前
Spring Boot
java·spring boot·后端
扣丁梦想家6 分钟前
✅ 常用 Java HTTP 客户端汇总及使用示例
java·开发语言·http
程序员杰哥8 分钟前
Postman常见问题及解决方法
自动化测试·软件测试·python·测试工具·职场和发展·测试用例·postman
sss191s33 分钟前
Java 集合面试题 PDF 及常见考点解析与备考指南
java·开发语言·pdf
七七&55634 分钟前
java面试-场景题
java·python·面试
我就是全世界34 分钟前
2025主流智能体Agent终极指南:Manus、OpenManus、MetaGPT、AutoGPT与CrewAI深度横评
人工智能·python·机器学习
loser.loser39 分钟前
QQ邮箱发送验证码(Springboot)
java·spring boot·mybatis
誰能久伴不乏1 小时前
Qt 开发中的父类与父对象的区别和父对象传递:如何选择 `QWidget` 或 `QObject`?
java·开发语言·qt
waterHBO1 小时前
python 爬虫工具 mitmproxy, 几问几答,记录一下
开发语言·爬虫·python
pianmian11 小时前
arcpy与扩展模块
python