Loading resources in Java always was a little risky. This Blog is about the different ways how resources can be loaded. It contains a test class proving that the mechanism didn't change in Java 9, although resources can not be loaded across module boundaries any more (my next Blog will be about this).
Resources
A resource denotes a file that may be packed into the JAR archive representing a Java application, be it a web or a desktop app. Normally you load a Java resource through a file path or name and an anchor class. Consider following example files and directories:
src
resources
The files root.properties
and resources/package.properties
are example resources.
For this demo they can be empty.
We will use the class resources/ResourceAnchor.java
to load them.
Root Resources
Resources that do not belong to a specific class often are stored in the root directory of the project.
Example is root.properties
. Here is the standard way how to load it:
URL url = ResourceAnchor.class.getResource("/root.properties");
Alternatively you can fetch also an InputStream
, the mechanism is the same:
InputStream inputStream = ResourceAnchor.class.getResourceAsStream("/root.properties");
You can use any class for loading such resources.
Package Resources
Because we want to be safe against package moves and renames, we always place a resource in
the same package as its using Java class, and we load the resource through that class.
Let's assume that package.properties
belongs to ResourceAnchor.java
,
so they are in the same package named "resources".
(Mind that in a Maven structure they would be in different directories though).
Here is the default way to load package.properties
:
URL url = ResourceAnchor.class.getResource("package.properties");
Or:
InputStream inputStream = ResourceAnchor.class.getResourceAsStream("package.properties");
Class.getResource(resourcePath)
will work
on both the filesystem and inside a packed application JAR.
Alternatives
Some people prefer to load resources through the class-loader, not the class, which makes it more difficult to understand:
URL url = ResourceAnchor.class.getClassLoader().getResource("resources/package.properties");
All variants shown until now would work, but you see that this variant is quite strange, as it uses "resources/package.properties" as path, which seems to point to a sub-directory "resources" below the "resources" directory where the anchor class is. Nevertheless this variant works, although I would strongly discourage it. There is no need to use the class-loader for loading resources.
Test App
We see that a lot of combinations is possible (and unfortunately also used) for loading resources:
- just file name without path and leading slash
- full path without leading slash (relative path)
- full path with leading slash (absolute path)
- loading through class
- loading through class-loader
The following test class may help those that are confused and want to know what each of these combinations does. It can also be used to find out behavior changes between different Java versions (there ain't any until now).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | package resources; import java.io.IOException; import java.net.URL; public class ResourceAnchor { public void run() throws Exception { System.out.println("Trying to load a root resource:"); loadRootResource(); System.out.println("Trying to load a package resource:"); loadPackageResource(); } private void loadRootResource() throws IOException { checkThat( classResource("/root.properties") != null ); checkThat( classResource("root.properties") == null ); checkThat( classLoaderResource("/root.properties") == null ); checkThat( classLoaderResource("root.properties") != null ); } private void loadPackageResource() throws Exception { checkThat( classResource("package.properties") != null ); checkThat( classResource("/resources/package.properties") != null ); checkThat( classResource("resources/package.properties") == null ); checkThat( classLoaderResource("package.properties") == null ); checkThat( classLoaderResource("/resources/package.properties") == null ); checkThat( classLoaderResource("resources/package.properties") != null ); } private URL classResource(String resourcePath) throws IOException { URL url = getClass().getResource(resourcePath); System.out.println("getClass().getResource(\""+resourcePath+"\"): "+(url != null ? "found" : "missing")); return url; } private URL classLoaderResource(String resourcePath) throws IOException { URL url = getClass().getClassLoader().getResource(resourcePath); System.out.println("getClass().getClassLoader().getResource(\""+resourcePath+"\"): "+(url != null ? "found" : "missing")); return url; } private void checkThat(boolean result) { if (result != true) throw new IllegalStateException("Expected to be true!"); } public static void main(String[] args) throws Exception { System.out.println("Standing in = "+System.getProperty("user.dir")); System.out.println("Java Version = "+System.getProperty("java.version")); new ResourceAnchor().run(); } } |
Wherever you see checkThat(classResource(".....") != null)
it means success,
while checkThat(classResource(".....") == null)
denotes failed resource loading.
If you build such a project and run the resources.ResourceAnchor
class,
you will see following output:
Trying to load a root resource: getClass().getResource("/root.properties"): found getClass().getResource("root.properties"): missing getClass().getClassLoader().getResource("/root.properties"): missing getClass().getClassLoader().getResource("root.properties"): found Trying to load a package resource: getClass().getResource("package.properties"): found getClass().getResource("/resources/package.properties"): found getClass().getResource("resources/package.properties"): missing getClass().getClassLoader().getResource("package.properties"): missing getClass().getClassLoader().getResource("/resources/package.properties"): missing getClass().getClassLoader().getResource("resources/package.properties"): found
Conclusion
Try this out with Java 1.8 and Java 9 (10, 11), you will see that all succeed and output the same result.
Don't load resources through the class-loader, this gives a wrong impression of the actual location of the file.
Keine Kommentare:
Kommentar veröffentlichen