Spring项目中使用ClassLoader.getSystemResourceAsStream()获取资源,本地正常,jar包部署运行后无法获取问题探究
解决方法
使用当前类去获取类加载器,再通过getResourceAsStream()方法去加载jar包中具体资源。
具体代码
InputStream inputStream = DataFactory.class.getClassLoader().getResourceAsStream(resourcePath);
其中,DataFactory为当前类,resourcePaht为资源路径,默认根目录是classes(也就是你在编译前的recources目录下,他会将该目录下的内容直接打包到classes下)
注:如果只想知道结果的,到这就可以关闭该页面了,下面是个人的探究过程,以及最后的结论
一,问题描述以及背景
背景:本人在编写个人的项目时,忽然需要加载一些静态资源,习惯性使用ClassLoarder.getSystemResourceAsStream()方法进行获取,本地一切编译正常,编写完打成jar包后,直接java -jar运行该项目,却爆出如下错误
java.lang.NullPointerException: null
at java.base/java.util.Objects.requireNonNull(Objects.java:221) ~[na:na]
通过排查发现,该空指针异常是因为项目中在加载文件时报的异常;
二,探究结论以及过程
想必大家都不太喜欢看推到过程,我在此只放出最后的结论,供大家参考,也欢迎大家进行指正
首先来看图
图片为我在编译器打印出来的,通过ClassLoader和通过当前类获得的类加载器
图片为我打印出来的,通过ClassLoader和通过当前类获得的类加载器。
我们可以看到他们在服务器端打印的类加载器与在编译器端打印的不太一样。在服务器上打印的不是java原生的类加载器。通过查资料,我知道,该类加载器,是SpringBoot自己写的一个类加载器,以实现一些SpringBoot的功能。详细可以查看参考文章
通过我的实验,发现通过ClassLoader获得的类加载器在获取路径的时候也是能够找到jar包的位置的但是无法访问jar包内的内容,见下图,
从图中可以看出,通过ClassLoader是可以找到jar包位置,但是后面却无法拿到jar包里面的资源,从而,个人推测是系统类加载器没有实现去jar包中拿资源的功能,只能访问到目录,不能访问到jar包下。
至此,大概是明白了,在Spring Boot进行打包的时候会把自己的类加载器打包进去,用自己的类加载器加载项目中的文件以及资源,其实可以访问jar包中的所有文件的,包括类文件,而系统默认的加载类虽然也能找到jar包地址,但是其无法访问jar包中的内容,所以在spring boot项目中,访问资源文件最好是通过当前类来获取资源文件路径进行访问。
参考文章:
链接: Do you know the startup principle of springboot Java -jar?
LaunchedURLClassLoader在FatJar中的重要作用分析及反射的经典应用