What is the difference between Class.getResource() and ClassLoader.getResource()?
I wonder what the difference is between Class.getResource()
and ClassLoader.getResource()
?
edit: I especially want to know if any caching is involved on file/directory level. As in "are directory listings cached in the Class version?"
AFAIK the following should essentially do the same, but they are not:
getClass().getResource()
getClass().getClassLoader().getResource()
I discovered this when fiddling with some report generation code that creates a new file in WEB-INF/classes/
from an existing file in that directory. When using the method from Class, I could find files that were there at deployment using getClass().getResource()
, but when trying to fetch the newly created file, I recieved a null object. Browsing the directory clearly shows that the new file is there. The filenames were prepended with a forward slash as in "/myFile.txt".
The ClassLoader
version of getResource()
on the other hand did find the generated file. From this experience it seems that there is some kind of caching of the directory listing going on. Am I right, and if so, where is this documented?
From the API docs on Class.getResource()
Finds a resource with a given name. The rules for searching resources associated with a given class are implemented by the defining class loader of the class. This method delegates to this object's class loader. If this object was loaded by the bootstrap class loader, the method delegates to ClassLoader.getSystemResource(java.lang.String).
To me, this reads "Class.getResource is really calling its own classloader's getResource()". Which would be the same as doing getClass().getClassLoader().getResource()
. But it is obviously not. Could someone please provide me with some illumination into this matter?
Class.getResource
can take a "relative" resource name, which is treated relative to the class's package. Alternatively you can specify an "absolute" resource name by using a leading slash. Classloader resource paths are always deemed to be absolute.
So the following are basically equivalent:
foo.bar.Baz.class.getResource("xyz.txt");
foo.bar.Baz.class.getClassLoader().getResource("foo/bar/xyz.txt");
And so are these (but they're different from the above):
foo.bar.Baz.class.getResource("/data/xyz.txt");
foo.bar.Baz.class.getClassLoader().getResource("data/xyz.txt");
The first call searches relative to the .class
file while the latter searches relative to the classpath root.
To debug issues like that, I print the URL:
System.out.println( getClass().getResource(getClass().getSimpleName() + ".class") );
Had to look it up in the specs:
Class.getResource(String resource)
ClassLoader.getResource(String resource)
Class's getResource() - documentation states the difference:
This method delegates the call to its class loader, after making these changes to the resource name: if the resource name starts with "/", it is unchanged; otherwise, the package name is prepended to the resource name after converting "." to "/". If this object was loaded by the bootstrap loader, the call is delegated to ClassLoader.getSystemResource.
All these answers around here, as well as the answers in this question, suggest that loading absolute URLs, like "/foo/bar.properties" treated the same by class.getResourceAsStream(String)
and class.getClassLoader().getResourceAsStream(String)
. This is NOT the case, at least not in my Tomcat configuration/version (currently 7.0.40).
MyClass.class.getResourceAsStream("/foo/bar.properties"); // works!
MyClass.class.getClassLoader().getResourceAsStream("/foo/bar.properties"); // does NOT work!
Sorry, I have absolutely no satisfying explanation, but I guess that tomcat does dirty tricks and his black magic with the classloaders and cause the difference. I always used class.getResourceAsStream(String)
in the past and haven't had any problems.
PS: I also posted this over here
To answer the question whether there is any caching going on.
I investigated this point further by running a stand-alone Java application that continuously loaded a file from disk using the getResourceAsStream ClassLoader method. I was able to edit the file, and the changes were reflected immediately, i.e., the file was reloaded from disk without caching.
However: I'm working on a project with several maven modules and web projects that have dependencies on each other. I'm using IntelliJ as my IDE to compile and run the web projects.
I noticed that the above seemed to no longer hold true, the reason being that the file that I was being loaded is now baked into a jar and deployed to the depending web project. I only noticed this after trying to change the file in my target folder, to no avail. This made it seem as though there was caching going on.