Spring源码之Resource加载源码解析(二)
来源:互联网 发布:360软件管家字体模糊 编辑:程序博客网 时间:2024/05/17 04:38
Resource接口
//简单的接口对象:使用流InputStreamreSource public interface InputStreamSource { InputStream getInputStream() throws IOException;}//接口对象:用于从实际资源类型抽象出来的资源描述符的接口,如文件或类路径资源。public interface Resource extends InputStreamSource { //是否存在 boolean exists(); //是否可读 boolean isReadable(); //返回此资源是否表示具有开放流的句柄。如果是真的,无法读取InputStream的多次,并必须予以封闭,以防止资源泄漏。 boolean isOpen(); //返回此资源的URL句柄。 URL getURL() throws IOException; //返回此资源的URI句柄。 URI getURI() throws IOException; //返回此资源的文件句柄。 File getFile() throws IOException; //返回此资源的内容长度。 long contentLength() throws IOException; //返回此资源的最后修改时间戳。 long lastModified() throws IOException; //创建与此资源相对应的资源,并返回对应资源的资源句柄。relativepath相对路径(相对于这个资源) Resource createRelative(String relativePath) throws IOException; //确定此资源的文件名,通常是最后部分,如:test.txt String getFilename(); //返回此资源的描述,用于在处理资源时进行错误输出。 String getDescription();}
基础实现抽象类AbstractResource
//Resource的基础实现抽象类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 { throw new FileNotFoundException(getDescription() + " cannot be resolved to URL"); } //默认实现:先获取URL,再转URI public URI getURI() throws IOException { URL url = getURL(); try { return ResourceUtils.toURI(url); } catch (URISyntaxException ex) { throw new NestedIOException("Invalid URI [" + url + "]", ex); } } //默认实现报错,需子类自己实现 public File getFile() throws IOException { 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 { 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; } //返回File对象,调用getFile() protected File getFileForLastModifiedCheck() throws IOException { return getFile(); } //默认实现报错,需子类自己实现 public Resource createRelative(String relativePath) throws IOException { throw new FileNotFoundException("Cannot create a relative resource for " + getDescription()); } //默认返回空,需子类自己实现 public String getFilename() { return null; } @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(); }}
文件的抽象操作AbstractFileResolvingResource
//抽象File资源操作抽象类public abstract class AbstractFileResolvingResource extends AbstractResource { //获取文件对象 @Override public File getFile() throws IOException { URL url = getURL(); //url协议为"vfs",交给VfsResourceDelegate处理 if (url.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) { return VfsResourceDelegate.getResource(url).getFile(); } //其他则由ResourceUtils处理 return ResourceUtils.getFile(url, getDescription()); } /** * This implementation determines the underlying File * (or jar file, in case of a resource in a jar/zip). */ //这个实现决定了底层文件(或jar文件,如果是JAR / zip中的资源) @Override protected File getFileForLastModifiedCheck() throws IOException { //获取URL URL url = getURL(); //是否为jar if (ResourceUtils.isJarURL(url)) { //提取jar的URL URL actualUrl = ResourceUtils.extractJarFileURL(url); if (actualUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) { //url协议为"vfs",交给VfsResourceDelegate处理 return VfsResourceDelegate.getResource(actualUrl).getFile(); } //由ResourceUtils处理,获取File对象 return ResourceUtils.getFile(actualUrl, "Jar URL"); } else { return getFile(); } } //通过给定uri,获取File对象 protected File getFile(URI uri) throws IOException { //url协议为"vfs",交给VfsResourceDelegate处理 if (uri.getScheme().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) { return VfsResourceDelegate.getResource(uri).getFile(); } //否则由ResourceUtils处理,获取File对象 return ResourceUtils.getFile(uri, getDescription()); } //是否存在 @Override public boolean exists() { try { //或如URL URL url = getURL(); //1.文件路径,由文件系统解析 if (ResourceUtils.isFileURL(url)) { // Proceed with file system resolution... return getFile().exists(); } else { //2.否则,采用尝试获取URL链接请求中header的content-length属性 // Try a URL connection content-length header... URLConnection con = url.openConnection(); customizeConnection(con); HttpURLConnection httpCon = (con instanceof HttpURLConnection ? (HttpURLConnection) con : null); //2.1 httpCon对象非空,尝试获取链接,并获取返回状体码 if (httpCon != null) { //获取状态码 int code = httpCon.getResponseCode(); if (code == HttpURLConnection.HTTP_OK) { //链接陈功 return true; } else if (code == HttpURLConnection.HTTP_NOT_FOUND) { //未找到 return false; } } //2.2 链接失败,获取返content-length的值是否大于等于0 if (con.getContentLength() >= 0) { return true; } //2.3 httpCon对象非空,但HTTP状态码不是OK/NOT_FOUND状态 if (httpCon != null) { //没有HTTP OK状态,也没有获取到content-length header,则放弃链接,返回false httpCon.disconnect(); return false; } else { //2.4 尝试获取文件流,成功,则存在 InputStream is = getInputStream(); is.close(); return true; } } } catch (IOException ex) { return false; } } //是否可读 @Override public boolean isReadable() { try { URL url = getURL(); //1.文件路径,由文件系统解析 if (ResourceUtils.isFileURL(url)) { // Proceed with file system resolution... File file = getFile(); return (file.canRead() && !file.isDirectory()); } else { //2.否则返回true return true; } } catch (IOException ex) { return false; } } @Override public long contentLength() throws IOException { URL url = getURL(); //1.文件路径,由文件系统解析 if (ResourceUtils.isFileURL(url)) { // Proceed with file system resolution... return getFile().length(); } else { //2.否则,采用尝试获取URL链接请求中header的content-length属性 // Try a URL connection content-length header... URLConnection con = url.openConnection(); customizeConnection(con); return con.getContentLength(); } } @Override public long lastModified() throws IOException { URL url = getURL(); //1.文件或jar,调用父类方法进行处理 if (ResourceUtils.isFileURL(url) || ResourceUtils.isJarURL(url)) { // Proceed with file system resolution... return super.lastModified(); } else { //2.否则,采用尝试获取URL链接请求中header的last-modified属性 // Try a URL connection last-modified header... URLConnection con = url.openConnection(); customizeConnection(con); return con.getLastModified(); } } //自定义连接 protected void customizeConnection(URLConnection con) throws IOException { ResourceUtils.useCachesIfNecessary(con); if (con instanceof HttpURLConnection) { customizeConnection((HttpURLConnection) con); } } //自定义连接 protected void customizeConnection(HttpURLConnection con) throws IOException { //设置method为HEAD,则表明只返回请求头部 con.setRequestMethod("HEAD"); } //内部委托类,在运行时避免硬JBoss VFS API的依赖。 private static class VfsResourceDelegate { //委托给VfsResource处理 public static Resource getResource(URL url) throws IOException { return new VfsResource(VfsUtils.getRoot(url)); } public static Resource getResource(URI uri) throws IOException { return new VfsResource(VfsUtils.getRoot(uri)); } }}
ClassPathResource源码阅读
//对classpath下的xml文件进行解析,默认使用ClassLoader来加载资源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; 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; } //构造方法 protected ClassPathResource(String path, ClassLoader classLoader, Class<?> clazz) { this.path = StringUtils.cleanPath(path); this.classLoader = classLoader; this.clazz = clazz; } public final String getPath() { return this.path; } //获取类加载器 public final ClassLoader getClassLoader() { //clazz不为空,获取clazz的类加载,否则返回classLoader return (this.clazz != null ? this.clazz.getClassLoader() : this.classLoader); } @Override public boolean exists() { return (resolveURL() != null); } //解析底层类路径资源的URL。 protected URL resolveURL() { //1. path不以'/'开头时,默认是从此类所在的包下取资源;path以'/'开头时,则是从项目的ClassPath根下获取资源,获取path的资源路径 if (this.clazz != null) { return this.clazz.getResource(this.path); } //2.path不能以'/'开头时,path是指类加载器的加载范围(在资源加载的过程中,使用的逐级向上委托的形式加载的),'/'表示Boot ClassLoader中的加载范围,获取path的资源路径 else if (this.classLoader != null) { return this.classLoader.getResource(this.path); } //3.加载当前类的类加器,获取path的资源路径 else { return ClassLoader.getSystemResource(this.path); } } //获取文件流 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; } //此实现返回底层类路径资源的URL,如果可用的话。 @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; } //给定相对于该描述符的底层资源的路径,创建一个ClassPathResource对象。 @Override public Resource createRelative(String relativePath) { String pathToUse = StringUtils.applyRelativePath(this.path, relativePath); return new ClassPathResource(pathToUse, this.classLoader, this.clazz); } //获取文件名称 @Override public String getFilename() { return StringUtils.getFilename(this.path); } //获取文件描述 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(); } /** * This implementation compares the underlying class path locations. */ @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; } /** * This implementation returns the hash code of the underlying * class path location. */ @Override public int hashCode() { return this.path.hashCode(); }}
阅读全文
0 0
- Spring源码之Resource加载源码解析(二)
- Spring源码之Resource加载源码解析(一)
- Spring源码之Resource加载源码解析(三)
- spring源码解析-资源管理Resource
- spring源码解析(二)
- Spring源码解析之Bean的加载
- Spring 源码解析之HandlerAdapter源码解析(二)
- Spring 源码解析之HandlerAdapter源码解析(二)
- Spring 源码解析之HandlerAdapter源码解析(二)
- Spring源码解析之-资源加载(1)
- spring源码阅读之Bean的加载(二)
- spring IoC源码分析 (3)Resource解析
- Spring Bean加载源码解析
- spring ioc 源码解析(二)
- spring源码解析(二)IOC容器
- Spring源码解析之-Aop源码解析(2)
- 深入Spring IOC源码之Resource
- Spring源码阅读之Resource接口
- 2017金马五校赛 K.购买装备(二分)
- SurfaceView攻略~~~从零开始实现打雷下雨天气特效~①
- Lua与C++交互:函数
- 拥抱黑暗,心向光明
- NP完全问题的证明-算法概论课后习题8.15
- Spring源码之Resource加载源码解析(二)
- 细说ThreadLocal
- 并查集详解
- nodejs response
- 继承和原型链
- 人生的智慧
- python_文件操作
- 模版类型推导
- jdk环境变量配置