转载

WTF: why getResourceAsStream return null?

目录结构

├── src
│   ├── main
│       ├── java
│       │   └── com
│       │       └── example
│       │           └── coco
│       │               ├── Main.java
│       └── resources
│           └── definition.properties
│   └── test
└── target
    └── classes
        ├── com
        └── definition.properties
复制代码

Main.java

public class Main {
  public static void main(String[] args) {
    // 组1
    System.out.println(Main.class.getResource("definition.properties"));
    System.out.println(Main.class.getResource("/definition.properties"));
    
    // 组2
    System.out.println(Main.class.getClass().getResource("definition.properties"));
    System.out.println(Main.class.getClass().getResource("/definition.properties"));
    
    // 组3
    System.out.println(Main.class.getClassLoader().getResource("definition.properties"));
    System.out.println(Main.class.getClassLoader().getResource("/definition.properties"));
  
    // AppClassLoader
    System.out.println(Main.class.getClassLoader());
    // ExtClassLoader
    System.out.println(Main.class.getClassLoader().getParent());
    // BootstrapClassLoader(Java中用null表示)
    System.out.println(Main.class.getClassLoader().getParent().getParent()); 
    System.out.println(Main.class.getClass().getClassLoader()); 
  }
复制代码

输出

null
file:/D:/learning/demo/target/classes/definition.properties

null
file:/D:/learning/demo/target/classes/definition.properties

file:/D:/learning/demo/target/classes/definition.properties
null

sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@4f023edb
null
null
复制代码

组1 class.getResource 分析

Class.getResource源码

public java.net.URL getResource(String name) {
    name = resolveName(name);
    ClassLoader cl = getClassLoader0();
    if (cl==null) {
        // A system class.
        return ClassLoader.getSystemResource(name);
    }
    return cl.getResource(name);
}
复制代码

Class.resolveName源码

WTF: why getResourceAsStream return null?
可以看到 resolveName 方法会去判断 传入的参数 name 是否以 / 开头,如果不是 / 开头,则会使用当前方法的调用类 com.example.coco.Main 所在包作为前缀( com/example/coco/ ),然后拼接参数name( definition.properties ),最终返回 com/example/coco/definition.properties

,但是显然这个路径是不存在的,所以

Main.class.getResource("definition.properties") 返回 null
复制代码

而如果是 / 开头, resolveName 返回 definition.properties ,继续调用 ClassLoader.getResource 方法

WTF: why getResourceAsStream return null?
可以看出定位到了正确的目录 /D:/learning/demo/target/classes/definition.properties

那是如何定位的呢?

Main.java 的ClassLoader是 AppClassLoader , 它会从 classpath 中去查找 definition.properties 是否存在

classpath是什么?

当你使用Idea点运行 Main.java 的时候,其实是在拼一串命令行,类似 java Main ,只不过Idea拼接了很多参数,如下图:

WTF: why getResourceAsStream return null?

拷贝出来如下:

"C:/Program Files/Java/jdk1.8.0_181/bin/java.exe" 

"-javaagent:C:/Program Files/JetBrains/IntelliJ IDEA 2019.3/lib/idea_rt.jar=54345:C:/Program Files/JetBrains/IntelliJ IDEA 2019.3/bin" 

-Dfile.encoding=UTF-8 

-classpath 
"C:/Program Files/Java/jdk1.8.0_181/jre/lib/charsets.jar;C:/Program Files/Java/jdk1.8.0_181/jre/lib/deploy.jar;
C:/Program Files/Java/jdk1.8.0_181/jre/lib/ext/access-bridge-64.jar;
C:/Program Files/Java/jdk1.8.0_181/jre/lib/ext/cldrdata.jar;
C:/Program Files/Java/jdk1.8.0_181/jre/lib/ext/dnsns.jar;
C:/Program Files/Java/jdk1.8.0_181/jre/lib/ext/jaccess.jar;
C:/Program Files/Java/jdk1.8.0_181/jre/lib/ext/jfxrt.jar;
C:/Program Files/Java/jdk1.8.0_181/jre/lib/ext/localedata.jar;
C:/Program Files/Java/jdk1.8.0_181/jre/lib/ext/nashorn.jar;
C:/Program Files/Java/jdk1.8.0_181/jre/lib/ext/sunec.jar;
C:/Program Files/Java/jdk1.8.0_181/jre/lib/ext/sunjce_provider.jar;
C:/Program Files/Java/jdk1.8.0_181/jre/lib/ext/sunmscapi.jar;
C:/Program Files/Java/jdk1.8.0_181/jre/lib/ext/sunpkcs11.jar;
C:/Program Files/Java/jdk1.8.0_181/jre/lib/ext/zipfs.jar;
C:/Program Files/Java/jdk1.8.0_181/jre/lib/javaws.jar;
C:/Program Files/Java/jdk1.8.0_181/jre/lib/jce.jar;
C:/Program Files/Java/jdk1.8.0_181/jre/lib/jfr.jar;
C:/Program Files/Java/jdk1.8.0_181/jre/lib/jfxswt.jar;
C:/Program Files/Java/jdk1.8.0_181/jre/lib/jsse.jar;
C:/Program Files/Java/jdk1.8.0_181/jre/lib/management-agent.jar;
C:/Program Files/Java/jdk1.8.0_181/jre/lib/plugin.jar;
C:/Program Files/Java/jdk1.8.0_181/jre/lib/resources.jar;
C:/Program Files/Java/jdk1.8.0_181/jre/lib/rt.jar;
D:/learning/demo/target/classes" 

com.example.coco.Main
复制代码

其中 classpath 这个参数的最后一行为 D:/learning/demo/target/classes , 其实这就是我们项目的编译后的输出目录:

WTF: why getResourceAsStream return null?

组2 class.getClass().getResource分析

Main.class.getClass().getResource("definition.properties")

WTF: why getResourceAsStream return null?
可以看到getResource方法的实际调用者为 java.lang.Class ,而且传入进来的 name 未以 / 开头,所以最终name为 java/lang/definition.properties

, 而这个路径是不存在的,所以

Main.class.getClass().getResource("definition.properties") 返回null
复制代码

Main.class.getClass().getResource("/definition.properties")

Main.class.getClassjava.lang.Class , 该类的类加载器为为 null ,即 ClassLoaderBootstrapClassLoader

WTF: why getResourceAsStream return null?
当为 null 时最终会调用 AppClassLoader.getResource

方法

WTF: why getResourceAsStream return null?
原文  https://juejin.im/post/5ebe4d6a6fb9a04356088a07
正文到此结束
Loading...