Spring的classpath與classpath*通配符加載配置文件


classpath 與 classpath*以及通配符是怎么處理的

Spring加載Resource文件是通過ResourceLoader來進行的,那么我們就先來看看ResourceLoader的繼承體系,讓我們對這個模塊有一個比較系統的認知。

首先,我們來看下ResourceLoader的源碼

 

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

 

我們發現,其實ResourceLoader接口只提供了classpath前綴的支持。而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*:";  
      
          
        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小類進行處理,

處理的流程圖如下:

http://img.blog.csdn.net/20130712113042203?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemwzNDUwMzQx/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast

其實很簡單

  • 如果以classpath開頭,則創建為一個ClassPathResource,否則則試圖以URL的方式加載資源,創建一個UrlResource.
  • 如果路徑包含通配符的, 這種情況是最復雜的,涉及到層層遞歸,那我把加了注釋的代碼發出來大家看一下,其實主要的思想就是

 

1.先獲取目錄,加載目錄里面的所有資源

 

2.在所有資源里面進行查找匹配,找出我們需要的資源
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()]);  
    } 

好了,說了這么多,看下面的例子:

classpath:spring-servlet.xml

說明:無通配符,必須完全匹配
 
classpath:spring-servlet?.xml
說明:匹配一個字符,例如 spring-servlet1.xml 、 spring-servlet2.xml
 
classpath:config/*/spring-servlet.xml
說明:匹配零個或多個字符串(只針對名稱,不匹配目錄分隔符等),例如:config/a/ spring-servlet.xml 、 user/b/ spring-servlet.xml ,但是不匹配 user/ spring-servlet.xml
 
classpath:config/**/spring-servlet.xml
說明:匹配路徑中的零個或多個目錄,例如: config/a/ab/abc/ spring-servlet.xml,同時也能匹配 config/ spring-servlet.xml
 
classpath:**/*.xml
說明:表示在所有的類路徑中查找和加載文件名以“.xml”結尾的配置文件,但重復的文件名只加載其中一個,視加載順序決定
 
classpath*:config/**/*.xml
classpath*:**/*.xml
說明:“classpath*:”表示加載多個資源文件,即使重名也會被加載,j包括jar包里的重復的xml文件。
 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM