背景:
某日临近下班,一个同事欲任何类中获取项目绝对路径,不通过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。
由于时间匆忙,代码没有好好去组织。大致能看出上述两个功能,很简单不做解释。
04 |
public String
getPath() throws IOException{ |
06 |
InputStream
is = this.getClass().getResourceAsStream("/a.txt"); |
08 |
File
file = new File(Path.class.getResource("/").getPath()+"/b.txt"); |
10 |
OutputStream
os = new FileOutputStream(file); |
12 |
byte[]
buffer = new byte[8192]; |
13 |
while ((bytesRead
= is.read(buffer, 0, 8192))
!= -1)
{ |
14 |
os.write(buffer, 0,
bytesRead); |
19 |
return this.getClass().getResource("/").getPath(); |
03 |
public class PathServlet extends HttpServlet
{ |
04 |
private static final long serialVersionUID
= 4443655831011903288L; |
06 |
public void doGet(HttpServletRequest
request, HttpServletResponse response) |
07 |
throws ServletException,
IOException { |
08 |
Path
path = new Path(); |
10 |
request.setAttribute("path",
path.getPath()); |
11 |
PrintWriter
out = response.getWriter(); |
13 |
out.println("Class.getResource('/').getPath():" +
path.getPath()); |
16 |
public void doPost(HttpServletRequest
request, HttpServletResponse response) |
17 |
throws ServletException,
IOException { |
18 |
doGet(request,
response); |
在此之前使用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) { |
04 |
url
= parent.getResource(name); |
06 |
url
= getBootstrapResource(name); |
09 |
url
= findResource(name); |
15 |
public InputStream
getResourceAsStream(String name) { |
16 |
URL
url = getResource(name); |
18 |
return url
!= null ?
url.openStream() : null; |
19 |
} catch (IOException
e) { |
从代码中看,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>标签下发现了这样一段:
2 |
<compiling-loader path="webapps/WEB-INF/classes"/> |
3 |
<library-loader path="webapps/WEB-INF/lib"/> |
其中的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的啊)。还是这里我理解或者使用错误了?
在这里也请明白人指明。
- 【知识碎片】getResource和getResourceAsStream
1. 前言 在Java中获取资源的时候,经常用到getResource和getResourceAsStream,本文总结一下这两种获取资源文件的路径差异. 2.Class.getResource(St ...
- getResource和getResourceAsStream
1. 前言 在Java中获取资源的时候,经常用到getResource和getResourceAsStream,本文总结一下这两种获取资源文件的路径差异. 2.Class.getResource(St ...
- java读取配置文件的推荐方法getResource、getResourceAsStream
在java开发中经常会读取配置文件,如果把文件路径写死,就太LOW了,也不符合编码规范. 在网上找了一些资料后,发现有两种方法:xxx.class.getResource("") ...
- Class和ClassLoader的getResource方法对比
最近在看写Spring的源代码,里面有好多地方都用到了Class和ClassLoader类的getResource方法来加载资源文件.之前对这两个类的这个方法一知半解,概念也很模糊,这边做下整理,加深 ...
- Classloader中loadClass()方法和Class.forName()区别
Classloader中loadClass()方法和Class.forName()都能得到一个class对象,那这两者得到的class对象有什么区别呢 1.java类装载的过程 Java类装载有三个步 ...
- getResource()和getResourceAsStream以及路径问题【转】【补】
一 getResource 用JAVA获取文件,听似简单,但对于很多像我这样的新人来说,还是掌握颇浅,用起来感觉颇深,大常最经常用的,就是用JAVA的File类,如要取得c:/test.txt文件,就 ...
- getResource()和getResourceAsStream以及路径问题
用JAVA获取文件,听似简单,但对于很多像我这样的新人来说,还是掌握颇浅,用起来感觉颇深,大常最经常用的,就是用JAVA的File类,如要取得c:/test.txt文件,就会这样用File file ...
- [转]getResource()和getResourceAsStream以及路径问题
原文链接:http://blog.sina.com.cn/s/blog_4b5bc0110100g22w.html 用JAVA获取文件,听似简单,但对于很多像我这样的新人来说,还是掌握颇浅,用起来感觉 ...
- getResource()和getResourceAsStream以及路径问题(转)
用JAVA获取文件,听似简单,但对于很多像我这样的新人来说,还是掌握颇浅,用起来感觉颇深,大常最经常用的,就是用JAVA的File类,如要取得c:/test.txt文件,就会这样用File file ...
- class.getResource()和getResourceAsStream的用法
转自:http://blog.csdn.net/lcj8/article/details/3502849 class.getResource()的用法 用JAVA获取文件,听似简单,但对于很多像我这样 ...
随机推荐
- Mac m1 安装 scrcpy
前提:已经安装 brew 1. 设定 HOMEBREW_BOTTLE_DOMAIN(不设定的时候 ,会遇到报错 Bottle missing, falling back to the default ...
- Docker 优化NUXT镜像体积
FROM node:xxx-alpine # 环境变量赋值 ENV 参数名 参数值 RUN mkdir -p /app COPY ./app/package.json /app/package.jso ...
- Kubelet证书自动续签(为 kubelet 配置证书轮换)
1.概述 Kubelet 使用证书进行 Kubernetes API 的认证. 默认情况下,这些证书的签发期限为一年,所以不需要太频繁地进行更新. Kubernetes 包含特性 Kubelet 证书 ...
- 6.24.2 数据库&漏洞口令&应急取证
windows日志分析神器 logonTracer-外内网日志 github下载:#JPCERTCC/LogonTracer:通过可视化和分析 Windows 事件日志来调查恶意 Windows 登录 ...
- 一步一步将PlantUML类图导出为自定义格式的XMI文件
一步一步将PlantUML类图导出为自定义格式的XMI文件 说明: 首次发表日期:2024-09-08 PlantUML官网: https://plantuml.com/zh/ PlantUML命令行 ...
- Vert.x HttpClient调用后端服务时使用Idle Timeout和KeepAlive Timeout的行为分析
其实网上有大量讨论HTTP长连接的文章,而且Idle Timeout和KeepAlive Timeout都是HTTP协议上的事情,跟Vert.x本身没有太大关系,只不过最近在项目上遇到了一些问题,用到 ...
- EntityFramework Core并发迁移解决方案
场景 目前一个项目中数据持久化采用EF Core + MySQL,使用CodeFirst模式开发,并且对数据进行了分库,按照目前颗粒度分完之后,大概有一两百个库,每个库的数据都是相互隔离的. 借鉴了G ...
- C# WebSocket Servers -- Fleck、SuperSocket、TouchSocke
最近在维护老项目,感觉内存一直都有问题,定位到问题是WebSocketServer的问题,了解了 Fleck.SuperSocket.TouchSocke 等开源项目,这里记录一下. .net5..n ...
- Haproxy详解以及基于Haproxy的高可用实战
1.Haproxy与Keepalived VRRP 介绍 软件:haproxy 主要是做负载均衡的7层,也可以做4层负载均衡 apache也可以做7层负载均衡,但是很麻烦.实际工作中没有人用. ngi ...
- WebBrowser中打开新页面
要求新打开的网页受控于WebBrowser 解决办法很简单,分两情况,一是在当前WebBrowser中打开新页面,二是在新Form中的WebBrowser中打开新页面: public Form1() ...