`
cxmin121
  • 浏览: 18990 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

转:关于ClassLoader中getResource与getResourceAsStream的疑问

 
阅读更多
背景:

某日临近下班,一个同事欲任何类中获取项目绝对路径,不通过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。

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

view source
print?
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, 0, 8192)) != -1) {
14         os.write(buffer, 0, bytesRead);
15         }
16         os.close();
17         is.close();
18         
19         return this.getClass().getResource("/").getPath();
20     }
21 }

view source
print?
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的源码。


view source
print?
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>标签下发现了这样一段:


view source
print?
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的啊)。还是这里我理解或者使用错误了?


在这里也请明白人指明。

原文地址: http://www.oschina.net/question/129471_34225
分享到:
评论

相关推荐

    java classloader classpath 张孝祥

    #### 四、`getResource`和`getResourceAsStream`方法 ##### 4.1 方法介绍 `ClassLoader`提供了两个重要的方法用于资源定位: - `public URL getResource(String name)`:返回一个`URL`对象,表示名为`name`的资源...

    java文件路径获取

    - 当使用`ClassLoader.getResource`或`ClassLoader.getResourceAsStream`时,路径必须以 `/` 开头,否则可能无法找到资源。 - `getClassLoader().getResource("/")`会返回`classpath`的根目录,但`getClassLoader()....

    SpringBoot如何读取war包jar包和Resource资源

    在读取资源文件时,经常使用 Class.getResource() 或 ClassLoader.getResource() 方法。两种方法都可以读取资源文件,但是它们的使用场景和返回值不同。 Class.getResource() 方法返回 URL 对象,表示资源文件的...

    Java 读取资源文件

    2. 使用`ClassLoader.getResource()`和`ClassLoader.getResourceAsStream()` - 类加载器(`ClassLoader`)也可以用来获取资源,这对于不是从特定类而是从全局类路径中获取资源很有用。例如: ```java ClassLoader...

    Java-recourse.rar_java读取recouse

    你可以通过`Thread.currentThread().getContextClassLoader().getResource()`或`YourClass.class.getResource()`来获取`URL`对象,然后使用`InputStream`读取资源内容。例如: ```java URL resource = getClass()....

    读取jar文件

    - **方法**:类加载器提供了`getResource`和`getResourceAsStream`等方法用于查找和读取资源。 2. **资源文件的路径表示**: - 在jar包内部,资源文件的路径通常是以`/`开头的相对路径表示。例如,如果资源文件...

    jar包中类的加载及jar中资源的使用

    3. `Resources`类:Apache Commons Lang库中的`Resources`类提供了方便的方法,如`Resources.getResource()`和`Resources.asByteSource()`,用于更便捷地读取`jar`内的资源。 4. Spring框架:Spring框架的`Resource...

    java类动态获取相对路径

    `ClassLoader`提供了`getResource`和`getResourceAsStream`方法来获取资源的URL或输入流。例如,如果我们有一个名为`res.txt`的文件与`MyClass`同级,可以这样获取其URL: ```java URL resourceUrl = classLoader...

    Java Resource

    4. **尝试使用Resources类**:自Java 6开始,`java.nio`包下的`Resources`类提供了一种更简洁的方式来获取类路径中的资源,如`Resources.getResource("filename")`。 5. **资源的国际化**:在处理多语言环境时,...

    Tomcat 5.0.18 ClassLoader source code insight

    `WebappClassLoader`实现了`getResource`和`getResourceAsStream`方法,以便于获取Web应用中的资源。 4. **同步机制**:为了保证并发环境下的安全,`WebappClassLoader`使用锁机制来控制对类加载的访问,避免了多...

    JAVA获取文件绝对路径的方法

    当你需要获取与类相关的资源文件的绝对路径时,可以使用 `Class.getResource()` 或 `Class.getResourceAsStream()` 方法。这些方法返回一个 `java.net.URL` 对象,可以通过调用 `getPath()` 来获取路径字符串。例如...

    javaweb 读取 classes 下的文件

    `ServletContext`提供了`getResourceAsStream()`和`getResource()`方法。例如: ```java ServletContext context = servlet.getServletContext(); InputStream inputStream = context.getResourceAsStream("/WEB-...

    读取资源文件工具类

    `ClassLoader`的`getResource()`方法返回资源文件的`URL`,可以进一步处理。 ```java URL url = getClass().getClassLoader().getResource("config.properties"); File file = new File(url.getFile()); // ...

    关于读取Src下配置文件的那个传说

    标题“关于读取Src下配置文件的那个传说”指的是在软件开发过程中,如何从项目的源代码目录(通常是`src`)中读取配置文件的过程。在Java、Python、C#等编程语言中,读取配置文件是常见的操作,用于加载应用程序的...

    IOUtils组件,搭配本文项目专用

    这段代码首先通过`Thread.currentThread().getContextClassLoader().getResource()`或`getResourceAsStream()`方法找到类路径下的资源,然后使用IOUtils的`toString()`方法将输入流转换为字符串,这样就能方便地读取...

    从java jar文件包中读资源的常用方法

    类加载器提供了`getResource`和`getResourceAsStream`等方法,用于从类路径中查找资源。 例如: ```java InputStream is = YourClass.class.getClassLoader().getResourceAsStream("FileName"); ``` 这里,`Your...

    Java路径问题最终解决方案.doc

    在Java中,可以使用ClassLoader的`getResource()`和`getResourceAsStream()`方法,以相对路径的方式查找类路径下的资源。 四、相对于当前用户目录的相对路径 例如:相对于System.getProperty("user.dir")返回的路径...

    Java路径问题

    要访问这些资源,可以使用`ClassLoader`类的静态方法`getResource`和`getResourceAsStream`。 **示例代码**: ```java URL resource = getClass().getClassLoader().getResource("com/test/resource.txt"); ``` ...

    java中File的相对路径与绝对路径总结

    - 除了通过类的`getResource()`方法外,还可以通过`ClassLoader`来获取资源: ```java String f = Hello.class.getClassLoader().getResource("4.txt").getPath(); ``` - 需要注意的是,这种方式同样只能获取到...

Global site tag (gtag.js) - Google Analytics