classpath 带和不带的区别

classpath*:/path/target.txt classpath:/path/target.txt

  • 带* , 读取全部的lib
  • 不带*,仅读取classes目录下边的数据

    对于classpath的处理过程是交给Spring处理的

举个例子
读取 classpath:type*.txt
因为没有加 * 号,所以会读取 classpath
具体的处理是

  • 读取 “” 所在的路径. 因为我是idea启动所以是target下的classes目录 /Users/admin/open/boot-mybatis/target/classes 然后读取这个目录下所有的文件与type*.txt 进行比对.

    直接使用这种方式的读取是不能读取二级目录下的文件的. 如果需要读取,也要使用正则进行,例如 classpath:/*/type.txt 这样既能读取一级目录也能读取二级目录匹配的文件.

读取classpath*:type*.txt
加了 * 以后的读取会读取所有的lib. 这里的处理是交给JVM实现的.

  • ClassLoader#getResource 读取一个文件
  • ClassLoader#getResources 读取多个文件

总结

  • 正则匹配的是由Spring进行处理
    • 如果是classpath*, 会获取classes目录和所有的lib进行查找
    • 如果是classpath,则只会获取classes目录进行获取.
  • 具体的文件则是由JVM的ClassLoader进行读取

文件读取究竟是谁操作的

在Spring/SpringBoot中,读取文件分为以下几种情况

  1. new ClassPathResource(“classpath:type.txt”); 读取单个文件
  2. Thread.currentThread().getContextClassLoader().getResourceAsStream(“classpath:type.txt”) 单个文件
  3. ResourceUtils.getFile(“classpath:type.txt”); 单个文件
  4. new PathMatchingResourcePatternResolver() .getResources(“classpath:/**/type.txt”); 正则匹配并获取文件

根据上边的猜测可以肯定是 1,2,3应该是JVM的一个封装实现,直接使用的classLoader读取文件。 4则是根据classpath*的表现去决定.

看下来都是 classloader.getResourceAsStream(name) classloader是如何读取文件的呢

发现classloader内部都是依赖 ucp 实现的, URLClassPath , 码如其名,应该是加载的URL的一个集合,实际发现在 ucp 中读取文件其实是通过一些协议去读取文件内容的,如果是文件系统则是file协议,如果http开头的则是通过http协议。

在一些常见的攻击场景中,攻击者通常会注入一些第三方jar包到我们系统当中,这个第三方jar包就是通过http的方式进行加载的,如果我们保护系统不加载第三方jar包,可以通过字节码的技术在ucp加载jar包的时候判断如果是http协议的,就提示告警或者是加白名单..
image.png