classpath: VS classpath*:
同名资源存在时,classpath: 只从第一个符合条件的classpath中加载资源,而classpath*: 会从所有的classpath中加载符合条件的资源
classpath*:需要遍历所有的classpath,效率肯定比不上classpath,因此在项目设计的初期就尽量规划好资源文件所在的路径,避免使用classpath*来加载
classpath本质是jvm的根路径,jvm获取资源都是从该根路径下找的,注意这个根路径是个逻辑路径,并不是磁盘路径。比如两个jar包的路径是/packagea/a.jar和/packageb/b.jar,但是用classpath*:就可以找到这两个jar包中的资源。
一般classpath指向的是classes,也就是编译路径的根路径,而一般classes中放着这些文件:
1.java文件编译好的class文件。
2.properties配置文件。
3.xml配置文件。
4.一些模版文件,如*.ftl。
5.其他需要用classpath获取到的文件。
搞懂了classpath指向的classes里放的东西,我们再来看看classpath: 和classpath*:的区别。
1.classpath:只会到你的classes路径中查找找文件。
2.classpath*:不仅会到classes路径,还包括jar文件中(classes路径)进行查找。
<!-- myBatis配置.
classpath和classpath*的区别,参考文档:http://blog.csdn.net/zl3450341/article/details/9306983.
classpath只会返回第一个匹配的资源,建议确定路径的单个文档使用classpath;匹配多个文档时使用classpath*.
-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"
p:dataSource-ref="dataSource"
p:configLocation="classpath:mybatis.xml"
p:mapperLocations="classpath*:com/*Mapper.xml" />
Spring还提供了一种更强大的Ant模式通配符匹配,从能一个路径匹配一批资源。 Ant路径通配符支持“?”、“*”、“**”,注意通配符匹配不包括目录分隔符“/”:
“?”:匹配一个字符,如“config?.xml”将匹配“config1.xml”;
“*”:匹配零个或多个字符串,如“cn/*/config.xml”将匹配“cn/javass/config.xml”,但不匹配匹配“cn/config.xml”;而“cn/config-*.xml”将匹配“cn/config-dao.xml”;
“**”:匹配路径中的零个或多个目录,如“cn/**/config.xml”将匹配“cn /config.xml”,也匹配“cn/javass/spring/config.xml”;而“cn/javass/config-**.xml”将匹配“cn/javass/config-dao.xml”,即把“**”当做两个“*”处理。
Spring加载Resource文件是通过ResourceLoader来进行的
重点————classpath 与 classpath*以及通配符是怎么处理的
public interface ResourceLoader {
/** Pseudo URL prefix for loading from the class path: "classpath:" */
String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
Resource getResource(String location);
ClassLoader getClassLoader();
}
public interface ResourcePatternResolver extends ResourceLoader {
/**
* Pseudo URL prefix for all matching resources from the class path: "classpath*:"
* This differs from ResourceLoader's classpath URL prefix in that it
* retrieves all matching resources for a given name (e.g. "/beans.xml"),
* for example in the root of all deployed JAR files.
* @see org.springframework.core.io.ResourceLoader#CLASSPATH_URL_PREFIX
*/
String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
Resource[] getResources(String locationPattern) throws IOException;
}
通过2个接口的源码对比,我们发现ResourceLoader提供 classpath下单资源文件的载入,而ResourcePatternResolver提供了多资源文件的载入。
ResourcePatternResolver有一个实现类:PathMatchingResourcePatternResolver,那我们直奔主题,查看PathMatchingResourcePatternResolver的getResources()
public Resource[] getResources(String locationPattern) throws IOException {
Assert.notNull(locationPattern, "Location pattern must not be null");
//是否以classpath*开头
if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
//是否包含?或者*
if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
// a class path resource pattern
return findPathMatchingResources(locationPattern);
}
else {
// all class path resources with the given name
return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
}
}
else {
// Only look for a pattern after a prefix here
// (to not get fooled by a pattern symbol in a strange prefix).
int prefixEnd = locationPattern.indexOf(":") + 1;
//是否包含?或者*
if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
// a file pattern
return findPathMatchingResources(locationPattern);
}
else {
// a single resource with the given name
return new Resource[] {getResourceLoader().getResource(locationPattern)};
}
}
}
由此我们可以看出在加载配置文件时,以是否是以classpath*开头分为2大类处理场景,每大类在又根据路径中是否包括通配符分为2小类进行处理,
处理的流程图如下:
从上图看,整个加载资源的场景有三条处理流程
以classpath*开头,但路径不包含通配符的
让我们来看看findAllClassPathResources是怎么处理的
protected Resource[] findAllClassPathResources(String location) throws IOException {
String path = location;
if (path.startsWith("/")) {
path = path.substring(1);
}
Enumeration<URL> resourceUrls = getClassLoader().getResources(path);
Set<Resource> result = new LinkedHashSet<Resource>(16);
while (resourceUrls.hasMoreElements()) {
URL url = resourceUrls.nextElement();
result.add(convertClassLoaderURL(url));
}
return result.toArray(new Resource[result.size()]);
}
我们可以看到,最关键的一句代码是:Enumeration<URL> resourceUrls = getClassLoader().getResources(path);
public ClassLoader getClassLoader() {
return getResourceLoader().getClassLoader();
}
public ResourceLoader getResourceLoader() {
return this.resourceLoader;
}
//默认情况下
public PathMatchingResourcePatternResolver() {
this.resourceLoader = new DefaultResourceLoader();
}
其实上面这3个方法不是最关键的,之所以贴出来,是让大家清楚整个调用链,其实这种情况最关键的代码在于ClassLoader的getResources()方法。那么我们同样跟进去,看看源码
public Enumeration<URL> getResources(String name) throws IOException {
Enumeration[] tmp = new Enumeration[2];
if (parent != null) {
tmp[0] = parent.getResources(name);
} else {
tmp[0] = getBootstrapResources(name);
}
tmp[1] = findResources(name);
- return new CompoundEnumeration(tmp);
- }
是不是一目了然了?当前类加载器,如果存在父加载器,则向上迭代获取资源, 因此能加到jar包里面的资源文件。
- 不以classpath*开头,且路径不包含通配符的
- return new Resource[] {getResourceLoader().getResource(locationPattern)};
上面我们已经贴过getResourceLoader()的逻辑了, 即默认是DefaultResourceLoader(),那我们进去看看getResouce()的实现
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
try {
// Try to parse the location as a URL...
URL url = new URL(location);
return new UrlResource(url);
}
catch (MalformedURLException ex) {
// No URL -> resolve as resource path.
return getResourceByPath(location);
}
}
}
其实很简单,如果以classpath开头,则创建为一个ClassPathResource,否则则试图以URL的方式加载资源,创建一个UrlResource.
- 路径包含通配符的
protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
//拿到能确定的目录,即拿到不包括通配符的能确定的路径 比如classpath*:/aaa/bbb/spring-*.xml 则返回classpath*:/aaa/bbb/ //如果是classpath*:/aaa/*/spring-*.xml,则返回 classpath*:/aaa/
String rootDirPath = determineRootDir(locationPattern);
//得到spring-*.xml
String subPattern = locationPattern.substring(rootDirPath.length());
//递归加载所有的根目录资源,要注意的是递归的时候又得考虑classpath,与classpath*的情况,而且还得考虑根路径中是否又包含通配符,参考上面那张流程图
Resource[] rootDirResources = getResources(rootDirPath);
Set<Resource> result = new LinkedHashSet<Resource>(16);
//将根目录所有资源中所有匹配我们需要的资源(如spring-*)加载result中
for (Resource rootDirResource : rootDirResources) {
rootDirResource = resolveRootDirResource(rootDirResource);
if (isJarResource(rootDirResource)) {
result.addAll(doFindPathMatchingJarResources(rootDirResource, subPattern));
}
else if (rootDirResource.getURL().getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirResource, subPattern, getPathMatcher()));
}
else {
result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));
}
}
if (logger.isDebugEnabled()) {
logger.debug("Resolved location pattern [" + locationPattern + "] to resources " + result);
}
return result.toArray(new Resource[result.size()]);
}
protected String determineRootDir(String location) {
int prefixEnd = location.indexOf(":") + 1;
int rootDirEnd = location.length();
while (rootDirEnd > prefixEnd && getPathMatcher().isPattern(location.substring(prefixEnd, rootDirEnd))) {
rootDirEnd = location.lastIndexOf('/', rootDirEnd - 2) + 1;
}
if (rootDirEnd == 0) {
rootDirEnd = prefixEnd;
}
return location.substring(0, rootDirEnd);
}
加载到META-INF下的一个资源(classpath是加载到匹配的第一个资源,就算删除classpath下的notice.txt,他仍然可以
加载jar包中的notice.txt)
classpath: VS classpath*:的更多相关文章
- web.xml 配置中classpath: 与classpath*:的区别
首先 classpath是指 WEB-INF文件夹下的classes目录 解释classes含义: 1.存放各种资源配置文件 eg.init.properties log4j.properties s ...
- classpath: 和classpath*:的区别
classpath本质是jvm的根路径,jvm获取资源都是从该根路径下找的,注意这个根路径是个逻辑路径,并不是磁盘路径.比如两个jar包的路径是/a/a.jar和/b/b.jar,但是用classpa ...
- 在web.xml中classpath和classpath*的区别
classpath 和 classpath* 区别: classpath:只会到你指定的class路径中查找找文件; classpath*:不仅包含class路径,还包括jar文件中(class路径) ...
- WEB项目web.xml文件中classpath: 跟classpath*:使用的区别
引用一篇很不错的文章:http://blog.csdn.net/wxwzy738/article/details/16983935 首先 classpath是指 WEB-INF文件夹下的classes ...
- 【转载】Spring加载resource时classpath*:与classpath:的区别
免责声明: 本文转自网络文章,转载此文章仅为个人收藏,分享知识,如有侵权,请联系博主进行删除. 原文作者:kyfxbl 原文地址: spring配置中classpath和cla ...
- web.xml中classpath:和classpath*: 有什么区别?
web.xml中classpath:和classpath*: IccBoY applicationContext.xml 配置文件的存放位置 web.xml中classpath:和classp ...
- spring加载资源文件中classpath*与classpath的区别
在spring和MyBatis继承的时候,配置mapperLocations.一开始配置是这样的. 需要加载路径为com/thomas/base/mapper和com/thomas/bu/mapper ...
- Spring配置中的"classpath:"与"classpath*:"的区别研究(转)
Spring配置中的"classpath:"与"classpath*:"的区别研究(转) 概念解释及使用场景: classpath是指WEB-INF文件夹下 ...
- 转:web.xml 配置中classpath: 与classpath*:的区别
原文链接:web.xml 配置中classpath: 与classpath*:的区别 引用自:http://blog.csdn.net/wxwzy738/article/details/1698393 ...
随机推荐
- ViewPager+GridView实现横向滑动 仿大众点评
先看演示效果: 1 ViewPager类提供了多界面切换的新效果. 新效果有如下特征: [1] 当前显示一组界面中的其中一个界面. [2] 当用户通过左右滑动界面时,当前的屏幕显示当前界面和下一个界 ...
- IOS 页面间跳转
常用的就两种 一种通过导航,一种直接跳 第一种 直接跳转 思路大致就是new一个目的页面,然后设置下页面跳转动画 中间还可以做点目的页面的数据初始化: ValueInputView *valueVie ...
- Ruby的模型关系随笔
1 Class和Module的实例方法也就是所有具体类和具体Module的类方法,因为具体类和具体Module分别是Class和Module的实例.例如Object.new对应着Class#new,K ...
- RMAN还原遭遇ORA-32006&ORA-27102错误
案例环境: 服务器A: 操作系统 : Red Hat Enterprise Linux ES release 4 (Nahant Update 6) 数据库版本: Oracle Database ...
- linux shell for循环使用命令中读取到的值实例
#!/bin/bash file="states" for state in `cat $file` do echo "Visit beautiful $state&qu ...
- 【Linux学习】如何了解一个陌生的命令?
如何了解一个陌生的命令? 有一些命令可以用来了解某个命令本身的情况,比如这个命令的绝对路径. $which ls which 在默认路径中搜索命令,返回该命令的绝对路径. $whereis ls wh ...
- 利用varnish做Discuz论坛的缓存服务器
实验背景:公司有一台BBS服务器,用的是LNMP的架构搭建的.正好手头有一台空闲的虚拟机,于是想着给BBS前端加一台缓存服务器.于是选定了varnish,搜了很多教程,跌跌撞撞的完成了配置.这其中很多 ...
- ASP.NET 5 DNX SDK删除旧版本
ASP.NET 5各种升级后旧版本的DNX不会删除,想删除旧版本的DNX,可以通过以下命令完成 首先打开CMD或者Powershell 1.先输入dnvm看看命令中是否有uninstall 2.如果没 ...
- HashMap实现原理分析(转)
文章转自:http://blog.csdn.net/vking_wang/article/details/14166593 1. HashMap的数据结构 数据结构中有数组和链表来实现对数据的存储,但 ...
- CSS/CSS3常用样式小结
1.强制文本单行显示: white-space:nowrap; 多行文本最后省略号: display: -webkit-box; -webkit-line-clamp:2; overflow: hid ...