首页 > 工作 > Spring上下文配置之classpath*问题

Spring上下文配置之classpath*问题

今天有同事碰到问题,打在jar包中的spring配置文件无法加载,后发现是上下文配置成了:classpath:spring/*.xml
改成classpath*:spring/*.xml后搞定。

在csdn中看到多篇文章,这么解释的:

classpath*:的出现是为了从多个jar文件中加载相同的文件.
classpath:只能加载找到的第一个文件.

感觉不是太可靠,简单翻了一下源码,在PathMatchingResourcePatternResolver中可以一探究竟。

解析的入口在这

	public Resource[] getResources(String locationPattern) throws IOException {
		Assert.notNull(locationPattern, "Location pattern must not be null");
		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()))) {
				// 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*:spring/*.xml或者classpath:spring/*.xml的完整location来解析时,返回findPathMatchingResources的调用结果,这个调用内部会解析出rootDirPath(除了文件名以外路径)后再调用这个方法。
只用rootDirPath,比如classpath*:spring/或者classpath:spring/来请求时,根据是否为classpath*:开始,确定其调用findAllClassPathResources或是new Resource[] {getResourceLoader().getResource(locationPattern)},这两个方法的区别就在于*时返回当前classes目录资源及所有jar包符合此规则的目录,无*时只返回当前classes目录资源。

全路径时调用的方法findPathMatchingResources的实现如下,在方法中的第三行,使用rootDirPath又调用了一次前面说的getResources方法。

	/**
	 * 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 (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()]);
	}

带*时的解析方法如下,方法中第四行会返回classes及所有jar中匹配的路径

	/**
	 * Find all class location resources with the given location via the ClassLoader.
	 * @param location the absolute path within the classpath
	 * @return the result as Resource array
	 * @throws IOException in case of I/O errors
	 * @see java.lang.ClassLoader#getResources
	 * @see #convertClassLoaderURL
	 */
	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()]);
	}

不带*时,会调用下面的方法,返回路径为spring/的Resource,被全路径调用的方法中的resolveRootDirResource解析后,就是本地的classes路径。

	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:spring/*.xml时:不会处理任何jar包里的数据(而不是只处理第一个碰到的),加载classes中的相关配置。
classpath*:spring/*.xml时:这个没有疑问,classes和jar中的全扫描加载。

  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.