ResourceLoader
2019獨角獸企業(yè)重金招聘Python工程師標準>>>
前言
不使用框架,我們是如何訪問資源的?帶著這個思考閱讀本文
何為資源
class文件;vm/html文件;圖像/聲音;xml/xsd等配置文件都可以稱之為資源,而我們平常最長做的事情就是定義資源,保存資源,定位并加載資源,而本文就是對此做了闡述。
如何訪問資源
由于資源具有類型多樣性和存儲位置的差異,導致訪問資源的方式也千差萬別,以下是幾個主要的資源訪問方法,也是其他框架加載資源的底層方法。
訪問ClassPath中的資源
訪問文件系統(tǒng)中的資源
訪問web應用中的資源
訪問jar包中的資源
上述訪問資源的方法都是通過sun的API來實現(xiàn)的資源訪問,在 Sun 所提供的標準 API 里,資源訪問通常由 java.net.URL 和文件 IO 來完成,尤其是當我們需要訪問來自網(wǎng)絡的資源時,通常會選擇 URL 類。URL 類可以處理一些常規(guī)的資源訪問問題,但依然不能很好地滿足所有底層資源訪問的需要,比如,暫時還無法從類加載路徑、或相對于 ServletContext 的路徑來訪問資源,雖然 Java 允許使用特定的 URL 前綴注冊新的處理類(例如已有的 http: 前綴的處理類),但是這樣做通常比較復雜,而且 URL 接口還缺少一些有用的功能,比如檢查所指向的資源是否存在等。
ClassLoader的資源加載方式
本節(jié)主要討論資源的加載,不涉及雙親委派這些內容,主要涉及到的類有URL,URLClassLoader,URLClassPath,主要解決了以下問題:
1、有哪些資源加載方式?通俗點就是說支持哪些協(xié)議?
2、對于網(wǎng)絡上的jar資源,同時又依賴了網(wǎng)絡端本地的文件資源,如何加載?通俗點就是說協(xié)議可以嵌套么?
3、ClassPath如何管理Urls,并且快速定位到指定資源所在的絕對路徑的?通俗點就是說難道保存了所有資源和絕對路徑的映射么?
4、對于同一個名稱的資源,批量加載和單個加載有什么區(qū)別?通俗點比喻就是說classpath:和classpath*的區(qū)別(當然是不支持這兩種協(xié)議的啊)?
加載單個文件
先舉個加載class文件的例子:大致過程是先預處理所需加載的類名,轉換為具體的相對路徑的名稱,com.alibaba.test->com/alibaba/test,然后依據(jù)classPath和該相對路徑找到相應的資源,最后defineClass生成應用中需要用到的class實例。
【URLClassPath】 public?URL?findResource(String?name,?boolean?check)?{Loader?loader;for?(int?i?=?0;?(loader?=?getLoader(i))?!=?null;?i++)?{URL?url?=?loader.findResource(name,?check);if?(url?!=?null)?{return?url;}}return?null;}參數(shù)name就是轉換為相對路徑的class文件的位置,每一個classPath管理的url對應于一個loader,因為每個url的加載方式都不同,可以是jar或者file或者net。上面的代碼可以看到,遍歷所有的loader,找到可以加載該資源的loader,當然了,最差的情況就是遍歷完所有的loader才最終找到資源,不過好在classloader緩存了每個查找到的class文件,但是這種代碼的設計略欠優(yōu)雅。盒子里有很多巧克力,現(xiàn)在想找到紅色巧克力所在的盒子,元芳,你怎么看?
【URLClassPath】 private?synchronized?Loader?getLoader(int?index)?{//?Expand?URL?search?path?until?the?request?can?be?satisfied//?or?the?URL?stack?is?empty.while?(loaders.size()?<?index?+?1)?{//?Pop?the?next?URL?from?the?URL?stackURL?url;synchronized?(urls)?{if?(urls.empty())?{return?null;}?else?{url?=?(URL)urls.pop();}}//?Skip?this?URL?if?it?already?has?a?Loader.?(Loader//?may?be?null?in?the?case?where?URL?has?not?been?opened//?but?is?referenced?by?a?JAR?index.)if?(lmap.containsKey(url))?{continue;}//?Otherwise,?create?a?new?Loader?for?the?URL.Loader?loader;try?{loader?=?getLoader(url);//?If?the?loader?defines?a?local?class?path?then?add?the//?URLs?to?the?list?of?URLs?to?be?opened.URL[]?urls?=?loader.getClassPath();if?(urls?!=?null)?{push(urls);}}?catch?(IOException?e)?{//?Silently?ignore?for?now...continue;}//?Finally,?add?the?Loader?to?the?search?path.loaders.add(loader);lmap.put(url,?loader);}return?(Loader)loaders.get(index);}這是個比較重要的類,加載資源的時候再初始化loader,由于有些jar包的url依賴了一些jar包,因此代碼將未處理的url置于棧中,每次出棧初始化對應的loader,如果是jar包的url還會將其依賴的class-path壓棧處理。下面看看如何依據(jù)url來決策loader的加載方式的。
【URLClassPath】 private?Loader?getLoader(final?URL?url)?throws?IOException?{try?{return?(Loader)java.security.AccessController.doPrivileged(new?java.security.PrivilegedExceptionAction()?{public?Object?run()?throws?IOException?{String?file?=?url.getFile();if?(file?!=?null?&&?file.endsWith("/"))?{if?("file".equals(url.getProtocol()))?{return?new?FileLoader(url);}?else?{return?new?Loader(url);}}?else?{return?new?JarLoader(url,?jarHandler,?lmap);}}});}?catch?(java.security.PrivilegedActionException?pae)?{throw?(IOException)pae.getException();}}
從上面可以看到主要有三種加載方式:默認的加載方式、文件加載方式和jar包的加載方式,決策方式是依據(jù)url的協(xié)議。這里有個注意點:默認的加載方式和jar協(xié)議的加載方式都是支持遠程加載的,但是file協(xié)議是直接讀本地文件。
看看下面幾個url應該是采用哪種加載策略:http://www.springframework.org,http://localhost/jetty-all-8.1.7.v20120910.jar,file:///Users/apple/software/External-Jars/jetty-all-8.1.7.v20120910.jar,http://www.springframework.org/
接下來分析默認的加載方式和基于jar包的加載方式:
【URLClassPath&Loader】 URL?findResource(final?String?name,?boolean?check)?{URL?url;try?{url?=?new?URL(base,?ParseUtil.encodePath(name,?false));}?catch?(MalformedURLException?e)?{throw?new?IllegalArgumentException("name");}try?{if?(check)?{URLClassPath.check(url);}/**?For?a?HTTP?connection?we?use?the?HEAD?method?to*?check?if?the?resource?exists.*/URLConnection?uc?=?url.openConnection();if?(uc?instanceof?HttpURLConnection)?{HttpURLConnection?hconn?=?(HttpURLConnection)uc;hconn.setRequestMethod("HEAD");if?(hconn.getResponseCode()?>=?HttpURLConnection.HTTP_BAD_REQUEST)?{return?null;}}?else?{//?our?best?guess?for?the?other?casesInputStream?is?=?url.openStream();is.close();}return?url;}?catch?(Exception?e)?{return?null;}}封裝了url,然后直接openConnection了有木有,但如果是本地資源可怎么辦,url這個類是怎么區(qū)分的?這點也糾結了我好久,其實在new url的時候就會依據(jù)協(xié)議來指定相應的hanlder,http的自然就是httphandler了,這也就是我們在框架中經常看到openConnection這個方法,確沒有配置handler的原因。上面還有一個注意點,打開了流,確沒有讀數(shù)據(jù),我想是為了check的原因吧!畢竟throw了就會返回null了。
下面拿http://localhost/jetty-all-8.1.7.v20120910.jar?這個url來分析JarLoader
【URLClassPath&JarLoader】 Resource?getResource(final?String?name,?boolean?check)?{if?(metaIndex?!=?null)?{if?(!metaIndex.mayContain(name))?{return?null;}}try?{ensureOpen();}?catch?(IOException?e)?{throw?(InternalError)?new?InternalError().initCause(e);}final?JarEntry?entry?=?jar.getJarEntry(name);if?(entry?!=?null)return?checkResource(name,?check,?entry);if?(index?==?null)return?null;HashSet?visited?=?new?HashSet();return?getResource(name,?check,?visited);}ensureOpen這個方法會去依據(jù)url生成JarFile,這里當然依據(jù)url的協(xié)議,這里選擇了從遠程加載,然后再加載jar包里面的資源,如下。
private?JarFile?getJarFile(URL?url)?throws?IOException?{//?本地文件直接生成JarFileif?(isOptimizable(url))?{FileURLMapper?p?=?new?FileURLMapper?(url);if?(!p.exists())?{throw?new?FileNotFoundException(p.getPath());}return?new?JarFile?(p.getPath());}//網(wǎng)絡資源通過http協(xié)議緩存在本地的jar_tmp中URLConnection?uc?=?getBaseURL().openConnection();uc.setRequestProperty(USER_AGENT_JAVA_VERSION,?JAVA_VERSION);return?((JarURLConnection)uc).getJarFile();}生成JarFile之后,再加載其中的class等資源
【JarLoader】 Resource?checkResource(final?String?name,?boolean?check,final?JarEntry?entry)?{final?URL?url;try?{url?=?new?URL(getBaseURL(),?ParseUtil.encodePath(name,?false));if?(check)?{URLClassPath.check(url);}}?catch?(MalformedURLException?e)?{return?null;//?throw?new?IllegalArgumentException("name");}?catch?(IOException?e)?{return?null;}?catch?(AccessControlException?e)?{return?null;}return?new?Resource()?{public?String?getName()?{?return?name;?}public?URL?getURL()?{?return?url;?}public?URL?getCodeSourceURL()?{?return?csu;?}public?InputStream?getInputStream()?throws?IOException{?return?jar.getInputStream(entry);?}public?int?getContentLength(){?return?(int)entry.getSize();?}public?Manifest?getManifest()?throws?IOException{?return?jar.getManifest();?};public?Certificate[]?getCertificates(){?return?entry.getCertificates();?};public?CodeSigner[]?getCodeSigners(){?return?entry.getCodeSigners();?};};}這篇文檔描述的jar文件的加載可以參考下:http://fly-hyp.iteye.com/blog/296625
加載多個文件
這個沒什么好解釋的
【URLClassPath】 public?Enumeration?findResources(final?String?name,final?boolean?check)?{return?new?Enumeration()?{private?int?index?=?0;private?URL?url?=?null;private?boolean?next()?{if?(url?!=?null)?{return?true;}?else?{Loader?loader;while?((loader?=?getLoader(index++))?!=?null)?{url?=?loader.findResource(name,?check);if?(url?!=?null)?{return?true;}}return?false;}}public?boolean?hasMoreElements()?{return?next();}public?Object?nextElement()?{if?(!next())?{throw?new?NoSuchElementException();}URL?u?=?url;url?=?null;return?u;}};}Spring的優(yōu)化點
個人感覺Spring的資源加載最突出的特點是抽象了Resource,用策略模式的方式加載資源。Resource接口就是策略模式的典型應用,Resource 接口就代表資源訪問策略,但具體采用哪種策略實現(xiàn),Resource 接口并不理會。客戶端程序只和 Resource 接口耦合,并不知道底層采用何種資源訪問策略,這樣應用可以在不同的資源訪問策略之間自由切換。
Resource in spring
Spring為Resource提供了如下實現(xiàn)類:
這些 Resource 實現(xiàn)類,針對不同的的底層資源,提供了相應的資源訪問邏輯,并提供便捷的包裝,以利于客戶端程序的資源訪問。
1、UrlResource該類僅僅是對url的一個封裝,支持url所支持的那些協(xié)議裝載資源,特點是無上下文,一般是提供底層服務所用。
public?class?UrlResourceTest{public?static?void?main(String[]?args)?throws?Exception{//?創(chuàng)建一個?Resource?對象,指定從文件系統(tǒng)里讀取資源UrlResource?ur?=?new?UrlResource("file:book.xml");//?獲取該資源的簡單信息System.out.println(ur.getFilename());System.out.println(ur.getDescription());//?創(chuàng)建?Dom4j?的解析器SAXReader?reader?=?new?SAXReader();Document?doc?=?reader.read(ur.getFile());//?獲取根元素Element?el?=?doc.getRootElement();List?l?=?el.elements();//?此處省略了訪問、輸出?XML?文檔內容的代碼。...}}2、ClassPathResource該類主要功能是訪問classpath下的資源,底層依舊采用uri來實現(xiàn),特點是有上下文,并且增加了class協(xié)議
public?class?ClassPathResourceTest{public?static?void?main(String[]?args)?throws?Exception{//?創(chuàng)建一個?Resource?對象,從類加載路徑里讀取資源ClassPathResource?cr?=?new?ClassPathResource("book.xml");//?獲取該資源的簡單信息System.out.println(cr.getFilename());System.out.println(cr.getDescription());//?創(chuàng)建?Dom4j?的解析器SAXReader?reader?=?new?SAXReader();Document?doc?=?reader.read(cr.getFile());//?獲取根元素Element?el?=?doc.getRootElement();List?l?=?el.elements();//?此處省略了訪問、輸出?XML?文檔內容的代碼。...}}3、FileSystemResource
該類的主要功能是訪問文件系統(tǒng)下的資源,封裝了file和path,特點是功能單一,但是使用 FileSystemResource 也可消除底層資源訪問的差異,程序通過統(tǒng)一的 Resource API 來進行資源訪問。并且執(zhí)行 Spring 框架的某方法時,該方法接受一個代表資源路徑的字符串參數(shù),當 Spring 識別該字符串參數(shù)中包含 file: 前綴后,系統(tǒng)將會自動創(chuàng)建 FileSystemResource 對象。
雖然提供了如此多的實現(xiàn)類,但是spring只需要和Resource接口耦合,提供一個context,spring會智能的識別出哪個Resource,而這個context就是ApplicationContext自身(自身是有默認資源實現(xiàn)的)和路徑參數(shù)(當然是根據(jù)協(xié)議了)。當 Spring 應用需要進行資源訪問時,實際上并不需要直接使用 Resource 實現(xiàn)類,而是調用 ApplicationContext 實例的 getResource() 方法來獲得資源,ApplicationContext 將會負責選擇 Resource 的實現(xiàn)類,也就是確定具體的資源訪問策略,從而將應用程序和具體的資源訪問策略分離開來,這就體現(xiàn)了策略模式的優(yōu)勢。細節(jié)可以參照:
【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?patternreturn?findPathMatchingResources(locationPattern);}else?{//?all?class?path?resources?with?the?given?namereturn?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?patternreturn?findPathMatchingResources(locationPattern);}else?{//?a?single?resource?with?the?given?namereturn?new?Resource[]?{getResourceLoader().getResource(locationPattern)};}}}
4、回顧上面的Resource的類圖,ContextResource接口的作用是用來干什么的?
ContextResource接口類只有一個接口方法:“String getPathWithinContext()”,該方法獲取Resource自身所在的path,可應用在這樣一種場景:一份xml中可能會import一些別的Resource,但是import的這些Resource也許會依賴宿主的為主,因此需要獲取宿主的url來定位目標Resource的位置。
正如其名,兼有Resource和Context的功能!
ResourceLoader in spring
Spring框架的ApplicationContext 不僅是 Spring 容器,而且它還是資源訪問策略的“決策者”,也就是策略模式中 Context 對象,它將為客戶端代碼“智能”地選擇策略實現(xiàn),當ApplicationContext 實例獲取 Resource 實例時,系統(tǒng)將默認采用與 ApplicationContext 相同的資源訪問策略。
畫圖是門藝術活啊!~
ResourceLoader的加載策略如下:
1、解析資源名稱,如果前綴帶有“classpath*”,轉到2,否則轉到3;
2、判斷是否符合ANT風格的模式,如果匹配,轉至4,否則轉至5;
3、判斷是偶符合ANT風格的模式,如果匹配,轉至4,否則轉至6;
4、解析匹配目標路徑的所有資源,并加載;
5、加載classpath下該名稱的資源組;
6、判斷資源名稱,若帶有"classpath"前綴,轉至7,否則轉至8;
7、加載classpath下該名稱的資源;
8、依據(jù)資源名稱的url判斷該資源是否存在,如果存在,轉至9,否則轉至10;
9、通過SUN API的URL類來加載該資源
10、采用ApplicationContext的默認資源加載策略定義該Resource的身份并加載,比如ClassPathXmlApplicationContext的默認加載資源為:ClassPathContextResource。
常見的加載協(xié)議有:
1、classpath:以 ClassPathResource 實例來訪問類路徑里的資源。
2、file:以 UrlResource 實例訪問本地文件系統(tǒng)的資源。
3、http:以 UrlResource 實例訪問基于 HTTP 協(xié)議的網(wǎng)絡資源。
4、無前綴:由于 ApplicationContext 的實現(xiàn)類來決定訪問策略。
Spring的Resource注入
Spring 提供的資源訪問策略,但這些依賴訪問策略要么需要使用 Resource 實現(xiàn)類,要么需要使用 ApplicationContext 來獲取資源。實際上,當應用程序中的 Bean 實例需要訪問資源時,Spring 有更好的解決方法:直接利用依賴注入。Spring 框架不僅充分利用了策略模式來簡化資源訪問,而且還將策略模式和 IoC 進行充分地結合,最大程度地簡化了 Spring 資源訪問。因此Bean實例要訪問資源,有兩種方案:
1、代碼中獲取 Resource 實例。
2、使用依賴注入。
對于第一種方式的資源訪問,當程序獲取 Resource 實例時,總需要提供 Resource 所在的位置,不管通過 FileSystemResource 創(chuàng)建實例,還是通過 ClassPathResource 創(chuàng)建實例,或者通過 ApplicationContext 的 getResource() 方法獲取實例,都需要提供資源位置。這意味著:資源所在的物理位置將被耦合到代碼中,如果資源位置發(fā)生改變,則必須改寫程序。因此,通常建議采用第二種方法,讓 Spring 為 Bean 實例依賴注入資源。下面舉個依賴注入的例子:
<?xml?version="1.0"?encoding="GBK"?><!--?指定?Spring?配置文件的?DTD?信息?--><!DOCTYPE?beans?PUBLIC?"-//SPRING//DTD?BEAN?2.0//EN"><!--?Spring?配置文件的根元素?--><beans><bean?id="test"?class="lee.TestBean"><!--?注入資源?--><property?name="resource" value="classpath:book.xml"/></bean></beans> public?class?TestBean{private?Resource?res;//?依賴注入?Resource?資源的?setter?方法public?void?setResource(Resource?res) { this.res?=?res; }至于里面的原理,則是通過Spring的BeanFactoryPostProcessor拓展機制來實現(xiàn)的:
【ResourceEditorRegistrar】 public?void?registerCustomEditors(PropertyEditorRegistry?registry)?{ResourceEditor?baseEditor?=?new?ResourceEditor(this.resourceLoader);registry.registerCustomEditor(Resource.class,?baseEditor);registry.registerCustomEditor(InputStream.class,?new?InputStreamEditor(baseEditor));registry.registerCustomEditor(File.class,?new?FileEditor(baseEditor));registry.registerCustomEditor(URL.class,?new?URLEditor(baseEditor));ClassLoader?classLoader?=?this.resourceLoader.getClassLoader();registry.registerCustomEditor(Class.class,?new?ClassEditor(classLoader));registry.registerCustomEditor(URI.class,?new?URIEditor(classLoader));if?(this.resourceLoader?instanceof?ResourcePatternResolver)?{registry.registerCustomEditor(Resource[].class,new?ResourceArrayPropertyEditor((ResourcePatternResolver)?this.resourceLoader));}}批量注入,WEBX3文檔里面有這樣的例子:
public?void?setLocations(URL[]?resources)??{this.resources?=?resources;} <property?name="locations"?value="WEB-INF/webx-*.xml"?/>但是對比上面的代碼發(fā)現(xiàn),貌似只支持Resource[]類型的批量注入,URL[]經過測試也是行不通的,此處希望讀者能指出我理解上的錯誤,或者webx3文檔這處寫得有問題!
Spring資源加載的缺點
1、不透明性
必須使用詳細的路徑來訪問資源。如果需要修改資源文件位置,那就需要修改所有的引用。說到底就是對于資源文件的管理力度(筆者覺得SpringExt做出的改進源自于此)不夠。
2、無擴展性
無法增加新的資源加載方式。
ps:webx3文檔中還提出了“魚和熊掌不可兼得”的缺點,其實不盡然,就算是使用了FileSystemXmlApplicationContext,你也可以指明資源的訪問協(xié)議來替換默認的加載方式。
那么針對于前兩種缺點,SpringExt做了哪些改進呢?
SpringExt資源加載的擴展點
替換Spring的ResourceLoader
A、如何配置
當你不使用它時,Spring原有的ResourceLoader功能不受影響;但當你在spring配置文件中添加Resource Loading服務時,ResourceLoader即被切換到新的機制。新的機制可兼容原有的Spring配置和代碼,但支持更多的資源裝載方式,以及更多的功能,如資源重命名、資源重定向等。你只需要在配置文件中增加以下內容,就可以將Spring?ResourceLoader機制替換成Webx的Resource Loading服務:
<resource-loading><resource-alias?pattern="/"?name="/webroot"?/><resource?pattern="/webroot"?internal="true"><res-loaders:webapp-loader?/></resource><resource?pattern="/classpath"?internal="true"><res-loaders:classpath-loader?/></resource></?resource-loading>B、新的ResourceLoader如何替換為Spring原生態(tài)的實現(xiàn),又如何兼容原有的功能?
1、自定義的ResourceLoader與ResourcePatternResolver兼容原有spring的功能
protected?Resource?getResourceByPath(String?path)?{Resource?resource?=?null;if?(resourceLoadingExtender?!=?null)?{resource?=?resourceLoadingExtender.getResourceByPath(path);}if?(resource?==?null)?{resource?=?super.getResourceByPath(path);}return?resource;}/***?擴展<code>ResourcePatternResolver</code>機制,實現(xiàn)自定義的資源裝載。*/@Overrideprotected?ResourcePatternResolver?getResourcePatternResolver()?{final?ResourcePatternResolver?defaultResolver?=?super.getResourcePatternResolver();return?new?ResourcePatternResolver()?{public?Resource[]?getResources(String?locationPattern)?throws?IOException?{ResourcePatternResolver?resolver?=?null;if?(resourceLoadingExtender?!=?null)?{resolver?=?resourceLoadingExtender.getResourcePatternResolver();}if?(resolver?==?null)?{resolver?=?defaultResolver;}return?resolver.getResources(locationPattern);}2、RespurceLoadingService是一個受容器管理的Bean,那么它的生命周期始于何時,初始化又始于何時?
public?class?ResourceLoadingSupport?implements?ResourceLoadingExtender,?ApplicationListener 【ResourceLoadingSupport】 public?void?onApplicationEvent(ApplicationEvent?event)?{if?(event?instanceof?ContextRefreshedEvent)?{contextRefreshed?=?true;resourceLoadingService?=?getResourceLoadingServiceFromContext();}} 【ResourceLoadingSupport】 private?ResourceLoadingService?getResourceLoadingServiceFromContext()?{try?{return?(ResourceLoadingService)?factory.getBean(resourceLoadingServiceName);}?catch?(IllegalStateException?e)?{//?beanFactory未準備好,試一下parent?factory。如果均取不到ResourceLoadingService,返回null,不打日志ApplicationContext?parent?=?factory.getParent();if?(parent?!=?null)?{try?{return?(ResourceLoadingService)?parent.getBean(resourceLoadingServiceName);}?catch?(Exception?ee)?{}}}?catch?(NoSuchBeanDefinitionException?e)?{if?(!complained)?{complained?=?true;log.warn("ResourceLoadingService?does?not?exists:?beanName={}",?resourceLoadingServiceName);}}因此可以看到,它的生命周期始于容器啟動時,而初始化于容器refresh之后。
SpringExt擴展的功能點
SpringExt把資源加載的方式和資源的類型集中管理了起來,因此體現(xiàn)了易拓展和透明的特點。下面結合代碼和功能點來看看有哪些拓展點。
1、重命名資源
優(yōu)點:環(huán)境無關性。
【AbstractResourceLoadingContext】 if?(findBestMatch())?{//?findBestMatch()?情況1.?找到alias,但沒有找到最終的resource?mappingif?(lastMatchedPattern?instanceof?ResourceAlias)?{if?(parent?!=?null)?{log.trace("Resource?\"{}\"?not?found.??Trying?to?find?it?in?super?ResourceLoadingService",resourceName);try?{resource?=?loadParentResource(resourceName,?options);}?catch?(ResourceNotFoundException?e)?{//?alias將改變resourceName,故保存異常作為caused?by異常chainingException?=?e;}}}?else?{//?findBestMatch()?情況2,?3.?找到resource?mappingResourceLoaderMapping?mapping?=?(ResourceLoaderMapping)?lastMatchedPattern;resource?=?loadMappedResource(mapping,?options);2、多個Loader查找資源
優(yōu)點:封裝了邏輯,更易于使用。
【AbstractResourceLoadingContext】 //?這里的mapping保存了所有的loading?list resource?=?loadMappedResource(mapping,?options);3、重定向資源
優(yōu)點:方便我們拓展資源的定位,比如可以加載特定目錄下的cms模板。
這就是一個pattern的優(yōu)先級的問題,沒什么好說的,舉個例子:
<resource-loading=> ... <resource-alias?pattern="/templates"?name="/webroot/templates"?/> <resource?pattern="/templates/cms"> <res-loaders:file-loader?basedir="${cms_root}"?/> </resource> <resource?pattern="/webroot"?internal="true"> <res-loaders:webapp-loader?/> </resource> ... </resource-loading>}4、內容修改,filter的概念
優(yōu)點:方便我們拓展資源查找的邏輯,比如轉換資源文件的格式。
【ResourceLoaderContextImpl】 public?Resource?getResource()?throws?ResourceNotFoundException?{if?(filtersMatcher.matches(resourceName))?{ResourceFilterMapping?filterMapping?=?filtersMatcher.bestMatchPettern;lastMatchedPattern?=?filterMapping;lastSubstitution?=?new?MatchResultSubstitution(filtersMatcher.bestMatchResult);log.debug("Resource?\"{}\"?matched?resource-filters?pattern:?\"{}\"",?resourceName,filterMapping.getPatternName());ResourceFilterChain?root?=?this;?//?as?ResourceFilterChainResourceMatchResult?matchResult?=?this;?//?as?ResourceMatchResultResourceFilterChain?chain?=?filterMapping.getResourceFilterChain(root);return?chain.doFilter(matchResult,?options);?//?with?filters}?else?{return?doLoad(resourceName,?options);?//?no?filters}5、定義新的資源,新的pattern格式
優(yōu)點:方便我們分類管理各個類型的資源,比如:/a,/b可以分開管理加載邏輯。
總結
ClassLoader加載資源具有資源多樣化,加載方式多樣化的特點;Spring針對這一問題提出了策略模式加載資源的解決方法;但是對于大的工程,資源不進行管理會顯得雜亂無章,因此SpringExt主要解決了資源管理的問題。
轉載于:https://my.oschina.net/tryUcatchUfinallyU/blog/266783
總結
以上是生活随笔為你收集整理的ResourceLoader的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 我要把智商减100
- 下一篇: java中的数据加密1 消息摘要