解决 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
}
相关推荐
来来走走20 小时前
Android开发(Kotlin) 高阶函数、内联函数
android·开发语言·kotlin
L.EscaRC21 小时前
Spring Boot 自定义组件深度解析
java·spring boot·后端
pengzhuofan21 小时前
IntelliJ IDEA 常用快捷键
java·ide·intellij-idea
ANGLAL21 小时前
17.MyBatis动态SQL语法整理
java·sql·mybatis
lskisme21 小时前
springboot maven导入本地jar包
开发语言·python·pycharm
SheepHappy21 小时前
MyBatis-Plus 源码阅读(二)代码生成器原理深度剖析
java·源码阅读
雨白21 小时前
重识 Java IO、NIO 与 OkIO
android·java
light_in_hand21 小时前
内存区域划分——垃圾回收
java·jvm·算法
金銀銅鐵21 小时前
[Java] JDK 9 新变化之 Convenience Factory Methods for Collections
java·后端