Spring中classpath*:和classpath的區別
以AbstractApplicationContext為例講解
AbstractApplicationContext持有一個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*:";
/**
* Resolve the given location pattern into Resource objects.
* <p>Overlapping resource entries that point to the same physical
* resource should be avoided, as far as possible. The result should
* have set semantics.
* @param locationPattern the location pattern to resolve
* @return the corresponding Resource objects
* @throws IOException in case of I/O errors
*/
Resource[] getResources(String locationPattern) throws IOException;
}
該接口擴展了ResourceLoader,提供了根據指定模式(例如Ant)的路徑獲取資源的方法。ResourceLoader則是提供了根據指定路徑獲取資源的能力。
AbstractApplicationContext持有的ResourcePatternResolver實例會在其無參構造函數中完成初始化
public AbstractApplicationContext() {
this.resourcePatternResolver = getResourcePatternResolver();
}
protected ResourcePatternResolver getResourcePatternResolver() {
return new PathMatchingResourcePatternResolver(this);
}
getResourcePatternResolver可以被重寫,例如AbstractRefreshableWebApplicationContext。我們暫時只分析AbstractApplicationContext。來看看PathMatchingResourcePatternResolver類,它實現了ResourcePatternResolver接口,來看看比較重要的方法
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 {
// Generally only look for a pattern after a prefix here,
// and on Tomcat only after the "*/" separator for its "war:" protocol.
int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 :
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)};
}
}
}
其中getPathMatcher默認為AntPathMatcher
跟著代碼一行行走
-
如果傳入的路徑以classpath*:開頭,分兩種情況
-
如果符合PathMatcher(此處可以簡化為Ant風格),則執行findPathMatchingResources
例如classpath*:*-db.properties
-
如果不符合PathMatcher,則執行findAllClassPathResources
例如classpath*:db.properties
-
-
如果傳入的路徑不以classpath:開頭,分兩種情況
-
如果符合PathMatcher(此處可以簡化為Ant風格),則執行findPathMatchingResources
例如*-db.properties
如果不符合PathMatcher,則調用其持有的ResourceLoader實例去獲取指定資源。此時只獲取到唯一一個資源,以AbstractApplicationContext為例,它將自己作為PathMatchingResourcePatternResolver的構造函數參數,因此PathMatchingResourcePatternResolver持有的ResourceLoader的實例就是AbstractApplicationContext自己,而AbstractApplicationContext繼承自DefaultResourceLoader,因此,這個地方會調用DefaultResourceLoader的getResource方法
-
接下來看看上文提到的幾個比較重要的方法做了什么
findPathMatchingResources
先看看其方法注釋
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.
該方法會遞歸查找所有所有符合規則的資源
findPathMatchingResources
Find all class location resources with the given location via the ClassLoader.
結論
其實從代碼看來classpath*:*-db.properties和classpath:*-db.properties效果是一樣的。而對于classpath*:db-properties和classpath:db-properties,前者會加載所有的db-properties,后者只會加載一個。可以這么簡單歸納,但凡是Ant風格的location,都會去加載所有符合規則的資源,否則只有在指定了classpath*:情況下才會去加載所有指定location的資源,其他情況下只加載一個指定location的資源,即便是找到了多個