這篇文章主要介紹“怎么理解Spring中的Resource與ResourceLoader體系”,在日常操作中,相信很多人在怎么理解Spring中的Resource與ResourceLoader體系問題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”怎么理解Spring中的Resource與ResourceLoader體系”的疑惑有所幫助!接下來,請(qǐng)跟著小編一起來學(xué)習(xí)吧!
“專業(yè)、務(wù)實(shí)、高效、創(chuàng)新、把客戶的事當(dāng)成自己的事”是我們每一個(gè)人一直以來堅(jiān)持追求的企業(yè)文化。 創(chuàng)新互聯(lián)是您可以信賴的網(wǎng)站建設(shè)服務(wù)商、專業(yè)的互聯(lián)網(wǎng)服務(wù)提供商! 專注于成都網(wǎng)站設(shè)計(jì)、成都做網(wǎng)站、軟件開發(fā)、設(shè)計(jì)服務(wù)業(yè)務(wù)。我們始終堅(jiān)持以客戶需求為導(dǎo)向,結(jié)合用戶體驗(yàn)與視覺傳達(dá),提供有針對(duì)性的項(xiàng)目解決方案,提供專業(yè)性的建議,創(chuàng)新互聯(lián)建站將不斷地超越自我,追逐市場(chǎng),引領(lǐng)市場(chǎng)!
這些Resources主要就分為2大類:只讀,與可讀可寫。
從上面類圖我們可以看出FileSystemResource,實(shí)現(xiàn)了WritableResource,因此僅有這個(gè)類屬于可讀可寫,而其它的均屬于只讀的Resource.
Resources接口
public interface Resource extends InputStreamSource { // 判斷資源是否存在 boolean exists(); // 判斷資源是否可讀,只有在返回true的時(shí)候,getInputStream方法才可用 boolean isReadable(); // 判斷資源是否已打開,如果已打開則資源不能多次讀寫,資源應(yīng)該在讀完成之后關(guān)閉。 boolean isOpen(); // 獲取資源對(duì)象的URL,如果該資源不能表示為URL形式則拋出異常 URL getURL() throws IOException; // 獲取資源對(duì)象的URI,如果該資源不能表示為URI形式則拋出異常 URI getURI() throws IOException; // 獲取資源的File表示對(duì)象,如果資源不能表示為File對(duì)象則拋出異常 File getFile() throws IOException; // 獲取資源內(nèi)容的長(zhǎng)度,如果資源無法解析則拋出異常 long contentLength() throws IOException; // 獲取資源最后修改時(shí)間戳,如果資源無法解析則拋出異常 long lastModified() throws IOException; // 相對(duì)當(dāng)前資源創(chuàng)建新的資源對(duì)象,如果相對(duì)的資源無法解析則拋出異常 Resource createRelative(String relativePath) throws IOException; // 獲取當(dāng)前資源的文件名,如果當(dāng)前資源沒有文件名則返回null String getFilename(); // 獲取當(dāng)對(duì)資源的描述信息 String getDescription(); }
在Resource接口中定義的方法,并不需要每一種實(shí)際資源類型都必須實(shí)現(xiàn),各個(gè)實(shí)際資源類型根據(jù)自身的情況決定要實(shí)現(xiàn)哪些方法。例如基于文件的資源一般會(huì)實(shí)現(xiàn)getFile方法,而不是基于文件的資源則一般不實(shí)現(xiàn)getFile方法。
對(duì)于大多數(shù)資源文件來說,不一定可寫但一般是可讀的。因此這里專門為可寫的資源類型定義了WritableResource接口,此接口中定義了兩個(gè)和寫操作相關(guān)的方法:
boolean isWritable(); OutputStream getOutputStream() throws IOException;
public abstract class AbstractResource implements Resource { public boolean exists() { // Try file existence: can we find the file in the file system? try { return getFile().exists(); } catch (IOException ex) { // Fall back to stream existence: can we open the stream? try { InputStream is = getInputStream(); is.close(); return true; } catch (Throwable isEx) { return false; } } } public boolean isReadable() { return true; } public boolean isOpen() { return false; } public URL getURL() throws IOException { // 默認(rèn)認(rèn)為資源無法表示為URL,子類可覆寫此方法 throw new FileNotFoundException(getDescription() + " cannot be resolved to URL"); } public URI getURI() throws IOException { URL url = getURL(); try { // 通過getURL方法的返回值來進(jìn)行轉(zhuǎn)換 return ResourceUtils.toURI(url); } catch (URISyntaxException ex) { throw new NestedIOException("Invalid URI [" + url + "]", ex); } } public File getFile() throws IOException { // 默認(rèn)認(rèn)為資源無法表示為File對(duì)象,子類可覆寫 throw new FileNotFoundException(getDescription() + " cannot be resolved to absolute file path"); } public long contentLength() throws IOException { InputStream is = this.getInputStream(); Assert.state(is != null, "resource input stream must not be null"); try { // 默認(rèn)實(shí)現(xiàn)為讀取inputStream中的所有數(shù)據(jù)來獲取長(zhǎng)度 long size = 0; byte[] buf = new byte[255]; int read; while((read = is.read(buf)) != -1) { size += read; } return size; } finally { try { is.close(); } catch (IOException ex) { } } } public long lastModified() throws IOException { long lastModified = getFileForLastModifiedCheck().lastModified(); if (lastModified == 0L) { throw new FileNotFoundException(getDescription() + " cannot be resolved in the file system for resolving its last-modified timestamp"); } return lastModified; } protected File getFileForLastModifiedCheck() throws IOException { return getFile(); } public Resource createRelative(String relativePath) throws IOException { // 默認(rèn)不支持創(chuàng)建相對(duì)路徑資源 throw new FileNotFoundException("Cannot create a relative resource for " + getDescription()); } public String getFilename() { return null; // 默認(rèn)返回null,即認(rèn)為資源五文件名 } @Override public String toString() { return getDescription(); } @Override public boolean equals(Object obj) { return (obj == this || (obj instanceof Resource && ((Resource) obj).getDescription().equals(getDescription()))); } @Override public int hashCode() { return getDescription().hashCode(); } }
在AbstractResource的實(shí)現(xiàn)中,默認(rèn)認(rèn)為資源不能夠表示為URL和File的形式,這樣的資源如ByteArrayResource、InputStreamResource等都可以適應(yīng),因?yàn)檫@些資源類型底層并不是基于文件而是包裝了字節(jié)數(shù)組或輸入流而成,因此正對(duì)這種類型的操作,一般只支持讀取操作。
從上面的繼承關(guān)系圖中可以看到,F(xiàn)ileSystemResource不但繼承了AbstractResource還實(shí)現(xiàn)了WritableResource接口,也就是基于文件系統(tǒng)的資源類型,一般可以支持讀寫操作,當(dāng)然一般也會(huì)支持相對(duì)路徑資源。
abstractFileResolvingResource表示需要通過解析URL來獲取的資源。例如其exists方法的實(shí)現(xiàn):
@Override public boolean exists() { try { URL url = getURL(); if (ResourceUtils.isFileURL(url)) { // Proceed with file system resolution return getFile().exists();// 如果是文件,則直接檢測(cè)文件是否存在 } else { // Try a URL connection content-length header URLConnection con = url.openConnection(); customizeConnection(con); HttpURLConnection httpCon = (con instanceof HttpURLConnection ? (HttpURLConnection) con : null); if (httpCon != null) { // 如果是http url則檢測(cè)url對(duì)應(yīng)的資源是否存在 int code = httpCon.getResponseCode(); if (code == HttpURLConnection.HTTP_OK) { return true; } else if (code == HttpURLConnection.HTTP_NOT_FOUND) { return false; } } if (con.getContentLength() >= 0) { return true; } if (httpCon != null) { // No HTTP OK status, and no content-length header: give up httpCon.disconnect(); return false; } else { // Fall back to stream existence: can we open the stream? getInputStream().close(); return true; } } } catch (IOException ex) { return false; } }
并且他還有兩個(gè)子類:UrlResource和ClassPathResouce,其中ClassPathResource類型是我們?cè)赟pring中非常常用的資源類型。
UrlResource來說,基本上就是通過解析URL來完成相關(guān)的操作,只要符合URL規(guī)范的格式都可以表示為UrlResource對(duì)象。
public class ClassPathResource extends AbstractFileResolvingResource { private final String path; private ClassLoader classLoader; private Class<?> clazz; public ClassPathResource(String path) { this(path, (ClassLoader) null); } public ClassPathResource(String path, ClassLoader classLoader) { Assert.notNull(path, "Path must not be null"); String pathToUse = StringUtils.cleanPath(path); if (pathToUse.startsWith("/")) { pathToUse = pathToUse.substring(1); } this.path = pathToUse; // 如果ClassLoader為null則使用默認(rèn)的ClassLoader this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader()); } public ClassPathResource(String path, Class<?> clazz) { Assert.notNull(path, "Path must not be null"); this.path = StringUtils.cleanPath(path); this.clazz = clazz;// 使用Class來加載資源,也可以使用ClassLoader加載 } protected ClassPathResource(String path, ClassLoader classLoader, Class<?> clazz) { this.path = StringUtils.cleanPath(path); // 同時(shí)使用Clss和ClassLoader this.classLoader = classLoader; this.clazz = clazz; } public final String getPath() { return this.path; } public final ClassLoader getClassLoader() { return (this.clazz != null ? this.clazz.getClassLoader() : this.classLoader); } @Override public boolean exists() { return (resolveURL() != null); } protected URL resolveURL() { // 資源是否存在通過Class和ClassLoader來判斷 if (this.clazz != null) { return this.clazz.getResource(this.path); } else if (this.classLoader != null) { return this.classLoader.getResource(this.path); } else { return ClassLoader.getSystemResource(this.path); } @Override public InputStream getInputStream() throws IOException { InputStream is; // InputStream的獲取也是通過Class和ClassLoader來判斷 if (this.clazz != null) { is = this.clazz.getResourceAsStream(this.path); } else if (this.classLoader != null) { is = this.classLoader.getResourceAsStream(this.path); } else { is = ClassLoader.getSystemResourceAsStream(this.path); } if (is == null) { throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist"); } return is; } @Override public URL getURL() throws IOException { URL url = resolveURL(); if (url == null) { throw new FileNotFoundException(getDescription() + " cannot be resolved to URL because it does not exist"); } return url; } @Override public Resource createRelative(String relativePath) { String pathToUse = StringUtils.applyRelativePath(this.path, relativePath); return (this.clazz != null ? new ClassPathResource(pathToUse, this.clazz) : new ClassPathResource(pathToUse, this.classLoader)); } @Override public String getFilename() { return StringUtils.getFilename(this.path); } @Override public String getDescription() { StringBuilder builder = new StringBuilder("class path resource ["); String pathToUse = path; if (this.clazz != null && !pathToUse.startsWith("/")) { builder.append(ClassUtils.classPackageAsResourcePath(this.clazz)); builder.append('/'); } if (pathToUse.startsWith("/")) { pathToUse = pathToUse.substring(1); } builder.append(pathToUse); builder.append(']'); return builder.toString(); } @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj instanceof ClassPathResource) { ClassPathResource otherRes = (ClassPathResource) obj; return (this.path.equals(otherRes.path) && ObjectUtils.nullSafeEquals(this.classLoader, otherRes.classLoader) && ObjectUtils.nullSafeEquals(this.clazz, otherRes.clazz)); } return false; } @Override public int hashCode() { return this.path.hashCode(); } }
雖然ClassPathContextResource和ClassPathRelaticeContextResource都是繼承自ClassPathResource,但是前者使用的ClassLoader來加載資源,后者使用的是Class來加載資源,二者還是有區(qū)別的:
ClassLoader來加載資源,路徑以類路徑的根目錄為基準(zhǔn)(如WEB-INF/classes為基準(zhǔn))。
Class來架子資源,資源以Class縮在的包路徑為基準(zhǔn)(如Web-INF/classes/com/test/resource/)
Spring中對(duì)資源進(jìn)行抽象,從而統(tǒng)一對(duì)資源操作的API,屏蔽不同資源之間的差異。使得其他組件可以不關(guān)心具體資源類型的實(shí)現(xiàn),使用統(tǒng)一的API進(jìn)行操作,并且通過不同的接口來分別定義資源的不同行為,然后通過抽象類的形式給出一個(gè)通用的實(shí)現(xiàn),底層具體的實(shí)現(xiàn)只需要繼承這個(gè)抽象類,并覆寫跟當(dāng)前資源類型需要特殊處理的方法即可。另外,在定義接口時(shí),通過給出一對(duì)方法(如:isReadable和getInputStream)來分離條件檢測(cè)和執(zhí)行邏輯。
Spring中的Resource體系,只是對(duì)資源類型進(jìn)行了抽象,統(tǒng)一了接口,但是如果需要使用不同類型的Resource的時(shí)候,還是得通過new具體資源類型的方式來獲取。Spring中為了簡(jiǎn)化對(duì)Resource的查找和加載,提供了ResourceLoader來專門負(fù)責(zé)加載Resource,使用這不需要關(guān)心如何加載具體的資源,只需要給出資源的定義(schame),剩下的就交由ResourceLoader來處理了。
ResourceLoader接口:
public interface ResourceLoader { // ClassPathResource對(duì)應(yīng)的Url的協(xié)議頭(前綴) String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX; // 根據(jù)給出的資源Url獲取對(duì)應(yīng)的Resource對(duì)象 Resource getResource(String location); // 獲取當(dāng)前ResourceLoader所使用的ClassLoader ClassLoader getClassLoader(); }
從接口定義中看ResourceLoader最主要的行為就是getResource操作。getResource方法接收一個(gè)字符串類型參數(shù),通過對(duì)字符串參數(shù)的解析和處理返回對(duì)應(yīng)的Resource對(duì)象,這里的location參數(shù)可以包含一定的協(xié)議頭或前綴來表明參數(shù)的來源信息。
默認(rèn)的實(shí)現(xiàn)DefaultResourceLoader,它可以使用ClassLoader作為參數(shù)進(jìn)行創(chuàng)建。其getResource方法實(shí)現(xiàn)如下:
public Resource getResource(String location) { Assert.notNull(location, "Location must not be null"); if (location.startsWith(CLASSPATH_URL_PREFIX)) { // classpath:前綴 // ClassPathResource需要使用Class或ClassLoader,這里傳入ClassLoader 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); // 如果不符合URL規(guī)范,則當(dāng)做普通路徑(如:test/resource.xml)處理 } } }
對(duì)于傳入的location參數(shù),顯示判斷了是否包含classpath:前綴,如果包含則返回ClassPathResource對(duì)象,如果不是則通過解析URL的方式解析location參數(shù),如果參數(shù)符合URL規(guī)范,則創(chuàng)建一個(gè)UrlResource,如果資源既不包含classpath:特殊前綴,也不是URL形式,那么就將其當(dāng)做普通的資源路徑傳遞給getResourceByPath進(jìn)行處理:
protected Resource getResourceByPath(String path) { return new ClassPathContextResource(path, getClassLoader()); } private static class ClassPathContextResource extends ClassPathResource implements ContextResource { public ClassPathContextResource(String path, ClassLoader classLoader) { super(path, classLoader); } public String getPathWithinContext() { return getPath(); } @Override public Resource createRelative(String relativePath) { String pathToUse = StringUtils.applyRelativePath(getPath(), relativePath); return new ClassPathContextResource(pathToUse, getClassLoader()); } }
getResourceByPath返回ClassPathContextResource類型,實(shí)際上也可以也就是ClassPathResource??梢钥闯?,ResourceLoader中并不判斷資源是否真實(shí)存在和是否可讀寫,而僅僅通過解析出傳入的location參數(shù)返回不同的Resource實(shí)現(xiàn)而已,資源是否存在,是否可讀,需要調(diào)用方在拿到Resource對(duì)象后通過Resource提供的方法自行判斷。
Spring除了提供ResourceLoader之外,還提供了ResourcePatternResolver來擴(kuò)充ResourceLoader。引進(jìn)了一個(gè)新的前綴:classpath*:。和classpath:的差別就是,classpath*:可以搜索class path下所有滿足條件的資源(包括同名的資源),而classpath:則只能返回一個(gè)資源(即使存在多個(gè))。
到此,關(guān)于“怎么理解Spring中的Resource與ResourceLoader體系”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!
當(dāng)前名稱:怎么理解Spring中的Resource與ResourceLoader體系
文章路徑:http://jinyejixie.com/article12/gdphgc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供營(yíng)銷型網(wǎng)站建設(shè)、響應(yīng)式網(wǎng)站、云服務(wù)器、外貿(mào)建站、標(biāo)簽優(yōu)化、靜態(tài)網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)