spring的classpath资源定位分析

在了解 classpath 获取资源之前,先了解 classLoader如何获取资源的

ClassLoader获取资源

为了说明怎么加载文件的,先举例一个具体的场景

现有 demo 项目,目录结构如下

d:
└─demo
    └─src
	 	└─com
			└─aya
				├─CustomMain.java
				└─config.properties

这里获取 com/aya/config.properties 有以下两种方式:

  1. CustomMain.class.getResource(“config.properties”);
  2. Thread.currentThread().getContextClassLoader().getResource(“com/aya/config.properties”);

class 与 classLoader 获取资源的调用形式如下图:

java加载资源

Class加载资源

class 内部实际上的调用方式:

Thread.currentThread().getContextClassLoader().getResource( resolveName(clazz) +path)

resolveName(clazz) 会获得class类所在的目录名称

加载步骤如下:

  1. CustomMain.java 的目录 + 目标文件名 = com/aya/config.properties
  2. 用当前线程的 ClassLoader去加载目标资源

ClassLoader加载资源

  1. 尝试 BootStrap ClassLoader 加载资源
  2. 尝试 Extention ClassLoader 加载资源
  3. 尝试 当前的 ApplicationClassLoader

ClassLoader 根据双亲委派模型去加载资源,有父加载器则先去父加载器加载,如果没有加载成功,则在当前类加载器加载,以此类推

BootStrap ClassLoader

引导类加载器, 加载 jre/lib 目录下的jar包,JVM固定的几个jar包,把jar包丢进去也没用

Extention ClassLoader

扩展类加载器, 加载 jre/lib/ext 目录下的jar包,可以把jar包丢过去

ApplicationClassLoader

应用程序的类加载器,加载应用程序的根路径下的内容

类加载器加载资源

注意: ClassLoader 加载的资源一定要有确定的名称,不支持通配符

ClassPathResource 加载资源

spring的 org.springframework.core.io.ClassPathResource 有三种构造 ,如下图

通常使用无参构造会使用当前线程的ClassLoader去加载(没有的话,就是用AppClassLoader加载)

classpath构造方式

在使用 org.springframework.core.io.ClassPathResource.getInputStream获取输入流的时候,会根据构造的内容选择加载方式

spring的classpath解释

事实上 spring 的 ClassPathResource 就是使用的 ClassLoader.getResourceAsStream 获取的资源

classpath的星号问题

spring 可以使用 <import resource="classpath:spring-*.xml"/> 或者 <import resource="classpath*:spring-*.xml"/>

那么 classpath 与 classpath* 有什么区别呢?

import 标签解析的时候,会使用org.springframework.core.io.support.PathMatchingResourcePatternResolver.getResources 去加载

这里就不贴代码了,直接给出最终的搜索流程图

区分定位符与内容

这里的资源表示:文件或者目录

资源内容没有符号内容有星号或者问号加载jar包资源
classpath:第一个匹配的资源第一个匹配的目录下匹配的所有资源
classpath*:当前线程classLoader去加载所有资源匹配所有的目录+匹配目录下的资源

classpath 与 classpath* 都会加载jar包的资源.

  1. 当存在通配符的时候,classpath 不一定先加载当前类路径的内容
  2. (根据双亲委派的加载机制,要是你放一个jar包在ExtClassLoader的目录下,如果classpath不加星号的话,所匹配的资源文件就跟当前工程没什么关系了)
  3. 匹配的内容如果有通配符(?,*),那么会先匹配确认的目录,然后再去目录下遍历每个文件的文件名和通配符是否匹配

tomcat匹配失败

<import resource="classpath*:spring-*.xml"/>
在 tomcat7 与 tomcat8 会有不同的效果, tomcat7 无法加载jar包的文件, tomcat8 却可以。

tomcat7默认使用的是 org.apache.catalina.loader.WebappClassLoader,tomcat8 默认使用的是 org.apache.catalina.loader.ParallelWebappClassLoader

可以在 tomcat 的 conf/context.xml 下加入Loader标签,强制修改应用的classLoader,事实上并没有用。

这两个类无论如何都会去加载jar包的内容,只是处理的方式不同而已

tomcat7

//WebappClassLoaderBase.getResources 下 (findResource方法实现内容)
// Looking at the JAR files
synchronized (jarFiles) {
    if (openJARs()) {
        for (i = 0; i < jarFilesLength; i++) {
            JarEntry jarEntry = jarFiles[i].getJarEntry(name);//找不到文件或者文件夹就会返回null
            if (jarEntry != null) {
                try {
                    String jarFakeUrl = getURI(jarRealFiles[i]).toString();
                    result.add(UriUtil.buildJarUrl(jarFakeUrl, name));
                } catch (MalformedURLException e) {
                    // Ignore
                }
            }
        }
    }
}

tomcat8

// 会返回jar包的根路径
WebResource[] webResources = resources.getClassLoaderResources(path);
for (WebResource webResource : webResources) {
    if (webResource.exists()) {
        result.add(webResource.getURL());
    }
}

在tomcat7下要想解决星号匹配的问题,加入目录就可以了

<import resource="classpath*:dir/spring-*.xml"/>
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值