当前位置: 代码网 > it编程>编程语言>Java > Java资源加载机制及使用最佳实践

Java资源加载机制及使用最佳实践

2025年12月13日 Java 我要评论
/** * finds the resource with the given name. a resource is some data * (images, audio, tex
    /**
     * finds the resource with the given name.  a resource is some data
     * (images, audio, text, etc) that can be accessed by class code in a way
     * that is independent of the location of the code.
     *
     * <p> the name of a resource is a '<tt>/</tt>'-separated path name that
     * identifies the resource.
     *
     * <p> this method will first search the parent class loader for the
     * resource; if the parent is <tt>null</tt> the path of the class loader
     * built-in to the virtual machine is searched.  that failing, this method
     * will invoke {@link #findresource(string)} to find the resource.  </p>
     *
     * @apinote when overriding this method it is recommended that an
     * implementation ensures that any delegation is consistent with the {@link
     * #getresources(java.lang.string) getresources(string)} method.
     *
     * @param  name
     *         the resource name
     *
     * @return  a <tt>url</tt> object for reading the resource, or
     *          <tt>null</tt> if the resource could not be found or the invoker
     *          doesn't have adequate  privileges to get the resource.
     *
     * @since  1.1
     */
    public url getresource(string name) {
        url url;
        if (parent != null) {
            url = parent.getresource(name);
        } else {
            url = getbootstrapresource(name);
        }
        if (url == null) {
            url = findresource(name);
        }
        return url;
    }
    /**
     * finds all the resources with the given name. a resource is some data
     * (images, audio, text, etc) that can be accessed by class code in a way
     * that is independent of the location of the code.
     *
     * <p>the name of a resource is a <tt>/</tt>-separated path name that
     * identifies the resource.
     *
     * <p> the search order is described in the documentation for {@link
     * #getresource(string)}.  </p>
     *
     * @apinote when overriding this method it is recommended that an
     * implementation ensures that any delegation is consistent with the {@link
     * #getresource(java.lang.string) getresource(string)} method. this should
     * ensure that the first element returned by the enumeration's
     * {@code nextelement} method is the same resource that the
     * {@code getresource(string)} method would return.
     *
     * @param  name
     *         the resource name
     *
     * @return  an enumeration of {@link java.net.url <tt>url</tt>} objects for
     *          the resource.  if no resources could  be found, the enumeration
     *          will be empty.  resources that the class loader doesn't have
     *          access to will not be in the enumeration.
     *
     * @throws  ioexception
     *          if i/o errors occur
     *
     * @see  #findresources(string)
     *
     * @since  1.2
     */
    public enumeration<url> getresources(string name) throws ioexception {
        @suppresswarnings("unchecked")
        enumeration<url>[] tmp = (enumeration<url>[]) new enumeration<?>[2];
        if (parent != null) {
            tmp[0] = parent.getresources(name);
        } else {
            tmp[0] = getbootstrapresources(name);
        }
        tmp[1] = findresources(name);
        return new compoundenumeration<>(tmp);
    }
    /**
     * finds the resource with the given name. class loader implementations
     * should override this method to specify where to find resources.
     *
     * @param  name
     *         the resource name
     *
     * @return  a <tt>url</tt> object for reading the resource, or
     *          <tt>null</tt> if the resource could not be found
     *
     * @since  1.2
     */
    protected url findresource(string name) {
        return null;
    }
    /**
     * returns an enumeration of {@link java.net.url <tt>url</tt>} objects
     * representing all the resources with the given name. class loader
     * implementations should override this method to specify where to load
     * resources from.
     *
     * @param  name
     *         the resource name
     *
     * @return  an enumeration of {@link java.net.url <tt>url</tt>} objects for
     *          the resources
     *
     * @throws  ioexception
     *          if i/o errors occur
     *
     * @since  1.2
     */
    protected enumeration<url> findresources(string name) throws ioexception {
        return java.util.collections.emptyenumeration();
    }
    /**
     * registers the caller as parallel capable.
     * the registration succeeds if and only if all of the following
     * conditions are met:
     * <ol>
     * <li> no instance of the caller has been created</li>
     * <li> all of the super classes (except class object) of the caller are
     * registered as parallel capable</li>
     * </ol>
     * <p>note that once a class loader is registered as parallel capable, there
     * is no way to change it back.</p>
     *
     * @return  true if the caller is successfully registered as
     *          parallel capable and false if otherwise.
     *
     * @since   1.7
     */
    @callersensitive
    protected static boolean registerasparallelcapable() {
        class<? extends classloader> callerclass =
            reflection.getcallerclass().assubclass(classloader.class);
        return parallelloaders.register(callerclass);
    }
    /**
     * find a resource of the specified name from the search path used to load
     * classes.  this method locates the resource through the system class
     * loader (see {@link #getsystemclassloader()}).
     *
     * @param  name
     *         the resource name
     *
     * @return  a {@link java.net.url <tt>url</tt>} object for reading the
     *          resource, or <tt>null</tt> if the resource could not be found
     *
     * @since  1.1
     */
    public static url getsystemresource(string name) {
        classloader system = getsystemclassloader();
        if (system == null) {
            return getbootstrapresource(name);
        }
        return system.getresource(name);
    }
    /**
     * finds all resources of the specified name from the search path used to
     * load classes.  the resources thus found are returned as an
     * {@link java.util.enumeration <tt>enumeration</tt>} of {@link
     * java.net.url <tt>url</tt>} objects.
     *
     * <p> the search order is described in the documentation for {@link
     * #getsystemresource(string)}.  </p>
     *
     * @param  name
     *         the resource name
     *
     * @return  an enumeration of resource {@link java.net.url <tt>url</tt>}
     *          objects
     *
     * @throws  ioexception
     *          if i/o errors occur
     * @since  1.2
     */
    public static enumeration<url> getsystemresources(string name)
        throws ioexception
    {
        classloader system = getsystemclassloader();
        if (system == null) {
            return getbootstrapresources(name);
        }
        return system.getresources(name);
    }
    /**
     * find resources from the vm's built-in classloader.
     */
    private static url getbootstrapresource(string name) {
        urlclasspath ucp = getbootstrapclasspath();
        resource res = ucp.getresource(name);
        return res != null ? res.geturl() : null;
    }
    /**
     * find resources from the vm's built-in classloader.
     */
    private static enumeration<url> getbootstrapresources(string name)
        throws ioexception
    {
        final enumeration<resource> e =
            getbootstrapclasspath().getresources(name);
        return new enumeration<url> () {
            public url nextelement() {
                return e.nextelement().geturl();
            }
            public boolean hasmoreelements() {
                return e.hasmoreelements();
            }
        };
    }
    // returns the urlclasspath that is used for finding system resources.
    static urlclasspath getbootstrapclasspath() {
        return sun.misc.launcher.getbootstrapclasspath();
    }
    /**
     * returns an input stream for reading the specified resource.
     *
     * <p> the search order is described in the documentation for {@link
     * #getresource(string)}.  </p>
     *
     * @param  name
     *         the resource name
     *
     * @return  an input stream for reading the resource, or <tt>null</tt>
     *          if the resource could not be found
     *
     * @since  1.1
     */
    public inputstream getresourceasstream(string name) {
        url url = getresource(name);
        try {
            return url != null ? url.openstream() : null;
        } catch (ioexception e) {
            return null;
        }
    }
    /**
     * open for reading, a resource of the specified name from the search path
     * used to load classes.  this method locates the resource through the
     * system class loader (see {@link #getsystemclassloader()}).
     *
     * @param  name
     *         the resource name
     *
     * @return  an input stream for reading the resource, or <tt>null</tt>
     *          if the resource could not be found
     *
     * @since  1.1
     */
    public static inputstream getsystemresourceasstream(string name) {
        url url = getsystemresource(name);
        try {
            return url != null ? url.openstream() : null;
        } catch (ioexception e) {
            return null;
        }
    }

