博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java 加载资源文件
阅读量:6156 次
发布时间:2019-06-21

本文共 6489 字,大约阅读时间需要 21 分钟。

hot3.png

简介

Java中获取资源的最常用的2中方式就是使用Class的getResource和使用ClassLoader的getResource方法,当然还有它们相关的方法。这里就介绍一下使用这2中方式的区别,和它们搜索使用的路径。 这里先说结论(hotspot): ClassLoader的getResource(name)方法会依次查找:

  1. 在"sun.boot.class.path"指定的路径问根目录下查找name资源
  2. 在"java.ext.dirs"指定的路径为根目录下查找name资源
  3. 在"java.class.path"指定的路径为根目录下查找name资源
  4. 利用ClassLoader(自定义的,重写了findResource)的findResource(name)获取URL

Class的getResource(name)方法是调用的ClassLoader的getResource(name)方法,但是它做了2点处理:

  1. 如果name以"/"开头,就把name中开头的"/"去掉,然后调优ClassLoader的getResource(name)方法。然后在ClassLoader的getResource(name)方法搜索方式搜索。
  2. 如果name不以"/"开头,那么就用Class的包名+name作为新的name来调用ClassLoader的getResource(name)方法。cn.freemethod.start.BaseName.class.getResource("config.properties")最终调用的就是ClassLoader的getResource("cn/freemethod/start/config.properties")可能ClassLoader的类型不同,但是不影响ClassLoader的getResource(name)搜索套路。(前提是没有破坏父类委托机制)

注意:这些路径包括了jar包中的资源,例如,你的classpath中包含了springmvc的jar包,你就可以通过下面的方式来加载DispatcherServlet.properties文件。

import java.net.URL;import org.junit.Test;public class ResourceTest {        private static final String DISPATCHER_SERVLET_PROPERTIES_PKG = "org/springframework/web/servlet/DispatcherServlet.properties";    @Test    public void testResource()    {        URL url = null;        ClassLoader loader = ResourceTest.class.getClassLoader();        url = loader.getResource(DISPATCHER_SERVLET_PROPERTIES_PKG);        System.out.println(url);        url = ClassLoader.getSystemResource(DISPATCHER_SERVLET_PROPERTIES_PKG);        System.out.println(url);    }}

另一点值得注意的是在IDE中,比如Eclipse中运行的时候Eclipse常常是添加了新的classpath路径的,比如,如果是Java project工程,Eclipse就会把项目根目录下的bin目录添加到classpath中,如果是maven工程,Eclipse就会把项目根目录下的target目录下的classes和pom中的jar包加到classpath中,运行JUnit,Eclipse会把test-classes和pom中的jar包加到classpath中。 要知道指定的路径有没有在搜索路径中,可以输出3个系统属性看一下就可以了:

System.out.println(System.getProperty("java.class.path"));System.out.println(System.getProperty("sun.boot.class.path"));System.out.println(System.getProperty("java.ext.dirs"));

其实用的最多的还是"java.class.path",一般注意一下"java.class.path"这个就可以了,知道了上面的内容基本上遇到getResource相关的内容的问题基本都能解决了。接下来我们就通过代码来看一些细节的东西。

ClassLoader#getResource(String name)

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;    }

上面就是ClassLoader的getResource方法了,看上去感觉逻辑很简单,其实还是比较绕的。如果没有破坏父类委托机制,那么调用栈中应该先通过getBootstrapResource(name)这个方法查找,为了专注与ClassLoader的getResource方法的逻辑,我们先不细说getBootstrapResource(name),我们先看一下它干了什么,getBootstrapResource(name)方法做的工作就是在"sun.boot.class.path"指定的路径和其他通过接口添加到启动类加载器搜索路径中查找name。一般我们把资源放在classpath路径下,所以,一般是查找不到的。那么接下来执行到的将是getBootstrapResource(name)方法,层层委托先执行启动类加载器的getBootstrapResource(name),然后扩展类加载器的getBootstrapResource(name),然后系统类加载器的getBootstrapResource(name),最后自定义类加载器的getBootstrapResource(name)(这里假设的使用的自定义或者系统类加载器,如果不是,就不搜索下层的加载路径)。扩展类加载器的getBootstrapResource(name)干的事情就是在"java.ext.dirs"指定的路径和其他通过接口添加到扩展类加载器搜索路径中查找name。系统类加载器的getBootstrapResource(name)干的事情就是在"java.class.path"指定的路径和其他通过接口添加到系统类加载器搜索路径中查找name。 如果有兴趣的朋友可以看一下Launcher,URLClassLoader,URLClassPath这3个类的源码,如果深入一点还可以看一下MetaIndex缓存、URLStreamHandler相关的类等。

