上文提到应老板要求开发一个测试工具能方便的加载存于文件中的测试参数,当时考虑既然是测试,把测试参数文件和测试类放在一起岂不是很方便,但是老板说:我的需求是你把测试参数文件放到统一文件夹下比如resources目录下,当然你做的这个也可以保留。 好吧,既然老板都说了,我就开干呗,主要问题是如何在CaseloaderSupplier中获取resources文件夹的路径,很多人的第一反应是直接new File("/src/test/resources")不行吗? 当然是不行的,因为通过maven编译以后会把资源文件拷贝到target目录下边,直接通过file是定位不到的。唯一的解决方案(我目前感觉是唯一的)是使用Java提供的抽象方法:resources。可以通过Class.getResources()或者ClassLoader.getResources()。 Class.getResource底层也是将加载任务委托给ClassLoader做的。 当然,在做这个之前对resource不是太熟悉,只是偶尔会调用一下接口,所以感觉还是详细了解一下resource心里有底一点,google了很多文章,大概意思是getResource("")获取的是当前调用类路径,比如当前类路径为file:/Users/caiyao/workservice/test-caseloader/target/classes/base/ (打包以后的),getResource("/")这个获取的是classpath的根目录,比如file:/Users/caiyao/workservice/test-caseloader/target/classes/(注意:没有包名)。问题是老板要求测试参数类要在test包下的resources目录里,这样获取的是main的包下面的,那么怎样才能让在测试的时候(执行@Test方法)加载test包下的资源呢? 当然在思考这个问题之前,我还是在执行@Test时试了一下,令人迷惑的是,在执行@Test的时候拿到的已经是test包下的资源了!!!file:/Users/caiyao/workservice/test-caseloader/target/test-classes/ 如何办到的,详细看下源码:

首先看下getResource方法

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);}c1.getResource(name)这句可以看出Class是把获取资源委托给ClassLoader来执行的。进入ClassLoader的getResoure方法(java.lang.ClassLoader#getResource):
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;}从这里可以看出Java获取资源和加载类是同样的道理,使用的是双亲委托机制,首先执行父类加载器的getResource方法,如果父类加载器为空则执行Bootstrap的类加载器,如果父类加载的getResource没有获取到值再从自己的上下文查找资源,该方法真正执行操作的是findResource(name)这个步骤,进入java.lang.ClassLoader#findResource,ClassLoader的默认实现直接返回null,应该看ClassLoader的实现类URLClassLoader(从很少的源码阅读经验中我发现通过IDE断点调试能避免很多不必要的代码阅读,让注意力更集中于关注的这条线),从调试的过程可以看出,最后的结果是由AppClassLoader返回,这点很重要,后面要用到:
public URL findResource(final String name) {    /*     * The same restriction to finding classes applies to resources     */    URL url = AccessController.doPrivileged(        new PrivilegedAction<URL>() {            public URL run() {                return ucp.findResource(name, true);            }        }, acc);

    return url != null ? ucp.checkURL(url) : null;}
AccessController.doPrivileged方法是一个native方法,无法通过IDE进去调试,但是可以对ucp.findResource(name,true)打断点,通过调试可以看出正是该方法的执行返回了file:/Users/caiyao/workservice/test-caseloader/target/test-classes/,要注意一点这里调用的findResource是通过该类的一个成员属性ucp中进去的,ucp持有该方法的一些上下文,断点进sun.misc.URLClassPath#findResource方法:
public URL findResource(String var1, boolean var2) {    int[] var4 = this.getLookupCache(var1);

    URLClassPath.Loader var3;    for(int var5 = 0; (var3 = this.getNextLoader(var4, var5)) != null; ++var5) {        URL var6 = var3.findResource(var1, var2);        if (var6 != null) {            return var6;        }    }

    return null;}从该方法可以看出URL是通过this.getNextLoader(var4, var5))返回的Loader里得到的,而this正是上面URLClassLoader的成员属性ucp,回到URLClassLoader类中寻找ucp的初始化代码,可以看到一个单参数的构造方法:
public URLClassLoader(URL[] urls) {    super();    // this is to make the stack depth consistent with 1.1    SecurityManager security = System.getSecurityManager();    if (security != null) {        security.checkCreateClassLoader();    }    ucp = new URLClassPath(urls);    this.acc = AccessController.getContext();}然后需要找到urls是重什么地方传递过来的,我以前大致了解过Java的类加载过程,知道类加载器是由AppClassLoader / ExtClassLoader / BootstrapClassLoader这样的一个层级结构组成,它们由Launcher初始化,从上面已经得知最后的资源路径是由AppClassLoader得到,所以可以猜测AppClassLoader是URLClassLoader的子类,可能在Launcher中初始化,从Launcher中可以找到如下代码:
static class AppClassLoader extends URLClassLoader {    final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);

    public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {        final String var1 = System.getProperty("java.class.path");        final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);        return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<Launcher.AppClassLoader>() {            public Launcher.AppClassLoader run() {                URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);                return new Launcher.AppClassLoader(var1x, var0);            }        });    }证实了猜想,AppClassLoader从java.class.path变量中获取资源路径,而java.class.path这个变量由在什么地方设置的呢? 肯定在执行java命令时设置的参数,可以 ps aux |grep Test1(Test1是我测试类的名字)查看到当前执行的测试进程的信息,果然在执行Java命令时把如下两个路径都加到了classpath中: 

