YON CISE

Java 资源加载

Java 运行时会用到一些非代码的资源类文件,  JVM 本身提供了几种方式来加载资源, 常用的 Spring 也提供了相应的加载资源文件的方式. 如果不了解这些加载资源的方式的细节, 实际开发时就没法按照你自己的意愿来灵活加载资源, 经常会遇到加载不到或者加载到错误资源的问题. 常见的两类加载资源的方式就是上面提到的基于 JVM 和 Spring 提供的方式来加载资源.

JVM


JVM 提供的加载资源的方式又可以分为两类, 一个是通过 ClassLoader 另一个是通过 Class, 都是到 classpath 中配置的地址中查找资源. 两者区别不是很大, 通过 Class 来查找资源最终也是调用到 ClassLoader 来查找的, 只是 Class 默认是在对应类所在的目录下来查找的, 如果你要从根目录来查找就需要在查找的资源前加上 /, 要注意的是 ClassLoader 来查找资源的时候是不支持以 / 开头的资源名的 [1]. ClassLoadergetResourcegetResources 两个方法, 如果指定的资源名有多个资源时, 前者会返回找到的第一个资源, 后者会返回所有.

前面提到, ClassClassLoader 都是从 classpath 中去查找资源, 我们知道, classpath 中可以配置文件目录地址, 也可以配置 jar 包, 相对应的, JVM 实现时分别使用了 FileLoaderJarLoader 来查找资源. 这两个 Loader 对于空字符串的处理是不一样的, FileLoader 会返回根目录, JarLoader 则找不到对应资源, 具体实现可以看下源码,入口是 URLClassLoader, 最终会使用 sun.misc.URLClassPath 查找资源 [2].

Spring


JVM 只能从 classpath 中查找资源, Spring 则在此基础上增加了从文件地址, 网络, jar 等地方加载, 通过在资源名前指定 protocol 来区分 [3]. 常见的 protocol 有:

  • classpath:: 同 ClassLoadergetResource
  • classpath*:: 同 ClassLoadergetResources, 同时还支持 ant 风格的通配符 [4], 具体实现上就是先把第一个通配符前面的字符串取出来调用 ClassLoadergetResources 方法, 然后再在找到的资源下面遍历查找 [5, 6]
  • file:: 从本地文件里查找, 支持 ant 风格的通配符
  • jar:: 从 jar 包查找, 支持 ant 风格的通配符
  • zip:: 从 zip 压缩包查找, 支持 ant 风格的通配符
  • http://: 从网络里加载


[1] https://stackoverflow.com/questions/47900677/where-does-leading-slash-in-java-class-loader-getresource-leads-to
[2] https://stackoverflow.com/questions/45624909/java-empty-path-convention-especially-that-used-in-classloader-getresources
[3] https://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/resources.html
[4] https://stackoverflow.com/questions/2952196/ant-path-style-patterns
[5] https://stackoverflow.com/questions/13994840/what-is-the-difference-between-classpath-and-classpath-in-spring-xml
[6] https://stackoverflow.com/questions/3294423/spring-classpath-prefix-difference