成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

怎么理解Spring中的Resource與ResourceLoader體系

這篇文章主要介紹“怎么理解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體系

怎么理解Spring中的Resource與ResourceLoader體系
這些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方法。

1.1 WriteableResource接口

對(duì)于大多數(shù)資源文件來說,不一定可寫但一般是可讀的。因此這里專門為可寫的資源類型定義了WritableResource接口,此接口中定義了兩個(gè)和寫操作相關(guān)的方法:

	boolean isWritable();
	OutputStream getOutputStream() throws IOException;

1.2 AbstractResource類

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ì)這種類型的操作,一般只支持讀取操作。

1.2.1 FileSystemResource類

從上面的繼承關(guān)系圖中可以看到,F(xiàn)ileSystemResource不但繼承了AbstractResource還實(shí)現(xiàn)了WritableResource接口,也就是基于文件系統(tǒng)的資源類型,一般可以支持讀寫操作,當(dāng)然一般也會(huì)支持相對(duì)路徑資源。

1.2.2 AbstractFileResolvingResource類

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中非常常用的資源類型。

(1)UrlResource

UrlResource來說,基本上就是通過解析URL來完成相關(guān)的操作,只要符合URL規(guī)范的格式都可以表示為UrlResource對(duì)象。

(2)ClassPathResouce
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/)

1.3 總結(jié)

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í)行邏輯。

二 ResourceLoader體系

怎么理解Spring中的Resource與ResourceLoader體系
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ù)的來源信息。

2.1 DefaultResourceLoader類

默認(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提供的方法自行判斷。

2.2 ResourcePatternResolver

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)

網(wǎng)站托管運(yùn)營(yíng)
南岸区| 泽州县| 丰宁| 黄龙县| 清流县| 鱼台县| 射洪县| 康乐县| 通化县| 饶阳县| 称多县| 乐都县| 咸阳市| 章丘市| 莱阳市| 扎兰屯市| 朝阳县| 崇礼县| 贵德县| 新建县| 繁峙县| 南漳县| 宁河县| 宣威市| 太谷县| 昌乐县| 平江县| 阿瓦提县| 灵川县| 永平县| 哈密市| 建阳市| 永宁县| 永靖县| 周至县| 高邑县| 维西| 塘沽区| 手游| 岚皋县| 武宣县|