以上代码是 java 标准库中 java.lang.classloader 类的一部分,主要涉及 资源(resource)加载机制。理解这些方法对深入掌握 java 类加载机制、模块化系统、以及框架(如 spring、hibernate、mybatis 等)如何加载配置文件、模板、静态资源等至关重要。

一、核心概念:什么是“resource”?

在 java 中,resource 是指与类代码逻辑相关的非代码数据,例如:

  • 配置文件(如 application.properties
  • xml 映射文件(如 mybatis 的 mapper.xml
  • 图片、音频、文本等静态资源
  • 国际化语言包(.properties

这些资源通常和 .class 文件一起打包在 jar/war 中,或者放在 classpath 目录下。

关键点:resource 的路径是相对于 classpath 根目录 的,用 / 分隔(不是平台相关的 \/)。

二、核心方法解析

1.public url getresource(string name)

功能:

根据资源名(如 "config/app.properties")返回一个 url,可用于读取该资源。

搜索顺序(双亲委派模型):

  1. 先委托 父类加载器(parent classloader)查找;
  2. 如果父加载器为 null(即启动类加载器 bootstrap classloader),则调用 getbootstrapresource()
  3. 若仍未找到,则调用子类可重写的 findresource(name) 方法(由当前 classloader 自己查找)。

🔁 这体现了 双亲委派模型(parent delegation model),保证核心资源优先由上级加载器处理,避免重复或冲突。

返回值:

  • 成功:url 对象(如 jar:file:/xxx.jar!/config/app.properties
  • 失败:null

2.public enumeration<url> getresources(string name)

功能:

返回所有匹配名称的资源(可能有多个同名资源分布在不同 jar 或目录中)。

使用场景:

  • spi(service provider interface)机制:如 meta-inf/services/javax.servlet.servletcontainerinitializer
  • 多模块项目中合并多个 application.properties

注意:

  • 返回的是 enumeration<url>,需遍历。
  • 第一个元素应与 getresource() 返回结果一致(api 注释强调一致性)。

3.protected url findresource(string name)和protected enumeration<url> findresources(string name)

作用:

这两个是 模板方法,供自定义 classloader 子类实现具体资源查找逻辑。

  • 默认实现:findresource 返回 nullfindresources 返回空枚举。
  • 实际实现如 urlclassloader 会从指定的 url 路径(如 jar、目录)中查找资源。

4. 静态方法:getsystemresource/getsystemresources/getsystemresourceasstream

功能:

通过 系统类加载器(system classloader) 加载资源,等价于:

classloader.getsystemclassloader().getresource(name);

📌 系统类加载器通常是 appclassloader(应用类加载器),负责加载 -classpath 指定的类和资源。

使用场景:

  • 工具类中直接读取 classpath 资源(不依赖当前类的 classloader)
  • 启动时加载全局配置

5.getresourceasstream(string name)

功能:

直接返回 inputstream,省去手动 openstream() 的步骤。

内部实现:

url url = getresource(name);
return url != null ? url.openstream() : null;

⚠️ 注意:如果资源不存在或 i/o 出错,返回 null(不会抛异常!)

三、常见使用方式示例

1. 从当前类的 classloader 读取资源

inputstream is = myclass.class.getclassloader().getresourceasstream("config/db.properties");

2. 从当前类所在包的相对路径读取(注意开头无/)

// 假设 myclass 在 com.example 包下
inputstream is = myclass.class.getresourceasstream("local-config.txt"); 
// 实际路径:com/example/local-config.txt

💡 class.getresource()classloader.getresource() 的区别:

  • class.getresource(path)
    • path/ 开头 → 从 classpath 根开始
    • 否则 → 从当前类所在包开始
  • classloader.getresource(path)始终从 classpath 根开始

四、在主流框架中的应用

1.spring framework

  • resourceloader 接口抽象了资源加载,底层大量使用 classloader.getresource()
  • @propertysource("classpath:app.properties") → 通过 classloader 查找。
  • applicationcontext 启动时扫描 meta-inf/spring.factories(使用 getresources() 支持多 jar 合并)。

2.mybatis

  • mapper xml 文件通常通过:
    inputstream is = resources.getresourceasstream("mappers/usermapper.xml");
    
    其中 resources 工具类内部调用 classloader.getresourceasstream()

3.hibernate / jpa

  • persistence.xml 必须位于 meta-inf/persistence.xml,由 persistenceprovider 通过 classloader.getresources("meta-inf/persistence.xml") 发现。

4.spi 机制(java serviceloader)

  • serviceloader.load(myservice.class) 会调用:
    classloader.getresources("meta-inf/services/com.example.myservice");
    
    从而发现所有 jar 中提供的实现。

五、注意事项 & 最佳实践

问题建议
资源路径错误使用 / 分隔,不要用 \;确认是否从 classpath 根开始
打包后资源找不到确保构建工具(maven/gradle)将资源复制到 target/classes
多个同名资源使用 getresources() 遍历,避免只取第一个导致遗漏
安全管理器限制在受限环境(如 applet)中可能返回 null
流未关闭使用 try-with-resources 确保 inputstream 关闭

六、总结

方法用途是否静态委托父加载器
getresource()获取单个资源 url
getresources()获取所有同名资源
getsystemresource()通过系统类加载器获取❌(直接用系统加载器)
getresourceasstream()直接获取输入流
findresource()子类实现具体查找逻辑❌(自身实现)

💡 核心思想:java 的资源加载机制基于 classloader 的双亲委派模型,保证了资源查找的安全性、一致性和可扩展性。理解它,就掌握了 java 应用加载配置、插件、静态文件的底层逻辑。

如果你正在开发自定义 classloader、模块化系统、或需要动态加载资源(如热部署、插件系统),这些方法就是你的基石。需要我进一步举例说明如何自定义 classloader 实现资源加载吗?

到此这篇关于java资源加载机制及使用最佳实践的文章就介绍到这了,更多相关java资源加载机制内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com