/Users/caiyao/workservice/test-caseloader/target/test-classes
/Users/caiyao/workservice/test-caseloader/target/classes

而且test-classes在前面,从sun.misc.URLClassPath#findResource中的代码可以看出,在遍历classpath的时候,一旦发现了一个存在就不会再往后遍历,所以在执行test方法的时候只会拿到test目录,至于为什么在执行Test的时候自动把test的资源路径加到了classpath里,这个我没有再深入研究,猜测应该是maven做的操作,因为target这个目录就是maven的规定。

终于找到原因,可以安心的写代码了~~~

转载于:https://www.cnblogs.com/caiyao/p/9306719.html

Java是如何加载资源文件的?(源码解毒)相关推荐

  1. java加载资源文件的3种方式

    使用绝对路径加载(不推荐) 直接写死路径,使用FileInputStream加载资源文件,但是路径就不能动了 public static void main(String[] args) throws ...

  2. PyQt5教程(八)——实现QQ登录界面(二、加载资源文件)

                                         实现QQ登录界面--加载资源文件 一.创建资源文件: 上篇文章创建了QQ登录界面,本篇介绍创建并加载资源文件. 1.创建资源文 ...

  3. Wxwidget 动态加载资源文件

    一.创建自己的资源文件 你可以从一个xml文件中加载对话框,frame窗口,菜单条,工具条等等.不用c++代码去实现,这符合代码界面和代码分离的原则.我们可以使用DialogBlocks,XRCed和 ...

  4. 易优cms load 加载资源文件标签使用方法

    [基础用法] 标签:load 描述:资源文件加载,比如:css/js 用法: {eyou:load href='/static/js/common.js' ver='on' /} 属性: file=' ...

  5. yepnope.js 异步加载资源文件

    yepnope.js是一个能够根据输入条件来选择性异步加载资源文件的js脚本,可以在页面上仅加载用户需要的js/css. yepnope的优点: 可以同时处理javascript以及css 能够按条件 ...

  6. yepnope.js – 异步加载资源文件

    yepnope.js是一个能够根据输入条件来选择性异步加载资源文件的js脚本,可以在页面上仅加载用户需要的js/css. 典型代码示例 yepnope({test : Modernizr.geoloc ...

  7. 图片加载框架Picasso - 源码分析

    简书:图片加载框架Picasso - 源码分析 前一篇文章讲了Picasso的详细用法,Picasso 是一个强大的图片加载缓存框架,一个非常优秀的开源库,学习一个优秀的开源库,,我们不仅仅是学习它的 ...

  8. OSG —— 笔记2 - 加载模型(附源码)

    效果         相关文章      OSG -- 笔记1 - 指令调用模型      OSG -- 笔记2 - 加载模型(附源码)      OSG -- 笔记3 - 绘制矩形(附源码)     ...

  9. java毕业生设计高校教学资源系统计算机源码+系统+mysql+调试部署+lw

    java毕业生设计高校教学资源系统计算机源码+系统+mysql+调试部署+lw java毕业生设计高校教学资源系统计算机源码+系统+mysql+调试部署+lw 本源码技术栈: 项目架构:B/S架构 开 ...

最新文章

  1. GoldenGate复制单表开并行
  2. redis api java 正则_java代码怎么正则删除redis的数据
  3. 模块导入---如何在一个文件中导入其它模块,来调用它的变量、函数等,以节省代码量...
  4. Scanner类的基本总结
  5. Oracle PL/SQL中如何使用%TYPE和%ROWTYPE
  6. kafka副本注意点
  7. AS 中 Plugin for Gradle 和 Gradle 之间的版本对应关系
  8. Oracle数据库物理存储结构管理遇到的问题与解决
  9. 【笔记】用正则匹配字符串的方法摘抄
  10. 没有计算机基础可以学python-零基础,没有编程和计算机基础,究竟该怎么自学python?...
  11. 【格雷码】LeetCode 89. Gray Code
  12. hp RAID卡 命令行管理
  13. 计算机软件评估资料,软件项目工作量评估方法 计算机软件及应用 IT计算机 专业资料.doc...
  14. 移动前端开发与WEB前端开发有什么联系与区别?
  15. 英语学习口诀大全be 的用法口诀
  16. Python学习笔记-系统性能信息模块psutil
  17. 在线查询IP及IP信息
  18. 舆情监测系统适用哪些行业,如何选择舆情监测系统?
  19. 请问转换音乐格式的软件有哪些
  20. Python绘制六角星、多角星、小太阳、小风车《打包好的各种游戏源码,画图源码》

热门文章

  1. 修改ActiveProcessLinks链表隐藏进程
  2. Linux获取毫秒级时间
  3. 读史以明志,把握好自己的明天
  4. linux下openssl编程
  5. [转]如何编写 INF 文件
  6. jdbctemplate 开启事务_来,讲讲Spring事务有哪些坑?
  7. undocumented windows nt pdf_我用Python写了一个PDF转换器!
  8. jQuery 属性操作——案例:购物车案例模块
  9. Map .NET Concepts to the Lightning Platform——list,set,map
  10. 将oracle select * from all_tab_comments where comments like ‘%XXXX%‘查询结果转为select语句