What is the difference between Class.getResource() and ClassLoader.getResource()?

Viewed 173285

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?

9 Answers

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

Since Java 9 there is a pitfall with ClassLoader#getResource when running on the module path. Because of this, I would never use ClassLoader#getResource in new code.

If your code is in a named module, and you use ClassLoader#getResource, your code can fail to retrieve a resource even if the resource is in the same module. This is very surprising behavior.

I experienced this myself and was very surprised as to this difference between Class#getResource and ClassLoader#getResource. However, it is entirely specified behavior according to the javadoc:

Additionally, and except for the special case where the resource has a name ending with ".class", this method will only find resources in packages of named modules when the package is opened unconditionally (even if the caller of this method is in the same module as the resource).

Javadoc (emphasis mine)

Another more efficient way to do is just use @Value

@Value("classpath:sss.json")
private Resource resource;

and after that you can just get the file this way

File file = resource.getFile();

Related