Recently, we are working on a project which will reference some config file used in Jar. In order to reference those file, we can’t use those files absolute path (because the packaged Jar may use in any computer which will invalid the path) or relative path as we run it in a IDE (for packaged Jar has different file structure with that in IDE.
So, Java provide a mechanism to reference files: getResource
Get Resource via Different Ways
By a simple search, we can see that there exists two main ways of doing so:
this.getClass().getResource("file");
this.getClass().getClassLoader().getResource("file");
And common questions arises: “what the differences between them?”
Let’s have a look at the following code snippet:
// get file of foo/bar/name
foo.bar.A.class.getResource("name")
// get file of `A's classloader cwd`/name
foo.bar.A.class.getResource("/name")
// get file of `A's classloader cwd`/name
foo.bar.A.class.getClassLoader().getResource("name")
// always get null
foo.bar.A.class.getClassLoader().getResource("/name")
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.
From the source of class#getResource
, we can verify above statement:
public java.net.URL getResource(String name) {
name = resolveName(name);
// name is changed to foo/bar/name
ClassLoader cl = getClassLoader0();
if (cl==null) {
// means this class loaded by bootstrap classLoader
return ClassLoader.getSystemResource(name);
}
return cl.getResource(name);
}
And from the source code, we can actually find the third way (Only if your resources are system or global resources):
// be careful about this way, if your class is not loaded by bootstrap
// classLoader, it will return null, which is very common in application
// run in container, e.g. running in Tomcat, or Spring Boot.
ClassLoader.getSystemResource("name")
ClassLoader.getSystemResource("/name")
Pitfalls of getFile
The method above all return a URL
, which have a method getFile
which says ‘Gets the file name of this URL’. But this is kind of pitfall if we use it. It will work in IDE when we debug it, but fails when we package it into Jar.
The reason is simple, class loader resources are not java.io.File
objects when they are in a Jar archive. In essence, the Jar archive is a compressed file like zip, so a single file in it can’t be taken as a File
if it is still compressed.
Or Choose getResourceAsStream
If we look at the interface ofClass
and ClassLoader
more closely, we can notice that they provide more utility related to resources.
If we are only interested in the content of file, we usually find the URL less useful than an InputStream
, so we prefer to use getResourceAsStream
instead of getResource
.
InputStream in = this.getClass().getClassLoader().getResourceAsStream(fileName);
More
It’s the end of blog, but not the use of resources. Resources can be other forms other than file (image, audio etc), and we may find more usage of resources as the following scenario, which will be left for further study:
- An applet loaded from the Internet using multiple HTTP connections.
- An applet loaded using JAR files.
- A Java Bean loaded or installed in the CLASSPATH.
- A “library” installed in the CLASSPATH.
Ref
- Difference betweeen class#getResource and classloader#getResource
- How to reference resources
- Doc of resources
Written with StackEdit.
评论
发表评论