解决 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
}
相关推荐
勿忘,瞬间几秒前
多线程之进阶修炼
java·开发语言
萌萌站起2 分钟前
Vscode 中 python模块的导入问题
ide·vscode·python
014-code12 分钟前
线程池参数怎么配才不翻车
java
吴梓穆13 分钟前
UE5 c++ 常用方法
java·c++·ue5
是小蟹呀^16 分钟前
【总结】提示词工程
python·llm·prompt·agent
YBAdvanceFu19 分钟前
从零构建智能体:深入理解 ReAct Plan Solve Reflection 三大经典范式
人工智能·python·机器学习·数据挖掘·多智能体·智能体
王夏奇35 分钟前
python中的__all__ 具体用法
java·前端·python
王夏奇39 分钟前
pycharm中3种不同类型的python文件
ide·python·pycharm
明湖起风了41 分钟前
mqtt消费堆积
java·jvm·windows
Free Tester1 小时前
如何判断 LeakCanary 报告的严重程度
java·jvm·算法