spring引用配置文件的时候classpath:和classpath*:的区别
来源:互联网 发布:nginx和apache是什么 编辑:程序博客网 时间:2024/06/11 17:26
我们平时使用spring框架的时候经常需要引入一些配置文件,如.propertis文件。其实有号和没有星号的区别不是很大,但有细微区别,这里我结合spring的源码简要分析有号和没有*号的的区别,有误的地方请指出。
classparth:只会从第一个classpath中加载,而classpath*:会从所有的classpath中加载如果要加载的资源,不在当前ClassLoader的路径里,那么如果用classpath:打头前缀是找不到的,这种情况下就需要使用classpath*:前缀;另一种情况下,在多个classpath中存在同名资源,都需要加载,那么用classpath:只会加载第一个,这种情况下也需要用classpath*:前缀。可想而知,用classpath*:需要遍历所有的classpath,所以加载速度较classpath慢,因此,在规划的时候,应该尽可能规划好资源文件所在的路径,尽量避免使用classpath*。
现在我们分析一下classpath 与 classpath*以及通配符spring是怎么处理的。
首先需要知道spring解析文件路径主要是spring-core-xxxxx.jar\org\springframework\core\io包及其子包完成的。先看InputStreamSource
在Spring中,定义了接口InputStreamSource,这个类中只包含一个方法:
public interface InputStreamSource {
/** * Return an {@link InputStream}. * <p>It is expected that each call creates a <i>fresh</i> stream. * <p>This requirement is particularly important when you consider an API such * as JavaMail, which needs to be able to read the stream multiple times when * creating mail attachments. For such a use case, it is <i>required</i> * that each {@code getInputStream()} call returns a fresh stream. * @return the input stream for the underlying resource (must not be {@code null}) * @throws IOException if the stream could not be opened * @see org.springframework.mail.javamail.MimeMessageHelper#addAttachment(String, InputStreamSource) */InputStream getInputStream() throws IOException;
}
InputStreamSource下面有一个Resource子接口,重要的方法有几个:
/**
* Return whether this resource actually exists in physical form.
*
This method performs a definitive existence check, whereas the
* existence of a {@code Resource} handle only guarantees a
* valid descriptor handle.
*/
boolean exists();
/** * Return whether the contents of this resource can be read, * e.g. via {@link #getInputStream()} or {@link #getFile()}. * <p>Will be {@code true} for typical resource descriptors; * note that actual content reading may still fail when attempted. * However, a value of {@code false} is a definitive indication * that the resource content cannot be read. * @see #getInputStream() */boolean isReadable();/** * Return whether this resource represents a handle with an open * stream. If true, the InputStream cannot be read multiple times, * and must be read and closed to avoid resource leaks. * <p>Will be {@code false} for typical resource descriptors. */boolean isOpen();
而Spring加载Resource文件是通过ResourceLoader来进行的,ResourceLoader接口只提供了classpath前缀的支持。
/* Pseudo URL prefix for loading from the class path: “classpath:” /
String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
而classpath*的前缀支持是在它的子接口ResourcePatternResolver中。
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*:";
……………….
}
ResourcePatternResolver有一个实现类:PathMatchingResourcePatternResolver,现在我们看看PathMatchingResourcePatternResolver的getResources()方法,
@Overridepublic Resource[] getResources(String locationPattern) throws IOException { Assert.notNull(locationPattern, "Location pattern must not be null"); //CLASSPATH_ALL_URL_PREFIX对应classpath*: if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) { // a class path resource (multiple resources for same name possible) if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) { // 包含通配符 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 { // 不以classpath*开头,且路径不包含通配符的就以这个方式处理; return new Resource[] {getResourceLoader().getResource(locationPattern)}; } }}
由此可以看出以classpath*开头,包含通配符的就调用findAllClassPathResources(….)处理,
protected Resource[] findAllClassPathResources(String location) throws IOException {
String path = location;
if (path.startsWith(“/”)) {
path = path.substring(1);
}
Set result = doFindAllClassPathResources(path);
return result.toArray(new Resource[result.size()]);
}
protected Set<Resource> doFindAllClassPathResources(String path) throws IOException { Set<Resource> result = new LinkedHashSet<Resource>(16); ClassLoader cl = getClassLoader(); Enumeration<URL> resourceUrls = (cl != null ? cl.getResources(path) : ClassLoader.getSystemResources(path)); while (resourceUrls.hasMoreElements()) { URL url = resourceUrls.nextElement(); result.add(convertClassLoaderURL(url)); } if ("".equals(path)) { // The above result is likely to be incomplete, i.e. only containing file system references. // We need to have pointers to each of the jar files on the classpath as well... addAllClassLoaderJarRoots(cl, result); } return result;}/** * Convert the given URL as returned from the ClassLoader into a {@link Resource}. * <p>The default implementation simply creates a {@link UrlResource} instance. * @param url a URL as returned from the ClassLoader * @return the corresponding Resource object * @see java.lang.ClassLoader#getResources * @see org.springframework.core.io.Resource */protected Resource convertClassLoaderURL(URL url) { return new UrlResource(url);}/** * Search all {@link URLClassLoader} URLs for jar file references and add them to the * given set of resources in the form of pointers to the root of the jar file content. * @param classLoader the ClassLoader to search (including its ancestors) * @param result the set of resources to add jar roots to */protected void addAllClassLoaderJarRoots(ClassLoader classLoader, Set<Resource> result) { if (classLoader instanceof URLClassLoader) { try { for (URL url : ((URLClassLoader) classLoader).getURLs()) { if (ResourceUtils.isJarFileURL(url)) { try { UrlResource jarResource = new UrlResource( ResourceUtils.JAR_URL_PREFIX + url.toString() + ResourceUtils.JAR_URL_SEPARATOR); if (jarResource.exists()) { result.add(jarResource); } } catch (MalformedURLException ex) { if (logger.isDebugEnabled()) { logger.debug("Cannot search for matching files underneath [" + url + "] because it cannot be converted to a valid 'jar:' URL: " + ex.getMessage()); } } } } } catch (Exception ex) { if (logger.isDebugEnabled()) { logger.debug("Cannot introspect jar files since ClassLoader [" + classLoader + "] does not support 'getURLs()': " + ex); } } } if (classLoader != null) { try { addAllClassLoaderJarRoots(classLoader.getParent(), result); } catch (Exception ex) { if (logger.isDebugEnabled()) { logger.debug("Cannot introspect jar files in parent ClassLoader since [" + classLoader + "] does not support 'getParent()': " + ex); } } }}这样一直加载到jar文件里面去了。
如果不以classpath*开头,且路径不包含通配符的就以这个方式处理;
// a single resource with the given name
return new Resource[] {getResourceLoader().getResource(locationPattern)};
如果不以classpath开头则使用DefaultResourceLoader 处理:
public class DefaultResourceLoader implements ResourceLoader {
private ClassLoader classLoader;/** * Create a new DefaultResourceLoader. * <p>ClassLoader access will happen using the thread context class loader * at the time of this ResourceLoader's initialization. * @see java.lang.Thread#getContextClassLoader() */public DefaultResourceLoader() { this.classLoader = ClassUtils.getDefaultClassLoader();}/** * Create a new DefaultResourceLoader. * @param classLoader the ClassLoader to load class path resources with, or {@code null} * for using the thread context class loader at the time of actual resource access */public DefaultResourceLoader(ClassLoader classLoader) { this.classLoader = classLoader;}/** * Specify the ClassLoader to load class path resources with, or {@code null} * for using the thread context class loader at the time of actual resource access. * <p>The default is that ClassLoader access will happen using the thread context * class loader at the time of this ResourceLoader's initialization. */public void setClassLoader(ClassLoader classLoader) { this.classLoader = classLoader;}/** * Return the ClassLoader to load class path resources with. * <p>Will get passed to ClassPathResource's constructor for all * ClassPathResource objects created by this resource loader. * @see ClassPathResource */@Overridepublic ClassLoader getClassLoader() { return (this.classLoader != null ? this.classLoader : ClassUtils.getDefaultClassLoader());}@Overridepublic Resource getResource(String location) { Assert.notNull(location, "Location must not be null"); if (location.startsWith("/")) { return getResourceByPath(location); } else 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*:resource/**/xxx.xml)
主要的思想就是
1.先获取目录,加载目录里面的所有资源
2.在所有资源里面进行查找匹配,找出我们需要的资源
/** * Find all resources that match the given location pattern via the * Ant-style PathMatcher. Supports resources in jar files and zip files * and in the file system. * @param locationPattern the location pattern to match * @return the result as Resource array * @throws IOException in case of I/O errors * @see #doFindPathMatchingJarResources * @see #doFindPathMatchingFileResources * @see org.springframework.util.PathMatcher */protected Resource[] findPathMatchingResources(String locationPattern) throws IOException { String rootDirPath = determineRootDir(locationPattern); String subPattern = locationPattern.substring(rootDirPath.length()); Resource[] rootDirResources = getResources(rootDirPath); Set<Resource> result = new LinkedHashSet<Resource>(16); for (Resource rootDirResource : rootDirResources) { rootDirResource = resolveRootDirResource(rootDirResource); if (rootDirResource.getURL().getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) { result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirResource, subPattern, getPathMatcher())); } else if (isJarResource(rootDirResource)) { result.addAll(doFindPathMatchingJarResources(rootDirResource, subPattern)); } 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()]);}
我们可以总结一下:
1.无论是classpath还是classpath*都可以加载整个classpath下(包括jar包里面)的资源文件。
2.classpath只会返回第一个匹配的资源,查找路径是优先在项目中存在资源文件,再查找jar包。
- spring引用配置文件的时候classpath:和classpath*:的区别
- Spring加载配置文件时 classpath* 和 classpath的区别
- Spring的classpath和classpath*的区别
- spring classpath和classpath*的区别
- Spring中classpath*和classpath的区别
- spring配置文件中classpath与classpath* 的区别
- classpath 和 classpath*的区别
- classPath和classPath*的区别
- classpath: 和 classpath*:的区别
- classpath: 和classpath*:的区别
- classpath和classpath*的区别
- classpath和classpath*的区别
- classpath和classpath*的区别
- Spring下面的classpath 和 classpath* 区别的简单讲解
- Spring下面的classpath 和 classpath* 区别的简单讲解
- spring配置中classpath和classpath*的区别
- spring中classpath和classpath*的配置区别
- spring中classpath和classpath*的配置区别
- xcode错误信息汇总
- POJ 2559 Largest Rectangle in a Histogram --维护单调栈
- 使用Druid监控SQL执行状态
- 关于各种进制之间互相转化(以十进制为桥梁)
- JS扑克牌特效
- spring引用配置文件的时候classpath:和classpath*:的区别
- Git基本用法
- node.js 中嵌入perl编程
- HDU1242 Rescue(广搜BFS+优先队列)
- lua协程
- ajax原理
- Silver Cow Party
- RET指令
- 【HTML5】过山车