public Enumeration
getResources(String name) throws IOException { Enumeration[] tmp = new Enumeration[2]; if (parent != null) { tmp[0] = parent.getResources(name); } else { tmp[0] = getBootstrapResources(name); } tmp[1] = findResources(name); return new CompoundEnumeration<>(tmp); }

ClassLoader的getResource是找到一个就不找了,而getResources把所有搜索路径中能找到的name资源都找出来。

ClassLoader提供的一下静态的查找资源方法

public static URL getSystemResource(String name) {        ClassLoader system = getSystemClassLoader();        if (system == null) {            return getBootstrapResource(name);        }        return system.getResource(name);    }

这个方法其实还是调用的还是按ClassLoader的套路来查找,不过调用的加载器已经确定了是系统类加载器(AppclassLoader)而已。

private static URL getBootstrapResource(String name) {        URLClassPath ucp = getBootstrapClassPath();        Resource res = ucp.getResource(name);        return res != null ? res.getURL() : null;    }

只在"sun.boot.class.path"路径下查找name资源。

static URLClassPath getBootstrapClassPath() {        return sun.misc.Launcher.getBootstrapClassPath();    }
public static Enumeration
getSystemResources(String name) throws IOException { ClassLoader system = getSystemClassLoader(); if (system == null) { return getBootstrapResources(name); } return system.getResources(name); }

和getSystemResource(String name)差不多,只是查找搜索路径下所有的name资源而已。

private static Enumeration
getBootstrapResources(String name) throws IOException { final Enumeration
e = getBootstrapClassPath().getResources(name); return new Enumeration
() { public URL nextElement() { return e.nextElement().getURL(); } public boolean hasMoreElements() { return e.hasMoreElements(); } }; }

和getBootstrapResource(String name),只是查找"sun.boot.class.path"下所有的name资源而已。

Class#getResource(String name)

public java.net.URL getResource(String name) {        name = resolveName(name);        ClassLoader cl = getClassLoader0();        if (cl==null) {            // A system class.            return ClassLoader.getSystemResource(name);        }        return cl.getResource(name);    }

我们一般使用Class的获取资源的时候都是使用的我们自定义的类,所以ClassLoader一般都是系统类加载器或者我们自定义的类加载器,所以getResource(String name)基本上就是按照ClassLoader的基本的套路来搜索的。最常见的用法就是加载和Class在同一个包下的资源,这样就不用拼接路径了。Class#getResource("/xxx"),加上"/"这个样的方式基本上就和ClassLoader#getSystemResource(String name)这个静态方法差不多了。

Class#resolveName(String name)

private String resolveName(String name) {        if (name == null) {            return name;        }        if (!name.startsWith("/")) {            Class
c = this; //获取数组的元素类型 while (c.isArray()) { //去掉一个数组维度 c = c.getComponentType(); } String baseName = c.getName(); int index = baseName.lastIndexOf('.'); if (index != -1) { //使用包名拼接上name name = baseName.substring(0, index).replace('.', '/') +"/"+name; } } else { name = name.substring(1); } return name; }

转载于:https://my.oschina.net/u/2474629/blog/799182

你可能感兴趣的文章
改变的六条规则
查看>>
采访Nicole Forsgren博士:DORA与Google就《DevOps促进状态报告》开展合作
查看>>
使用 Python 5 年后,我转向了Go
查看>>
Idris趋近发布1.0版
查看>>
Safari浏览器的智能跟踪预防工作原理
查看>>
Shoutem旨在成为React Native移动应用领域的WordPress
查看>>
gRPC-Web发布,REST又要被干掉了?
查看>>
新手教程:如何改变应用名称
查看>>
基于干净语言和好奇心的敏捷指导
查看>>
XebiaLabs DevOps平台推出软件发布风险和合规性管理功能
查看>>
7道常见的数据分析面试题
查看>>
GitHub Checks API帮助应用实现进一步的持续集成
查看>>
计算机科学家Erik Meijer眼中的Hacker Way工作方式
查看>>
敏捷测试者的担当
查看>>
微服务通信策略
查看>>
用WinForm/WPF代码来为.NET Core 3.0功能投票
查看>>
Netflix:我们为什么要将GraphQL引入前端架构?\n
查看>>
Micronaut for Spring支持Spring Boot应用以Micronaut形式运行
查看>>
腾讯信鸽海量移动推送服务是如何构建的
查看>>
Emacs 简介
查看>>