解决 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
}
相关推荐
m0_613856291 天前
mysql如何利用事务隔离级别解决特定业务冲突_mysql隔离方案选型
jvm·数据库·python
abcnull1 天前
用javaparser做精准测试
java·ast·静态代码分析·精准测试·javaparser
叶小鸡1 天前
Java 篇-项目实战-苍穹外卖-笔记汇总
java·开发语言·笔记
AI人工智能+电脑小能手1 天前
【大白话说Java面试题】【Java基础篇】第22题:HashMap 和 HashSet 有哪些区别
java·开发语言·哈希算法·散列表·hash
我的xiaodoujiao1 天前
API 接口自动化测试详细图文教程学习系列16--项目实战演练3
python·学习·测试工具·pytest
juniperhan1 天前
Flink 系列第21篇:Flink SQL 函数与 UDF 全解读:类型推导、开发要点与 Module 扩展
java·大数据·数据仓库·分布式·sql·flink
ID_180079054731 天前
Python 实现亚马逊商品详情 API 数据准确性校验(极简可用 + JSON 参考)
java·python·json
c++之路1 天前
C++23概述
java·c++·c++23
时空系1 天前
第10篇:继承扩展——面向对象编程进阶 python中文编程
开发语言·python·ai编程
专注API从业者1 天前
Open Claw 京东商品监控选品实战:一键抓取、实时监控、高效选品
java·服务器·数据库