<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    上善若水
    In general the OO style is to use a lot of little objects with a lot of little methods that give us a lot of plug points for overriding and variation. To do is to be -Nietzsche, To bei is to do -Kant, Do be do be do -Sinatra
    posts - 146,comments - 147,trackbacks - 0

    在《深入Spring IOC源碼之Resource》中已經(jīng)詳細(xì)介紹了SpringResource的抽象,Resource接口有很多實(shí)現(xiàn)類,我們當(dāng)然可以使用各自的構(gòu)造函數(shù)創(chuàng)建符合需求的Resource實(shí)例,然而Spring提供了ResourceLoader接口用于實(shí)現(xiàn)不同的Resource加載策略,即將不同Resource實(shí)例的創(chuàng)建交給ResourceLoader來(lái)計(jì)算。

    public interface ResourceLoader {

        //classpath

        String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;

        Resource getResource(String location);

        ClassLoader getClassLoader();

    }

    ResourceLoader接口中,主要定義了一個(gè)方法:getResource(),它通過(guò)提供的資源location參數(shù)獲取Resource實(shí)例,該實(shí)例可以是ClasPathResource、FileSystemResource、UrlResource等,但是該方法返回的Resource實(shí)例并不保證該Resource一定是存在的,需要調(diào)用exists方法判斷。該方法需要支持一下模式的資源加載:

    1.       URL位置資源,如”file:C:/test.dat”

    2.       ClassPath位置資源,如”classpath:test.dat”

    3.       相對(duì)路徑資源,如”WEB-INF/test.dat”,此時(shí)返回的Resource實(shí)例根據(jù)實(shí)現(xiàn)不同而不同。

    ResourceLoader接口還提供了getClassLoader()方法,在加載classpath下的資源時(shí)作為參數(shù)傳入ClassPathResource。將ClassLoader暴露出來(lái),對(duì)于想要獲取ResourceLoader使用的ClassLoader用戶來(lái)說(shuō),可以直接調(diào)用getClassLoader()方法獲得,而不是依賴于Thread Context ClassLoader,因?yàn)橛行r(shí)候ResourceLoader內(nèi)部使用自定義的ClassLoader

    在實(shí)際開(kāi)發(fā)中經(jīng)常會(huì)遇到需要通過(guò)某種匹配方式查找資源,而且可能有多個(gè)資源匹配這種模式,在Spring中提供了ResourcePatternResolver接口用于實(shí)現(xiàn)這種需求,該接口繼承自ResourceLoader接口,定義了自己的模式匹配接口:

    public interface ResourcePatternResolver extends ResourceLoader {

        String CLASSPATH_ALL_URL_PREFIX = "classpath*:";

        Resource[] getResources(String locationPattern) throws IOException;

    }

    ResourcePatternResolver定義了getResources()方法用于根據(jù)傳入的locationPattern查找和其匹配的Resource實(shí)例,并以數(shù)組的形式返回,在返回的數(shù)組中不可以存在相同的Resource實(shí)例。ResourcePatternResolver中還定義了”classpath*:”模式,用于表示查找classpath下所有的匹配Resource。

    Spring中,對(duì)ResourceLoader提供了DefaultResourceLoader、FileSystemResourceLoaderServletContextResourceLoader等單獨(dú)實(shí)現(xiàn),對(duì)ResourcePatternResolver接口則提供了PathMatchingResourcePatternResolver實(shí)現(xiàn)。并且ApplicationContext接口繼承了ResourcePatternResolver,在實(shí)現(xiàn)中,ApplicationContext的實(shí)現(xiàn)類會(huì)將邏輯代理給相關(guān)的單獨(dú)實(shí)現(xiàn)類,如PathMatchingResourceLoader等。在ApplicationContextResourceLoaderAware接口,可以將ResourceLoader(自身)注入到實(shí)現(xiàn)該接口的Bean中,在Bean中可以將其強(qiáng)制轉(zhuǎn)換成ResourcePatternResolver接口使用(為了安全,強(qiáng)轉(zhuǎn)前需要判斷)。在Spring中對(duì)ResourceLoader相關(guān)類的類圖如下:


    DefaultResourceLoader

    DefaultResourceLoaderResourceLoader的默認(rèn)實(shí)現(xiàn),AbstractApplicationContext繼承該類(關(guān)于這個(gè)繼承,簡(jiǎn)單吐槽一下,Spring內(nèi)部感覺(jué)有很多這種個(gè)人感覺(jué)使用組合更合適的繼承,比如還有AbstractBeanFactory繼承自FactoryBeanRegisterySupport,這個(gè)讓我看起來(lái)有點(diǎn)不習(xí)慣,而且也增加了類的繼承關(guān)系)。它接收ClassLoader作為構(gòu)造函數(shù)的參數(shù),或使用不帶參數(shù)的構(gòu)造函數(shù),此時(shí)ClassLoader使用默認(rèn)的ClassLoader(一般為Thread Context ClassLoader),ClassLoader也可以通過(guò)set方法后繼設(shè)置。

    其最主要的邏輯實(shí)現(xiàn)在getResource方法中,該方法首先判斷傳入的location是否以”classpath:”開(kāi)頭,如果是,則創(chuàng)建ClassPathResource(移除”classpath:”前綴),否則嘗試創(chuàng)建UrlResource,如果當(dāng)前location沒(méi)有定義URL的協(xié)議(即以”file:”、”zip:”等開(kāi)頭,比如使用相對(duì)路徑”resources/META-INF/MENIFEST.MF),則創(chuàng)建UrlResource會(huì)拋出MalformedURLException,此時(shí)調(diào)用getResourceByPath()方法獲取Resource實(shí)例。getResourceByPath()方法默認(rèn)返回ClassPathContextResource實(shí)例,在FileSystemResourceLoader中有不同實(shí)現(xiàn)。

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

            }

        }

    }

    protected Resource getResourceByPath(String path) {

        return new ClassPathContextResource(path, getClassLoader());

    }

    FileSystemResourceLoader

    FileSystemResourceLoader繼承自DefaultResourceLoader,它的getResource方法的實(shí)現(xiàn)邏輯和DefaultResourceLoader相同,不同的是它實(shí)現(xiàn)了自己的getResourceByPath方法,即當(dāng)UrlResource創(chuàng)建失敗時(shí),它會(huì)使用FileSystemContextResource實(shí)例而不是ClassPathContextResource

    protected Resource getResourceByPath(String path) {

        if (path != null && path.startsWith("/")) {

            path = path.substring(1);

        }

        return new FileSystemContextResource(path);

    }

    使用該類時(shí)要特別注意的一點(diǎn):即使location”/”開(kāi)頭,資源的查找還是相對(duì)于VM啟動(dòng)時(shí)的相對(duì)路徑而不是絕對(duì)路徑(從以上代碼片段也可以看出,它會(huì)先截去開(kāi)頭的”/”),這個(gè)和Servlet Container保持一致。如果需要使用絕對(duì)路徑,需要添加”file:”前綴。

    ServletContextResourceLoader

    ServletContextResourceLoader類繼承自DefaultResourceLoader,和FileSystemResourceLoader一樣,它的getResource方法的實(shí)現(xiàn)邏輯和DefaultResourceLoader相同,不同的是它實(shí)現(xiàn)了自己的getResourceByPath方法,即當(dāng)UrlResource創(chuàng)建失敗時(shí),它會(huì)使用ServletContextResource實(shí)例:

    protected Resource getResourceByPath(String path) {

        return new ServletContextResource(this.servletContext, path);

    }

    這里的path即使以”/”開(kāi)頭,也是相對(duì)ServletContext的路徑,而不是絕對(duì)路徑,要使用絕對(duì)路徑,需要添加”file:”前綴。

    PathMatchingResourcePatternResolver

    PathMatchingResourcePatternResolver類實(shí)現(xiàn)了ResourcePatternResolver接口,它包含了對(duì)ResourceLoader接口的引用,在對(duì)繼承自ResourceLoader接口的方法的實(shí)現(xiàn)會(huì)代理給該引用,同時(shí)在getResources()方法實(shí)現(xiàn)中,當(dāng)找到一個(gè)匹配的資源location時(shí),可以使用該引用解析成Resource實(shí)例。默認(rèn)使用DefaultResourceLoader類,用戶可以使用構(gòu)造函數(shù)傳入自定義的ResourceLoader。

    PathMatchingResourcePatternResolver還包含了一個(gè)對(duì)PathMatcher接口的引用,該接口基于路徑字符串實(shí)現(xiàn)匹配處理,如判斷一個(gè)路徑字符串是否包含通配符(’*’、’?’),判斷給定的path是否匹配給定的pattern等。Spring提供了AntPathMatcher對(duì)PathMatcher的默認(rèn)實(shí)現(xiàn),表達(dá)該PathMatcher是采用Ant風(fēng)格的實(shí)現(xiàn)。其中PathMatcher的接口定義如下:

    public interface PathMatcher {

        boolean isPattern(String path);

        boolean match(String pattern, String path);

        boolean matchStart(String pattern, String path);

        String extractPathWithinPattern(String pattern, String path);

    }

    isPattern(String path)

    判斷path是否是一個(gè)pattern,即判斷path是否包含通配符:

    public boolean isPattern(String path) {

        return (path.indexOf('*') != -1 || path.indexOf('?') != -1);

    }

    match(String pattern, String path)

    判斷給定path是否可以匹配給定pattern

    matchStart(String pattern, String path)

    判斷給定path是否可以匹配給定pattern,該方法不同于match,它只是做部分匹配,即當(dāng)發(fā)現(xiàn)給定path匹配給定path的可能性比較大時(shí),即返回true。在PathMatchingResourcePatternResolver中,可以先使用它確定需要全面搜索的范圍,然后在這個(gè)比較小的范圍內(nèi)再找出所有的資源文件全路徑做匹配運(yùn)算。

    AntPathMatcher中,都使用doMatch方法實(shí)現(xiàn),match方法的fullMatchtrue,而matchStartfullMatchfalse

    protected boolean doMatch(String pattern, String path, boolean fullMatch)

    doMatch的基本算法如下:

    1.       檢查patternpath是否都以”/”開(kāi)頭或者都不是以”/”開(kāi)頭,否則,返回false。

    2.       patternpath都以”/”為分隔符,分割成兩個(gè)字符串?dāng)?shù)組pattArraypathArray。

    3.       從頭遍歷兩個(gè)字符串?dāng)?shù)組,如果遇到兩給字符串不匹配(兩個(gè)字符串的匹配算法再下面介紹),返回false,否則,直到遇到pattArray中的”**”字符串,或pattArraypathArray中有一個(gè)遍歷完。

    4.       如果pattArray遍歷完:

    a)         pathArray也遍歷完,并且patternpath都以”/”結(jié)尾或都不以”/”,返回true,否則返回false。

    b)         pattArray沒(méi)有遍歷完,但fullMatchfalse,返回true。

    c)         pattArray只剩最后一個(gè)”*”,同時(shí)path”/”結(jié)尾,返回true。

    d)         pattArray剩下的字符串都是”**”,返回true,否則返回false。

    5.       如果pathArray沒(méi)有遍歷完,而pattArray遍歷完了,返回false

    6.       如果pathArraypattArray都沒(méi)有遍歷完,fullMatchfalse,而且pattArray下一個(gè)字符串為”**”時(shí),返回true

    7.       從后開(kāi)始遍歷pathArraypattArray,如果遇到兩個(gè)字符串不匹配,返回false,否則,直到遇到pattArray中的”**”字符串,或pathArraypattArray中有一個(gè)和之前的遍歷索引相遇。

    8.       如果是因?yàn)?/span>pathArray與之前的遍歷索引相遇,此時(shí),如果沒(méi)有遍歷完的pattArray所有字符串都是”**”,則返回true,否則,返回false。

    9.       如果pathArraypattArray中間都沒(méi)有遍歷完:

    a)         去除pattArray中相鄰的”**”字符串,并找到其下一個(gè)”**”字符串,其索引號(hào)為pattIdxTmp,他們的距離即為s

    b)         從剩下的pathArray中的第i個(gè)元素向后查找s個(gè)元素,如果找到所有s個(gè)元素都匹配,則這次查找成功,記itemp,如果沒(méi)有找到這樣的s個(gè)元素,返回false。

    c)         pattArray的起始索引設(shè)置為pattIdxTmp,將pathArray的索引號(hào)設(shè)置為temp+s,繼續(xù)查找,直到pattArraypathArray遍歷完。

    10.   如果pattArray沒(méi)有遍歷完,但剩下的元素都是”**”,返回true,否則返回false

    對(duì)路徑字符串?dāng)?shù)組中的字符串匹配算法如下:

    1.       pattern為模式字符串,str為要匹配的字符串,將兩個(gè)字符串轉(zhuǎn)換成兩個(gè)字符數(shù)組pattArraystrArray

    2.       遍歷pattArray直到遇到’*’字符。

    3.       如果pattArray中不存在’*’字符,則只有在pattArraystrArray的長(zhǎng)度相同兩個(gè)字符數(shù)組中所有元素都相同,其中pattArray中的’?’字符可以匹配strArray中的任何一個(gè)字符,否則,返回false。

    4.       如果pattArray只包含一個(gè)’*’字符,返回true

    5.       遍歷pattArraystrArray直到pattArray遇到’*’字符或strArray遍歷完,如果存在不匹配的字符,返回false。

    6.       如果因?yàn)?/span>strArray遍歷完成,而pattArray剩下的字符都是’*’,返回true,否則返回false

    7.       從末尾開(kāi)始遍歷pattArraystrArray,直到pattArray遇到’*’字符,或strArray遇到之前的遍歷索引,中間如果遇到不匹配字符,返回false

    8.       如果strArray遍歷完,而剩下的pattArray字符都是’*’字符,返回true,否則返回false

    9.       如果pattArraystrArray都沒(méi)有遍歷完(類似之前的算法):

    a)         去除pattArray相鄰的’*’字符,查找下一個(gè)’*’字符,記其索引號(hào)為pattIdxTmp,兩個(gè)’*’字符的相隔距離為s

    b)         從剩下的strArray中的第i個(gè)元素向后查找s個(gè)元素,如果有找到所有s個(gè)元素都匹配,則這次查找成功,記itemp,如果沒(méi)有到這樣的s個(gè)元素,返回false。

    c)         pattArray的起始索引設(shè)置為pattIdxTmp,strArray的起始索引設(shè)置為temp+s,繼續(xù)查找,直到pattArraystrArray遍歷完。

    10.   如果pattArray沒(méi)有遍歷完,但剩下的元素都是’*’,返回true,否則返回false

    String extractPathWithinPattern(String pattern, String path)

    去除path中和pattern相同的字符串,只保留匹配的字符串。比如如果pattern”/doc/csv/*.htm”,而path”/doc/csv/commit.htm”,則該方法的返回值為commit.htm。該方法默認(rèn)patternpath已經(jīng)匹配成功,因而算法比較簡(jiǎn)單:

    ’/’分割patternpath為兩個(gè)字符串?dāng)?shù)組pattArraypathArray,遍歷pattArray,如果該字符串包含’*’’?’字符,則并且pathArray的長(zhǎng)度大于當(dāng)前索引號(hào),則將該字符串添加到結(jié)果中。

    遍歷完pattArray后,如果pathArray長(zhǎng)度大于pattArray,則將剩下的pathArray都添加到結(jié)果字符串中。

    最后返回該字符串。

    不過(guò)也正是因?yàn)樵撍惴▽?shí)現(xiàn)比較簡(jiǎn)單,因而它的結(jié)果貌似不那么準(zhǔn)確,比如pattern的值為:/com/**/levin/**/commit.html,而path的值為:/com/citi/cva/levin/html/commit.html,其返回結(jié)果為:citi/levin/commit.html

    現(xiàn)在言歸正傳,看一下PathMatchingResourcePatternResolver中的getResources方法的實(shí)現(xiàn):

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

            }

        }

    }

    對(duì)classpath下的資源,相同名字的資源可能存在多個(gè),如果使用”classpath*:”作為前綴,表明需要找到classpath下所有該名字資源,因而需要調(diào)用findClassPathResources方法查找classpath下所有該名稱的Resource,對(duì)非classpath下的資源,對(duì)于不存在模式字符的location,一般認(rèn)為一個(gè)location對(duì)應(yīng)一個(gè)資源,因而直接調(diào)用ResourceLoader.getResource()方法即可(對(duì)classpath下沒(méi)有以”classpath*:”開(kāi)頭的location也適用)。

    findClassPathResources方法實(shí)現(xiàn)相對(duì)比較簡(jiǎn)單:

    適用ClassLoader.getResources()方法,遍歷結(jié)果URL集合,將每個(gè)結(jié)果適用UrlResource封裝,最后組成一個(gè)Resource數(shù)組返回即可。

    對(duì)包含模式匹配字符的location來(lái)說(shuō),需要調(diào)用findPathMatchingResources方法:

    protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {

        String rootDirPath = determineRootDir(locationPattern);

        String subPattern = locationPattern.substring(rootDirPath.length());

        Resource[] rootDirResources = getResources(rootDirPath);

        Set result = new LinkedHashSet(16);

        for (int i = 0; i < rootDirResources.length; i++) {

            Resource rootDirResource = resolveRootDirResource(rootDirResources[i]);

            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 (Resource[]) result.toArray(new Resource[result.size()]);

    }

    1.       determinRootDir()方法返回locationPattern中最長(zhǎng)的沒(méi)有出現(xiàn)模式匹配字符的路徑

    2.       subPattern則表示rootDirPath之后的包含模式匹配字符的路徑信pattern

    3.       使用getResources()獲取rootDirPath下的所有資源數(shù)組。

    4.       遍歷這個(gè)數(shù)組。

    a)         對(duì)jar中的資源,使用doFindPathMatchingJarResources()方法來(lái)查找和匹配。

    b)         對(duì)非jar中資源,使用doFindPathMatchingFileResources()方法來(lái)查找和匹配。

    doFindPathMatchingJarResources()實(shí)現(xiàn):

    1.       計(jì)算當(dāng)前ResourceJar文件中的根路徑rootEntryPath。

    2.       遍歷Jar文件中所有entry,如果當(dāng)前entry名以rootEntryPath開(kāi)頭,并且之后的路徑信息和之前從patternLocation中截取出的subPattern使用PathMatcher匹配,若匹配成功,則調(diào)用rootDirResource.createRelative方法創(chuàng)建一個(gè)Resource,將新創(chuàng)建的Resource添加入結(jié)果集中。

    doFindPathMatchingFileResources()實(shí)現(xiàn):

    1.       獲取要查找資源的根路徑(根路徑全名)

    2.       遞歸獲得根路徑下的所有資源,使用PathMatcher匹配,如果匹配成功,則創(chuàng)建FileSystemResource,并將其加入到結(jié)果集中。在遞歸進(jìn)入一個(gè)目錄前首先調(diào)用PathMatcher.matchStart()方法,以先簡(jiǎn)單的判斷是否需要遞歸進(jìn)去,以提升性能。

    protected void doRetrieveMatchingFiles(String fullPattern, File dir, Set result) throws IOException {

        if (logger.isDebugEnabled()) {

            logger.debug("Searching directory [" + dir.getAbsolutePath() +

                    "] for files matching pattern [" + fullPattern + "]");

        }

        File[] dirContents = dir.listFiles();

        if (dirContents == null) {

            throw new IOException("Could not retrieve contents of directory [" + dir.getAbsolutePath() + "]");

        }

        for (int i = 0; i < dirContents.length; i++) {

            File content = dirContents[i];

            String currPath = StringUtils.replace(content.getAbsolutePath(), File.separator, "/");

            if (content.isDirectory() && getPathMatcher().matchStart(fullPattern, currPath + "/")) {

                doRetrieveMatchingFiles(fullPattern, content, result);

            }

            if (getPathMatcher().match(fullPattern, currPath)) {

                result.add(content);

            }

        }

    }

    最后,需要注意的是,由于ClassLoader.getResources()方法存在的限制,當(dāng)傳入一個(gè)空字符串時(shí),它只能從classpath的文件目錄下查找,而不會(huì)從Jar文件的根目錄下查找,因而對(duì)”classpath*:”前綴的資源來(lái)說(shuō),找不到Jar根路徑下的資源。即如果我們有以下定義:”classpath*:*.xml”,如果只有在Jar文件的根目錄下存在*.xml文件,那么這個(gè)pattern將返回空的Resource數(shù)組。解決方法是不要再Jar文件根目錄中放文件,可以將這些文件放到Jar文件中的resourcesconfig等目錄下去。并且也不要在”classpath*:”之后加一些通配符,如”classpath*:**/*Enum.class”,至少在”classpath*:”后加入一個(gè)不存在通配符的路徑名。

    ServletContextResourcePatternResolver

    ServletContextResourcePatternResolver類繼承自PathMatchingResourcePatternResolver類,它重寫(xiě)了父類的文件查找邏輯,即對(duì)ServletContextResource資源使用ServletContext.getResourcePaths()方法來(lái)查找參數(shù)目錄下的文件,而不是File.listFiles()方法:

    protected Set doFindPathMatchingFileResources(Resource rootDirResource, String subPattern) throws IOException {

        if (rootDirResource instanceof ServletContextResource) {

            ServletContextResource scResource = (ServletContextResource) rootDirResource;

            ServletContext sc = scResource.getServletContext();

            String fullPattern = scResource.getPath() + subPattern;

            Set result = new LinkedHashSet(8);

            doRetrieveMatchingServletContextResources(sc, fullPattern, scResource.getPath(), result);

            return result;

        }

        else {

            return super.doFindPathMatchingFileResources(rootDirResource, subPattern);

        }

    }

    AbstractApplicationContext對(duì)ResourcePatternResolver接口的實(shí)現(xiàn)

    AbstractApplicationContext中,對(duì)ResourcePatternResolver的實(shí)現(xiàn)只是簡(jiǎn)單的將getResources()方法的實(shí)現(xiàn)代理給resourcePatternResolver字段,而該字段默認(rèn)在AbstractApplicationContext創(chuàng)建時(shí)新建一個(gè)PathMatchingResourcePatternResolver實(shí)例:

    public AbstractApplicationContext(ApplicationContext parent) {

        this.parent = parent;

        this.resourcePatternResolver = getResourcePatternResolver();

    }

    protected ResourcePatternResolver getResourcePatternResolver() {

        return new PathMatchingResourcePatternResolver(this);

    }

    public Resource[] getResources(String locationPattern) throws IOException {

        return this.resourcePatternResolver.getResources(locationPattern);

    }

    posted on 2012-12-01 23:21 DLevin 閱讀(15101) 評(píng)論(2)  編輯  收藏 所屬分類: Spring

    FeedBack:
    # re: 深入Spring IOC源碼之ResourceLoader
    2015-11-30 15:48 | nn
    我一直不太理解java 的加載機(jī)制,不知道為什么,我說(shuō)的理解 徹底透徹的理解不是簡(jiǎn)簡(jiǎn)單單的應(yīng)用,是不是要學(xué)習(xí) java虛擬機(jī)呢,  回復(fù)  更多評(píng)論
      
    # re: 深入Spring IOC源碼之ResourceLoader
    2015-12-01 10:06 | DLevin
    @nn
    看你要理解到什么樣的程度了,可以先讀這本書(shū):深入Java虛擬機(jī),或者:深入理解Java虛擬機(jī),個(gè)人感覺(jué)從理論的角度,第一本更好,從實(shí)戰(zhàn)的角度,第二本也還不錯(cuò)。如果你需要再深入的話,那就去讀虛擬機(jī)的源碼好了。。。。  回復(fù)  更多評(píng)論
      

    只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 国产V片在线播放免费无码| 在线观看H网址免费入口| 亚洲va在线va天堂va888www| 18女人水真多免费高清毛片| 亚洲成在人线在线播放无码| 国产亚洲日韩在线三区| 猫咪免费人成网站在线观看| 亚洲乱码中文字幕在线| 亚洲精品无码久久久久sm| 国产卡二卡三卡四卡免费网址| 特级一级毛片免费看| 亚洲日本在线观看| 日韩成人免费在线| 国内精品免费视频精选在线观看| 91丁香亚洲综合社区| 久久久久一级精品亚洲国产成人综合AV区 | 免费又黄又爽又猛的毛片| 国产精成人品日日拍夜夜免费| 日本亚洲免费无线码| 亚洲国产精品成人精品无码区在线 | 亚洲av午夜成人片精品网站| 成人免费a级毛片| 大地影院MV在线观看视频免费| 亚洲国产精品日韩av不卡在线| 亚洲成人在线电影| 国产在线观看免费视频播放器 | 国产精品亚洲mnbav网站 | 久久精品毛片免费观看| 特级毛片免费播放| 亚洲已满18点击进入在线观看| 中文字幕精品亚洲无线码一区| 成年女人午夜毛片免费看| 鲁大师在线影院免费观看| 一级毛片**免费看试看20分钟 | 国色精品va在线观看免费视频 | 久久免费高清视频| 一级做a爱过程免费视| 亚洲成熟丰满熟妇高潮XXXXX| 亚洲嫩草影院在线观看| 亚洲av不卡一区二区三区| 亚洲中文字幕无码久久综合网|