1:资源访问
在Sun所提供的标准API里,资源访问通常由Java.net.URL和文件IO来完成,如果需要访问来自网络的资源时,则通常会选择URL类。
Url类可以处理一些常规的资源访问问题,但是依然不能很好地满足所有底层资源访问的需要,比如,暂时还无法在类加载路径或相对于ServletContext的路径中访问资源,也无法检查所指向的资源是否存在。
Spring对其内部使用到的资源实现了自己的抽象结构:Resource接口来封装底层资源。
InputStreamSource封装任何能返回InputStream的类,比如File,Classpath下的资源和Byte Array等。它只有一个方法定义:getInputStream(),该方法返回一个新的InputStream对象。
Resource接口本身没有提供访问任何底层资源的实现逻辑,针对不同的底层资源,Spring将会提供不同的Resource实现类,不同的实现类负责不同的资源访问逻辑。
Spring的Resource设计是一种典型的策略模式,通过使用Resource接口,客户端程序可以在不同的资源访问策略之间自由切换。
package org.springframework.core.io; import java.io.IOException; import java.io.InputStream; public interface InputStreamSource { InputStream getInputStream() throws IOException; }
package org.springframework.core.io; import java.io.File; import java.io.IOException; import java.net.URI; import java.net.URL; public interface Resource extends InputStreamSource {
//存在性 boolean exists();
//可读性 boolean isReadable();
//是否处于打开状态 boolean isOpen(); URL getURL() throws IOException; URI getURI() throws IOException; File getFile() throws IOException; long contentLength() throws IOException; long lastModified() throws IOException;
//基于当前资源创建相对资源 Resource createRelative(String relativePath) throws IOException;
//不带路径信息的文件名 String getFilename();
//用于在错误处理中打印信息 String getDescription(); }
2:Resource实现类
UrlResource:访问网络资源的实现类
ClassPathResource:访问类加载路径里资源的实现类
FileSystemResource:访问文件系统里资源的实现类
ServletContextResource:访问相对于ServletContext路径下的资源的实现类
InputStreamResource:访问输入流资源的实现类
ByteArrayResouce:访问字节数组资源的实现类
1)UrlResource
file:用于访问文件系统, http:用于通过HTTP协议访问资源, ftp:用于通过FTP协议访问资源
package org.springframework.core.io; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.URLConnection; import org.springframework.util.Assert; import org.springframework.util.ResourceUtils; import org.springframework.util.StringUtils; public class UrlResource extends AbstractFileResolvingResource { private final URI uri; private final URL url; private final URL cleanedUrl; public UrlResource(URI uri) throws MalformedURLException { Assert.notNull(uri, "URI must not be null"); this.uri = uri; this.url = uri.toURL(); this.cleanedUrl = getCleanedUrl(this.url, uri.toString()); } public UrlResource(URL url) { Assert.notNull(url, "URL must not be null"); this.url = url; this.cleanedUrl = getCleanedUrl(this.url, url.toString()); this.uri = null; } public UrlResource(String path) throws MalformedURLException { Assert.notNull(path, "Path must not be null"); this.uri = null; this.url = new URL(path); this.cleanedUrl = getCleanedUrl(this.url, path); } public UrlResource(String protocol, String location) throws MalformedURLException { this(protocol, location, null); } public UrlResource(String protocol, String location, String fragment) throws MalformedURLException { try { this.uri = new URI(protocol, location, fragment); this.url = this.uri.toURL(); this.cleanedUrl = getCleanedUrl(this.url, this.uri.toString()); } catch (URISyntaxException ex) { MalformedURLException exToThrow = new MalformedURLException(ex.getMessage()); exToThrow.initCause(ex); throw exToThrow; } } private URL getCleanedUrl(URL originalUrl, String originalPath) { try { return new URL(StringUtils.cleanPath(originalPath)); } catch (MalformedURLException ex) { // Cleaned URL path cannot be converted to URL // -> take original URL. return originalUrl; } } @Override public InputStream getInputStream() throws IOException { URLConnection con = this.url.openConnection(); ResourceUtils.useCachesIfNecessary(con); try { return con.getInputStream(); } catch (IOException ex) { // Close the HTTP connection (if applicable). if (con instanceof HttpURLConnection) { ((HttpURLConnection) con).disconnect(); } throw ex; } } @Override public URL getURL() throws IOException { return this.url; } @Override public URI getURI() throws IOException { if (this.uri != null) { return this.uri; } else { return super.getURI(); } } @Override public File getFile() throws IOException { if (this.uri != null) { return super.getFile(this.uri); } else { return super.getFile(); } } @Override public Resource createRelative(String relativePath) throws MalformedURLException { if (relativePath.startsWith("/")) { relativePath = relativePath.substring(1); } return new UrlResource(new URL(this.url, relativePath)); } @Override public String getFilename() { return new File(this.url.getFile()).getName(); } @Override public String getDescription() { return "URL [" + this.url + "]"; } @Override public boolean equals(Object obj) { return (obj == this || (obj instanceof UrlResource && this.cleanedUrl.equals(((UrlResource) obj).cleanedUrl))); } @Override public int hashCode() { return this.cleanedUrl.hashCode(); } }
2)ClassPathResource
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; this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader()); } public final ClassLoader getClassLoader() { return (this.clazz != null ? this.clazz.getClassLoader() : this.classLoader); } @Override public InputStream getInputStream() throws IOException { InputStream is; 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; }
3)FileSystemResource
public FileSystemResource(String path) { Assert.notNull(path, "Path must not be null"); this.file = new File(path); this.path = StringUtils.cleanPath(path); } @Override public InputStream getInputStream() throws IOException { return new FileInputStream(this.file); }
4)ByteArrayResource
public ByteArrayResource(byte[] byteArray) { this(byteArray, "resource loaded from byte array"); } @Override public InputStream getInputStream() throws IOException { return new ByteArrayInputStream(this.byteArray); }
分析:
通过Resource,我们需要获取输入流InupStream,FileSystemResource和ByteArrayResource的getInputStream()方法更好理解。
以UrlResource举例:
通常会传进来一个String,比如说file:D://zhao.txt
public UrlResource(String path) throws MalformedURLException {
Assert.notNull(path, "Path must not be null");
this.uri = null;
this.url = new URL(path);
this.cleanedUrl = getCleanedUrl(this.url, path);
}
我们需要用到getCleanUrl(this.url,path)
private URL getCleanedUrl(URL originalUrl, String originalPath) {
try {
return new URL(StringUtils.cleanPath(originalPath));
}
catch (MalformedURLException ex) {
return originalUrl;
}
}
我们的目的是把传入的String拆开,得到 : 前的内容,告诉我们用什么协议,得到 : 后的内容,告诉我们位置在哪
public static String cleanPath(String path) { if (path == null) { return null; } String pathToUse = replace(path, WINDOWS_FOLDER_SEPARATOR, FOLDER_SEPARATOR); // Strip prefix from path to analyze, to not treat it as part of the // first path element. This is necessary to correctly parse paths like // "file:core/../core/io/Resource.class", where the ".." should just // strip the first "core" directory while keeping the "file:" prefix. int prefixIndex = pathToUse.indexOf(":"); String prefix = ""; if (prefixIndex != -1) { prefix = pathToUse.substring(0, prefixIndex + 1); if (prefix.contains("/")) { prefix = ""; } else { pathToUse = pathToUse.substring(prefixIndex + 1); } } if (pathToUse.startsWith(FOLDER_SEPARATOR)) { prefix = prefix + FOLDER_SEPARATOR; pathToUse = pathToUse.substring(1); } String[] pathArray = delimitedListToStringArray(pathToUse, FOLDER_SEPARATOR); List<String> pathElements = new LinkedList<String>(); int tops = 0; for (int i = pathArray.length - 1; i >= 0; i--) { String element = pathArray[i]; if (CURRENT_PATH.equals(element)) { // Points to current directory - drop it. } else if (TOP_PATH.equals(element)) { // Registering top path found. tops++; } else { if (tops > 0) { // Merging path element with element corresponding to top path. tops--; } else { // Normal path element found. pathElements.add(0, element); } } } // Remaining top paths need to be retained. for (int i = 0; i < tops; i++) { pathElements.add(0, TOP_PATH); } return prefix + collectionToDelimitedString(pathElements, FOLDER_SEPARATOR); }