背景:

某日临近下班,一个同事欲任何类中获取项目绝对路径,不通过Request方式获取,可是始终获取不到预想的路径。于是晚上回家google了一下,误以为是System.getProperty("java.class.path")-未实际进行测试,早上来和同事沟通,提出了使用这个内置方法,结果人家早已验证过,该方法是打印出CLASSPATH环境变量的值。

于是乎,继续google,找到了Class的getResource与getResourceAsStream两个方法。这两个方法会委托给ClassLoader对应的同名方法。以为这样就可以搞定(实际上确实可以搞定),但验证过程中却发生了奇怪的事情。

软件环境:Windows XP、Resin、Tomcat6.0、Myeclipse、JDK1.5

发展:



我的验证思路是这样的:

1、定义一个Servlet,然后在该Servlet中调用Path类的getPath方法,getPath方法返回工程classpath的绝对路径,显示在jsp中。

2、另外在Path类中,通过Class的getResourceAsStream读取当前工程classpath路径中的a.txt文件,写入到getResource路径下的b.txt。

由于时间匆忙,代码没有好好去组织。大致能看出上述两个功能,很简单不做解释。

01 Path.java
02 public class Path
{
03      
04     public String
getPath() 
throws IOException{
05          
06         InputStream
is = 
this.getClass().getResourceAsStream("/a.txt");
07          
08         File
file = 
new File(Path.class.getResource("/").getPath()+"/b.txt");
09          
10         OutputStream
os = 
new FileOutputStream(file);
11         int bytesRead
0;
12         byte[]
buffer = 
new byte[8192];
13         while ((bytesRead
= is.read(buffer, 
08192))
!= -
1)
{
14         os.write(buffer, 0,
bytesRead);
15         }
16         os.close();
17         is.close();
18          
19         return this.getClass().getResource("/").getPath();
20     }
21 }
01 PathServlet.java
02  
03 public class PathServlet extends HttpServlet
{
04     private static final long serialVersionUID
= 4443655831011903288L;
05      
06     public void doGet(HttpServletRequest
request, HttpServletResponse response)
07     throws ServletException,
IOException {
08         Path
path = 
new Path();
09          
10         request.setAttribute("path",
path.getPath());
11         PrintWriter
out = response.getWriter();
12          
13         out.println("Class.getResource('/').getPath():" +
path.getPath());
14     }
15      
16     public void doPost(HttpServletRequest
request, HttpServletResponse response)
17     throws ServletException,
IOException {
18         doGet(request,
response);
19     }
20 }

在此之前使用main函数测试Path.class.getResource("/").getPath()打印出预想的路径为:/D:/work/project/EhCacheTestAnnotation/WebRoot/WEB-INF/classes/

于是将WEB应用部署到Resin下,运行定义好的Servlet,出乎意料的结果是:/D:/work/resin-3.0.23/webapps/WEB-INF/classes/ 。特别奇怪,怎么会丢掉项目名称:EhCacheTestAnnotation呢?

还有一点值得注意,getPath方法中使用getResourceAsStream("/a.txt")却正常的读到了位于下图的a.txt。

然后写到了如下图的b.txt中。代码中是这样实现的:File file = new File(Path.class.getResource("/").getPath()+"/b.txt");本意是想在a.txt文件目录下再写入b.txt。结果却和料想的不一样。

请注意,区别还是丢掉了项目名称。

写的比较乱,稍微总结下:

程序中使用ClassLoader的两个方法:getResourceAsStream和getResource。但是事实证明在WEB应用的场景下却得到了不同的结果。大家别误会啊,看名字他们两个方法肯定不一样,这个我知道,但是getResourceAsStream总会获取指定路径下的文件吧,示例中的参数为"/a.txt",正确读取“/D:/work/resin-3.0.23/webapps/EhCacheTestAnnotation/WEB-INF/classes/ ”下的a.txt,可是将文件写到getResource方法的getPath返回路径的b.txt文件。两个位置的差别在项目名称(EhCacheTestAnnotation)。

这样我暂且得出一个结论:通过getResourceAsStream和getResource两个方法获取的路径是不同的。但是为什么呢?

于是查看了ClassLoader的源码,贴出getResource和getResourceAsStream的源码。

