记录一下spring里读取资源时的classpath*和classpath表达式的区别。不想看细节的可以直接跳到最后直接看结论。

读取资源的主要逻辑在PathMatchingResourcePatternResolver.getResources

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*:resource

    代码中的if逻辑,通过findAllClassPathResources,遍历整个classpath,搜索所有名字匹配的文件返回

  • classpath:resource

    代码中的else逻辑,getResourceLoader().getResource(locationPattern)最终调用tomcat提供的WebappClassLoader,从classpath中遍历每个目录(包括jar),寻找指定的resource直到找到指定的第一个匹配文件返回

读取完resource列表之后,AbstractBeanDefinitionReader.loadBeanDefinitions会载入得到resource列表

Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int loadCount = loadBeanDefinitions(resources);

结论

classpath*:resource中读取资源,就相当于把整个classpath下能找到的同名resource按照classpath中的次序拼接成一个大文件后载入,位于classpath后部的resource定义会覆盖前面的。由于tomcat会保证WEB-INF/class下的class会放在classpath中的第一位,所以导致jar中的同名resource定义覆盖app中的定义。

classpath:resource仅载入classpath路径中碰到的第一个resource。