读取文件的几个方法
在SpringBoot中,比较常见的几个读取文件的方法有
- Classloader.getResourceAsStream()
- ResourceUtils.getFile
- ClassPathResource读取
- new PathMatchingResourcePatternResolver() 读取
外部表现形式虽然有所差异,内在方式还是以 Classloader.getResourceAsStream
为唯一标杆,其余三个都是针对它的一个封装。
而 classloader
读取又是在classpath的划定范围内进行读取的
Classloader.getResourceAsStream()
classloader内部都是依赖 ucp
实现的, URLClassPath ,加载的URL的一个集合,URL是在实例化Classloader传递进去的,发现在 ucp 中读取文件其实是通过一些协议去读取文件内容的,如果是文件系统则是file协议,如果http开头的则是通过http协议。
在一些常见的攻击场景中,攻击者通常会注入一些第三方jar包到我们系统当中,这个第三方jar包就是通过http的方式进行加载的,如果我们保护系统不加载第三方jar包,可以通过字节码的技术在ucp加载jar包的时候判断如果是http协议的,就提示告警或者是加白名单..
ResourceUtils.getFile
这个方法我最先用在加载 log4j.xml
上边,经常出现
log4j.xml cannot be resolved to absolute file path because it does not exist
经过多次对源码进行观摩,发现它有2种读取方式,分别是
- 以classpath的方式进行读取,该方式扫描classpath,利用的是classloader寻找资源的能力
- 以绝对路径的方式进行,利用的是文件系统的能力
以 classpath:
开头走classpath (内部会进行一次subString移除classpath:), 其他走文件系统
ClassPathResource读取
public ClassPathResource(String path, ClassLoader classLoader) {
this.path = path;
this.classLoader = classLoader;
}
这个的定义就很明确,扫描classpath获取对应资源,缺点是只能获取一个。
虽然定义很明确,但是 ClassPath
本身就让人犯嘀咕,到底带不带 classpath:
, 结论是不要带,因为它是根据给定的文件,到classpath目录下进行查找。
我的classpath目录是 /Users/chenshun/open/demo/target/classes
, 给我一个 classpath:log.txt
. 两个拼凑的路径是 /Users/chenshun/open/demo/target/classes/claspath:log.txt
自然是没有的
PathMatchingResourcePatternResolver 进行读取
相比上边只能搞一个,这个就高级多了,用了ant风格进行正则匹配。支持搞多个文件,同时也引入了一个 classpath*:
的概念
- classpath:/mapper/.xml
- 扫描classes和所有的jar包符合该规则的文件,利用了classloader加载资源的能力
- classpath:/mapper/.xml 或者是 classpath:mapper/.xml
- 扫描classpath所有符合该规则的文件,利用ClassPathResource定位rootDir, 扫描rootDir的文件
- classpath:/mapper/a.xml 或者是 classpath:mapper/a.xml
- 去掉classpath 实现同下
- no classpath (内部ClassPathResource实现)
- /mapper/a.xml
- mapper/a.xml
总结
总结了 classloader是如何读取文件的,以及在Spring中各个加载文件的方法和class的使用方式。
SpringBoot的启动方式和classloader
上边讲述了读取文件的方式本质都是利用了 classloader
扫描classpath加载文件的能力,在Spring的启动方式中,它的classloader又存在不一样。如果不明白这一点,就很容易弄出一些乌龙。
IDEA main方法启动
在idea中启动的时候,可以看到idea是利用 -classpath 指定jar包进行启动的,指定了它的classpath, 而且它的classloder是appclassloader。
/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/bin/java -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=62066:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8 -classpath /Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/deploy.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/ext/jaccess.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/ext/jfxrt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/ext/nashorn.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/javaws.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/jfxswt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/management-agent.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/plugin.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/lib/ant-javafx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/lib/dt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/lib/javafx-mx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/lib/jconsole.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/lib/packager.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/lib/sa-jdi.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/lib/tools.jar:/Users/chenshun/open/demo/target/classes:/Users/chenshun/.m2/repository/org/springframework/boot/spring-boot-starter-web/2.5.3/spring-boot-starter-web-2.5.3.jar:/Users/chenshun/.m2/repository/org/springframework/boot/spring-boot-starter/2.5.3/spring-boot-starter-2.5.3.jar:/Users/chenshun/.m2/repository/org/springframework/boot/spring-boot/2.5.3/spring-boot-2.5.3.jar:/Users/chenshun/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.5.3/spring-boot-autoconfigure-2.5.3.jar:/Users/chenshun/.m2/repository/org/springframework/boot/spring-boot-starter-logging/2.5.3/spring-boot-starter-logging-2.5.3.jar:/Users/chenshun/.m2/repository/ch/qos/logback/logback-classic/1.2.4/logback-classic-1.2.4.jar:/Users/chenshun/.m2/repository/ch/qos/logback/logback-core/1.2.4/logback-core-1.2.4.jar:/Users/chenshun/.m2/repository/org/slf4j/slf4j-api/1.7.32/slf4j-api-1.7.32.jar:/Users/chenshun/.m2/repository/org/apache/logging/log4j/log4j-to-slf4j/2.14.1/log4j-to-slf4j-2.14.1.jar:/Users/chenshun/.m2/repository/org/apache/logging/log4j/log4j-api/2.14.1/log4j-api-2.14.1.jar:/Users/chenshun/.m2/repository/org/slf4j/jul-to-slf4j/1.7.32/jul-to-slf4j-1.7.32.jar:/Users/chenshun/.m2/repository/jakarta/annotation/jakarta.annotation-api/1.3.5/jakarta.annotation-api-1.3.5.jar:/Users/chenshun/.m2/repository/org/springframework/spring-core/5.3.9/spring-core-5.3.9.jar:/Users/chenshun/.m2/repository/org/springframework/spring-jcl/5.3.9/spring-jcl-5.3.9.jar:/Users/chenshun/.m2/repository/org/yaml/snakeyaml/1.28/snakeyaml-1.28.jar:/Users/chenshun/.m2/repository/org/springframework/boot/spring-boot-starter-json/2.5.3/spring-boot-starter-json-2.5.3.jar:/Users/chenshun/.m2/repository/com/fasterxml/jackson/core/jackson-databind/2.12.4/jackson-databind-2.12.4.jar:/Users/chenshun/.m2/repository/com/fasterxml/jackson/core/jackson-annotations/2.12.4/jackson-annotations-2.12.4.jar:/Users/chenshun/.m2/repository/com/fasterxml/jackson/core/jackson-core/2.12.4/jackson-core-2.12.4.jar:/Users/chenshun/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.12.4/jackson-datatype-jdk8-2.12.4.jar:/Users/chenshun/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.12.4/jackson-datatype-jsr310-2.12.4.jar:/Users/chenshun/.m2/repository/com/fasterxml/jackson/module/jackson-module-parameter-names/2.12.4/jackson-module-parameter-names-2.12.4.jar:/Users/chenshun/.m2/repository/org/springframework/boot/spring-boot-starter-tomcat/2.5.3/spring-boot-starter-tomcat-2.5.3.jar:/Users/chenshun/.m2/repository/org/apache/tomcat/embed/tomcat-embed-core/9.0.50/tomcat-embed-core-9.0.50.jar:/Users/chenshun/.m2/repository/org/apache/tomcat/embed/tomcat-embed-el/9.0.50/tomcat-embed-el-9.0.50.jar:/Users/chenshun/.m2/repository/org/apache/tomcat/embed/tomcat-embed-websocket/9.0.50/tomcat-embed-websocket-9.0.50.jar:/Users/chenshun/.m2/repository/org/springframework/spring-web/5.3.9/spring-web-5.3.9.jar:/Users/chenshun/.m2/repository/org/springframework/spring-beans/5.3.9/spring-beans-5.3.9.jar:/Users/chenshun/.m2/repository/org/springframework/spring-webmvc/5.3.9/spring-webmvc-5.3.9.jar:/Users/chenshun/.m2/repository/org/springframework/spring-aop/5.3.9/spring-aop-5.3.9.jar:/Users/chenshun/.m2/repository/org/springframework/spring-context/5.3.9/spring-context-5.3.9.jar:/Users/chenshun/.m2/repository/org/springframework/spring-expression/5.3.9/spring-expression-5.3.9.jar:/Users/chenshun/.m2/repository/org/springframework/boot/spring-boot-loader/2.5.3/spring-boot-loader-2.5.3.jar com.example.demo.View
java -jar 启动
通过java -jar启动的方式,在内部通过自定义classloader加载了fat-jar内部的BOOT-INF中间的jar包,读取不到部分文件。