What’s the difference between:
InputStream is = this.getClass().getClassLoader().getResourceAsStream(fileName)
and
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName)
and
InputStream is = this.getClass().getResourceAsStream(fileName)
When are each one more appropriate to use than the others?
The file that I want to read is in the classpath as my class that reads the file. My class and the file are in the same jar and packaged up in an EAR file, and deployed in WebSphere 6.1.
There are subtle differences as to how the fileName you are passing is interpreted. Basically, you have 2 different methods: ClassLoader.getResourceAsStream()
and Class.getResourceAsStream()
. These two methods will locate the resource differently.
In Class.getResourceAsStream(path)
, the path
is interpreted as a path local to the package of the class you are calling it from. For example calling, String.getResourceAsStream("myfile.txt")
will look for a file in your classpath at the following location: “java/lang/myfile.txt
“. If your path starts with a /
, then it will be considered an absolute path, and will start searching from the root of the classpath. So calling String.getResourceAsStream("/myfile.txt")
will look at the following location in your class path ./myfile.txt
.
这就是Class.getResourceAsStream(path)
让你疑惑的地方,/
开头不是代表系统的根目录。
ClassLoader.getResourceAsStream(path)
will consider all paths to be absolute paths. So calling String.getClassLoader().getResourceAsString("myfile.txt")
and String.getClassLoader().getResourceAsString("/myfile.txt")
will both look for a file in your classpath at the following location: ./myfile.txt
.
Everytime I mention a location in this post, it could be a location in your filesystem itself, or inside the corresponding jar file, depending on the Class
and/or ClassLoader
you are loading the resource from.
In your case, you are loading the class from an Application Server, so your should use Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName)
instead of this.getClass().getClassLoader().getResourceAsStream(fileName)
. this.getClass().getResourceAsStream()
will also work.
Warning for users of Tomcat 7 and below
One of the answers to this question states that my explanation seems to be incorrect for Tomcat 7. I’ve tried to look around to see why that would be the case.
So I’ve looked at the source code of Tomcat’s WebAppClassLoader
for several versions of Tomcat. The implementation of findResource(String name)
(which is utimately responsible for producing the URL to the requested resource) is virtually identical in Tomcat 6 and Tomcat 7, but is different in Tomcat 8.
In versions 6 and 7, the implementation does not attempt to normalize the resource name. This means that in these versions, classLoader.getResourceAsStream("/resource.txt")
may not produce the same result as classLoader.getResourceAsStream("resource.txt")
event though it should (since that what the Javadoc specifies).
In version 8 though, the resource name is normalized to guarantee that the absolute version of the resource name is the one that is used. Therefore, in Tomcat 8, the two calls described above should always return the same result.
As a result, you have to be extra careful when using ClassLoader.getResourceAsStream()
or Class.getResourceAsStream()
on Tomcat versions earlier than 8. And you must also keep in mind that class.getResourceAsStream("/resource.txt")
actually calls classLoader.getResourceAsStream("resource.txt")
(the leading /
is stripped).
I’m pretty sure that getClass().getResourceAsStream("/myfile.txt")
behaves differently from getClassLoader().getResourceAsStream("/myfile.txt")
.
They don’t behave differenty. Actually, the javadoc for Class.getResourceAsStream(String)
says the following thing: “This method delegates to this object’s class loader.”, and then gives a bunch of rules on how it converts a relative path to an absolute path before delegating to the classloader.
Look at the actual source. Class.getResourceAsStream
strips off the leading forward slash if you provide an absolute path.
Which makes it behave exactly the same as ClassLoader.getResourceAsStream()
since the latter interprets all paths as absolute, whether they start with a leading slash or not. So as long as you path is absolute, both methods behave identically. If your path is relative, then the behavior is different.