1

this is a maven project, and have one image in resources directory:

├─ src
   ├─ main
       ├─ java
       └─ resources  
          └─imgs
            └─logo.png

Code:

public class Test {
    public static void main(String[] args) {
        InputStream stream = Test.class.getClassLoader().getResourceAsStream("/imgs/logo.png");
        InputStream stream1 = Test.class.getClassLoader().getResourceAsStream("imgs/logo.png");
        System.out.println(stream == null ? "stream is null!" : "stream is not null!");
        System.out.println(stream1 == null ? "stream1 is null!" : "stream1 is not null!");
    }
}

when I add module-info.java to project, will print:

stream is null!
stream1 is null!

but when I remove module-info.java from project, will print:

stream is null!
stream1 is not null!

why? and how to use ClassLoader to load resources in modular java project?

Naman
  • 27,789
  • 26
  • 218
  • 353
Guo
  • 1,761
  • 2
  • 22
  • 45
  • Related - [Classloaders hierarchy in Java 9](https://stackoverflow.com/questions/46494112/classloaders-hierarchy-in-java-9) – Naman Apr 20 '20 at 07:12
  • When you get resource from ClassLoader, you are already starting from root, so you shouldn't use / in front, so the second one is correct. module is another story. – SwiftMango Apr 20 '20 at 07:34

1 Answers1

4

Resources should be loaded over the Test.class, not its ClassLoader. By loading the resource over the class, you establish a context (JAR, module, dependencies) for where the resource is located.

For resources in the same package, use a relative path:

Test.class.getResource("logo.png")

If the qualified name of Test is org.foo.Test, it would lookup the resource in org/foo/logo.png in the JAR (or in the resources folder, before building the JAR).

For resources in the same module, use an absolute path, starting with a slash:

Test.class.getResource("/logo.png")

^ this is what you want to use most of the time.

There's no need to go over the classloader. I see this often when developers are unaware of how to properly address a resource, and load the resource with a relative path but over the classloader, which works most of the time but not very well with modular projects/classloaders such as Java9 and OSGI.

Peter Walser
  • 15,208
  • 4
  • 51
  • 78
  • I know `Test.class.getResource("/logo.png")` this way to get resources, but I want to know how to use `ClassLoader` to load resources. – Guo Apr 20 '20 at 09:05
  • 1
    @Guo If you want to use a ClassLoader you can look at the source code of `getResourceAsStream` in the OpenJDK source code: https://github.com/AdoptOpenJDK/openjdk-jdk11/blob/master/src/java.base/share/classes/java/lang/Class.java#L2614 – Robert Apr 20 '20 at 17:01
  • 1
    The ClassLoader API docs is a better resource, in particular the statement that it only find resources in packages of named modules then the package is opened unconditionally. Class::getResourceAsStream is the right API for code in a module to open resources in its own module. – Alan Bateman Apr 24 '20 at 07:43