一、解决方案
换成XXX.class.getClassLoader().getResourceAsStream("a.txt")即可。
二、场景复现
src/main/resource下存在文件a.txt,项目类中static 方法中读取该文件。ClassLoader.getSystemResourceAsStream("a.txt")获取不到资源文件
三、原因剖析
getResourceAsStream会先使用本类的类加载器去加载,本类没有类加载器,才会使用系统类加载器。也就是说getResourceAsStream功能覆盖了getSystemResourceAsStream,所以推荐直接使用getResourceAsStream就完事了。都不需要知道具体两者有啥区别。
下面我们来细看有啥区别....
3.1 java类加载器
- 1.启动类加载器(Bootstrap ClassLoader):顶层的类加载器,没有父类加载器。负责加载 /lib 目录下的,或被 -Xbootclasspath 参数所指定路径中的,并被 JVM 识别的(仅按文件名识别,如 rt.jar,名字不符合的类库即使放在 lib 目录也不会被加载)类库加载到虚拟机内存中。所有被 Bootstrap classloader 加载的类,它的 Class.getClassLoader 方法返回的都是 null,所以也称作 NULL ClassLoader。
- 2.扩展类加载器(Extension CLassLoader):由 sun.misc.Launcher$ExtClassLoader 实现,负责加载 <JAVA_HOME>/lib/ext 目录下,或被 java.ext.dirs 系统变量所指定的目录下的所有类库;
- 3.应用程序类加载器(Application/System ClassLoader):由 sun.misc.Launcher$AppClassLoader 实现。它是 ClassLoader.getSystemClassLoader() 方法的默认返回值,所以也称为系统类加载器(System ClassLoader)。它负责加载 classpath 下所指定的类库,如果应用程序没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
3.2 tomcat容器下类加载器
Tomcat官方说明:Class Loader HOW-TO
跟普通的java程序相比,类加载大体顺序相同。
- 1.Bootstrap 启动类加载器: 加载JVM启动所需的类+系统扩展目录($JAVA_HOME/jre/lib/ext)里 JAR 文件中的类。
- 2.System 系统类加载器:从 CLASSPATH 系统变量指定的目录中加载类库。该加载器加载的类对 tomcat 本身和 web 应用都可见。但是,标准的 tomcat 启动脚本($CATALINA_HOME/bin/catalina.sh or %CATALINA_HOME%incatalina.bat)都会忽略系统变量 CLASSPATH 的值,而会使用如下的类库来创建 System 类加载器:
$CATALINA_HOME/bin/bootstrap.jar
$CATALINA_BASE/bin/tomcat-juli.jar 或 $CATALINA_HOME/bin/tomcat-juli.jar
$CATALINA_HOME/bin/commons-daemon.jar
- 3.Common 通用类加载器:通过该类加载器加载的类库可被 Tomcat 和所有应用共享。该类加载器的搜索位置是通过 $CATALINA_BASE/conf/catalina.properties 文件中的 common.loader 属性指定的,默认包括如下位置:
$CATALINA_BASE/lib 下未打包的类和资源;
$CATALINA_BASE/lib 下的 jar 包;
$CATALINA_HOME/lib 下未打包的类和资源;
$CATALINA_HOME/lib 下的 jar 包。
- 4.WebappX 应用类加载器:每个 Web 应用创建一个自己的类加载器,加载自己项目下的数据: /WEB-INF/classes 和 /WEB-INF/lib 下的类和资源。并且不使用双亲委派机制,先自己加载,加载不到才使用父类加载器。
本来顺序是1234,但是WebappX不使用委派机制而是先自己加载,加载不了才使用父类,所以真实的顺序是:
- Bootstrap classes of your JVM
- /WEB-INF/classes of your web application
- /WEB-INF/lib/*.jar of your web application
- System class loader classes (described above)
- Common class loader classes (described above)
tomcat8支持委托:配置允许委派:<Loader delegate="true"/>,顺序变为:
- Bootstrap classes of your JVM
- System class loader classes (described above)
- Common class loader classes (described above)
- /WEB-INF/classes of your web application
- /WEB-INF/lib/*.jar of your web application
3.3 问题回归
tomcat容器中运行的java程序,使用系统类加载器是不能获取到资源的,必须使用WebappClassLoader。使用getResourceAsStream获取当前类的类加载器,也就是WebappClassLoader,自然可以获取到资源了。
=======参考=========
https://blog.csdn.net/w1196726224/article/details/54428493