01 public URL
getResource(String name) {
02     URL
url;
03     if (parent
!= 
null)
{
04         url
= parent.getResource(name);
05     else {
06         url
= getBootstrapResource(name);
07     }
08     if (url
== 
null)
{
09         url
= findResource(name);
10     }
11     return url;
12     }
13      
14  
15 public InputStream
getResourceAsStream(String name) {
16     URL
url = getResource(name);
17     try {
18         return url
!= 
null ?
url.openStream() : 
null;
19     catch (IOException
e) {
20         return null;
21     }
22     }

从代码中看,getResourceAsStream将获取URL委托给了getResource方法。天啊,这是怎么回事儿?由此我彻底迷茫了,百思不得其解。

但是没有因此就放弃,继续回想了一遍整个过程:

1、在main函数中,测试getResource与getResourceAsStream是完全相同的,正确的。

2、将其部署到Resin下,导致了getResource与getResourceAsStream获取的路径不一致。

一个闪光点,是不是与web容器有关啊,于是换成Tomcat6.0。OMG,“奇迹”出现了,真的是这样子啊,换成Tomcat就一样了啊!和预想的一致。

在Tomcat下运行结果如下图:

对,这就是我想要的。

因此我对Resin产生了厌恶感,之前也因为在Resin下程序报错,在Tomcat下正常运行而纠结了好久。记得看《松本行弘的程序世界》中对C++中的多继承是这样评价的(大概意思):多重继承带来的负面影响多数是由于使用不当造成的。是不是因为对Resin使用不得当才使得和Tomcat下得到不同的结果。

最终,在查阅Resin配置文件resin.conf时候在<host-default>标签下发现了这样一段:

1 <class-loader>
2         <compiling-loader path="webapps/WEB-INF/classes"/>
3         <library-loader path="webapps/WEB-INF/lib"/>
4  </class-loader>

其中的compiling-loader很可能与之有关,遂将其注释掉,一切正常。担心是错觉,于是将compiling-loader的path属性改成:webapps/WEB-INF/classes1,然后运行pathServlet,b.txt位置如下图:

确实与compiling-loader有关。

结局:



终于通过将<class-loader>标签注释掉,同样可以在Resin中获取“预想”的路径。验证了的确是使用Resin的人出了问题。

疑问:



但是没有这样就结束,我继续对getResource的源码进行了跟进,由于能力有限,没有弄清楚getResource的原理。

最终留下了两个疑问:

1、如果追踪到getResource方法的最底层(也许是JVM层面),它实现的原理是什么?

2、为何Resin中<class-loader>的配置会对getResource产生影响,但是对getResourceAsStream毫无影响(getResourceAsStream可是将获取路径委托给getResource的啊)。还是这里我理解或者使用错误了?

在这里也请明白人指明。

关于ClassLoader中getResource与getResourceAsStream的疑问的更多相关文章

  1. 【知识碎片】getResource和getResourceAsStream

    1. 前言 在Java中获取资源的时候,经常用到getResource和getResourceAsStream,本文总结一下这两种获取资源文件的路径差异. 2.Class.getResource(St ...

  2. getResource和getResourceAsStream

    1. 前言 在Java中获取资源的时候,经常用到getResource和getResourceAsStream,本文总结一下这两种获取资源文件的路径差异. 2.Class.getResource(St ...

  3. java读取配置文件的推荐方法getResource、getResourceAsStream

    在java开发中经常会读取配置文件,如果把文件路径写死,就太LOW了,也不符合编码规范. 在网上找了一些资料后,发现有两种方法:xxx.class.getResource("")  ...

  4. Class和ClassLoader的getResource方法对比

    最近在看写Spring的源代码,里面有好多地方都用到了Class和ClassLoader类的getResource方法来加载资源文件.之前对这两个类的这个方法一知半解,概念也很模糊,这边做下整理,加深 ...

  5. Classloader中loadClass()方法和Class.forName()区别

    Classloader中loadClass()方法和Class.forName()都能得到一个class对象,那这两者得到的class对象有什么区别呢 1.java类装载的过程 Java类装载有三个步 ...

  6. getResource()和getResourceAsStream以及路径问题【转】【补】

    一 getResource 用JAVA获取文件,听似简单,但对于很多像我这样的新人来说,还是掌握颇浅,用起来感觉颇深,大常最经常用的,就是用JAVA的File类,如要取得c:/test.txt文件,就 ...

  7. getResource()和getResourceAsStream以及路径问题

    用JAVA获取文件,听似简单,但对于很多像我这样的新人来说,还是掌握颇浅,用起来感觉颇深,大常最经常用的,就是用JAVA的File类,如要取得c:/test.txt文件,就会这样用File file ...

  8. [转]getResource()和getResourceAsStream以及路径问题

    原文链接:http://blog.sina.com.cn/s/blog_4b5bc0110100g22w.html 用JAVA获取文件,听似简单,但对于很多像我这样的新人来说,还是掌握颇浅,用起来感觉 ...

  9. getResource()和getResourceAsStream以及路径问题(转)

    用JAVA获取文件,听似简单,但对于很多像我这样的新人来说,还是掌握颇浅,用起来感觉颇深,大常最经常用的,就是用JAVA的File类,如要取得c:/test.txt文件,就会这样用File file ...

  10. class.getResource()和getResourceAsStream的用法

    转自:http://blog.csdn.net/lcj8/article/details/3502849 class.getResource()的用法 用JAVA获取文件,听似简单,但对于很多像我这样 ...

随机推荐

  1. 熔断、限流、降级 —— SpringCloud Hystrix

    概述 Hystrix 为 微服务架构提供了一整套服务隔离.服务熔断和服务降级的解决方案.它是熔断器的一种实现,主要用于解决微服务架构的高可用及服务雪崩等问题 Hystrix 的特性如下: 服务熔断:H ...

  2. 宿舍管理系统——单链表+结构体实现入住、退房和查询功能(C语言版)

    本程序的编译和运行环境如下(如果有运行方面的问题欢迎在评论区留言,也欢迎直接加QQ:2961439733,备注博客园或CSDN即可): 编辑工具:Dev-C++(版本:5.11.0.0) 编译器:TD ...

  3. elementUI实现月、季度、年 时间选择框

    elementUI实现月.季度.年 时间选择框 一.通过 el-date-picker 组件来实现月.年的选择 代码如下: <el-date-picker v-if="dateType ...

  4. 即构音视频 Express Flutter SDK 全面支持空安全

    Flutter 是一套移动 UI 框架,可以快速在 iOS.Android 上构建高质量的原生用户界面.作为其官方语言 Dart 是类型安全的,当开发者获取变量的时候,编译器可以保证变量的类型,但类型 ...

  5. Maven高级——多环境配置与应用

    多环境配置与应用 开发步骤 定义多环境 <!--配置多环境--> <profiles> <!--开发环境--> <profile> <id> ...

  6. ROS基础入门——实操教程

    ROS基础入门--实操教程 前言 本教程实操为主,少说书.可供参考的文档中详细的记录了ROS的实操和理论,只是过于详细繁杂了,看得脑壳疼,于是做了这个笔记. Ruby Rose,放在这里相当合理 前言 ...

  7. 全网最适合入门的面向对象编程教程:56 Python字符串与序列化-正则表达式和re模块应用

    全网最适合入门的面向对象编程教程:56 Python 字符串与序列化-正则表达式和 re 模块应用 摘要: Python 的 re 模块提供了强大的正则表达式操作功能,用于在字符串中搜索.匹配.替换等 ...

  8. opengl在编译的过程中,glad使用

    我在编译的过程中,遇到:无法找到 -lglad这个错误.最后才发现对于glad的使用不能用-lglad.因为我们通过glad的在线服务可以得到一些文件,其中glad.c文件我们是需要放在我们的项目下面 ...

  9. 封装setItem 和 getItem 本地存储

    store.js 文件 按需导出setItem 和 getItem 函数 :在utils文件里面 : export const setItem = (key, value) => { // 复杂 ...

  10. Android复习(四)权限—>请求应用权限

    每款 Android 应用都在访问受限的沙盒中运行.如果应用需要使用其自己的沙盒外的资源或信息,则必须请求相应权限. 要声明您的应用需要某项权限,您可以在应用清单中列出该权限,然后在运行时请求用户批准 ...