从今天开始,一步步走上源码分析的路。刚开始肯定要从简单着手。我们先从Java发展史上最强大的框架——Spring、、、旗下的资源抽象接口Resource开始吧。

  我看了好多分析Spring源码的,每每一开始就是Spring IOC、AOP、BeanFactory这样的Spring典型模块,实在看厌了,这些暂且留到以后。我的想法是,分析就分析别人没分析过的,或者以不同的角度来分析别人分析过的。

  可能很多用了Spring多年的程序员对Resource都了解有限,毕竟访问资源一般是搭建web工程框架的时候的事情。不过了解它也是非常有好处的。

  这个接口的作用是可以让我们更方便操纵底层资源。因为JDK操纵底层资源基本就是 java.net.URL 、java.io.File 、java.util.Properties这些。取资源基本是根据绝对路径或当前类的相对路径来取。从类路径或Web容器上下文中获取资源的时候也不方便。Resource接口提供了更强大的访问底层资源的能力。

  废话不多说,看源码之前先来看一下Resource的类结构。

一、类结构

一、Resource接口

  如图,Resouce接口并不是一个根接口,它继承了一个简单的父接口 InputStreamSource,这个接口只有一个方法,用以返回一个输入流:

  1. InputStream getInputStream() throws IOException;

  来,直接上Resource接口的源码,中文是我根据英文注释自己翻译的,如下:

  1. public interface Resource extends InputStreamSource {
  2.  
  3. boolean exists(); // 资源是否存在
  4.  
  5. boolean isReadable(); // 资源是否可读
  6.  
  7. boolean isOpen(); // 资源所代表的句柄是否被一个stream打开了
  8.  
  9. URL getURL() throws IOException; // 返回资源的URL的句柄
  10.  
  11. URI getURI() throws IOException; // 返回资源的URI的句柄
  12.  
  13. File getFile() throws IOException; // 返回资源的File的句柄
  14.  
  15. long contentLength() throws IOException; // 资源内容的长度
  16.  
  17. long lastModified() throws IOException; // 资源最后的修改时间
  18.  
  19. Resource createRelative(String relativePath) throws IOException; //根据资源的相对路径创建新资源
  20.  
  21. String getFilename(); // 资源的文件名
  22.  
  23. String getDescription(); //资源的描述
  24.  
  25. }

  这个没什么好说的,继续!

二、抽象类AbstractResource

  对于任何的接口而言,这个直接抽象类是重中之重,里面浓缩了接口的大部分公共实现。翻译后如下:

  1. public abstract class AbstractResource implements Resource {
  2.  
  3. public boolean exists() { //判断文件是否存在,若判断过程产生异常(因为会调用SecurityManager来判断),就关闭对应的流
  4. try {
  5. return getFile().exists();
  6. }
  7. catch (IOException ex) {
  8. try {
  9. InputStream is = getInputStream(); //getInputStream()方法会被子类重写,
  10. is.close();
  11. return true;
  12. }
  13. catch (Throwable isEx) {
  14. return false;
  15. }
  16. }
  17. }
  18.  
  19. public boolean isReadable() { // 直接返回true,可读
  20. return true;
  21. }
  22.  
  23. public boolean isOpen() { // 直接返回false,未被打开
  24. return false;
  25. }
  26.  
  27. public URL getURL() throws IOException { // 留给子类重写
  28. throw new FileNotFoundException(getDescription() + " cannot be resolved to URL");
  29. }
  30.  
  31. public URI getURI() throws IOException { //返回url
  32. URL url = getURL();
  33. try {
  34. return ResourceUtils.toURI(url); //将url格式化后返回
  35. }
  36. catch (URISyntaxException ex) {
  37. throw new NestedIOException("Invalid URI [" + url + "]", ex);
  38. }
  39. }
  40.  
  41. public File getFile() throws IOException { // 留给子类重写
  42. throw new FileNotFoundException(getDescription() + " cannot be resolved to absolute file path");
  43. }
  44.  
  45. // 这个资源内容长度实际就是资源的字节长度,通过全部读取一遍来判断。这个方法调用起来很占资源啊!
  46. public long contentLength() throws IOException {
  47. InputStream is = this.getInputStream();
  48. Assert.state(is != null, "resource input stream must not be null"); //断言
  49. try {
  50. long size = 0;
  51. byte[] buf = new byte[255];
  52. int read;
  53. while((read = is.read(buf)) != -1) {
  54. size += read;
  55. }
  56. return size;
  57. }
  58. finally {
  59. try {
  60. is.close();
  61. }
  62. catch (IOException ex) {
  63. }
  64. }
  65. }
  66.  
  67. public long lastModified() throws IOException { // 返回资源的最后修改时间
  68. long lastModified = getFileForLastModifiedCheck().lastModified();
  69. if (lastModified == 0L) {
  70. throw new FileNotFoundException(getDescription() +
  71. " cannot be resolved in the file system for resolving its last-modified timestamp");
  72. }
  73. return lastModified;
  74. }
  75.  
  76. // 这是Resource接口所没有的方法,注释的意思是“返回文件,给时间戳检查”,要求子类重写...
  77. protected File getFileForLastModifiedCheck() throws IOException {
  78. return getFile();
  79. }
  80.  
  81. public Resource createRelative(String relativePath) throws IOException { // 留给子类重写
  82. throw new FileNotFoundException("Cannot create a relative resource for " + getDescription());
  83. }
  84.  
  85. public String getFilename() { // 默认返回空(假设资源没有文件名),除非子类重写
  86. return null;
  87. }
  88.  
  89. @Override
  90. public String toString() { // toString返回文件描述
  91. return getDescription();
  92. }
  93.  
  94. @Override
  95. public boolean equals(Object obj) { // equals比较的就是2个资源描述是否一样
  96. return (obj == this ||
  97. (obj instanceof Resource && ((Resource) obj).getDescription().equals(getDescription())));
  98. }
  99.  
  100. @Override
  101. public int hashCode() { // 返回资源描述的HashCode
  102. return getDescription().hashCode();
  103. }
  104.  
  105. }

结论:

  1、增加了一个方法,protected File getFileForLastModifiedCheck() throws IOException,要求子类实现,如果子类未实现,那么直接返回资源文件。这个方法的具体作用,后面再看实现类。

  2、方法 contentLength() ,是一个很比较重量级的方法,它通过将资源全部读取一遍来判断资源的字节数。255字节的缓冲数组来读取。子类一般会重写。(调整一下缓冲数组的大小?)

  3、getDescription() 是这个抽象类唯一没有实现的接口方法,留给子类去实现,资源文件默认的equals()、hashCode() 都通过这个来判断。

  4、InputStreamSource这个祖先接口的唯一方法 getInputStream()也没有被实现,留给子类。

三、Resource的子接口ContextResource和WritableResource

  这两个接口继承于Resource,拥有Resource的全部方法。其中,ContextResource接口增加了一个方法:

  1. String getPathWithinContext(); // 返回上下文内的路径

  这个方法使得它的实现类有了返回当前上下文路径的能力。

  WritableResource接口增加了2个方法:

  1. boolean isWritable(); // 是否可写
  2.  
  3. OutputStream getOutputStream() throws IOException; //返回资源的写入流

  这个方法使得它的实现类拥有了写资源的能力。

四、重要的抽象类AbstractFileResolvingResource

  这个抽象类继承自AbstractResource,重写了AbstractResource的大部分方法。

  1. /*
  2. * Copyright 2002-2011 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16.  
  17. package org.springframework.core.io;
  18.  
  19. import java.io.File;
  20. import java.io.IOException;
  21. import java.io.InputStream;
  22. import java.net.HttpURLConnection;
  23. import java.net.URI;
  24. import java.net.URL;
  25. import java.net.URLConnection;
  26.  
  27. import org.springframework.util.ResourceUtils;
  28.  
  29. /**
  30. * Abstract base class for resources which resolve URLs into File references,
  31. * such as {@link UrlResource} or {@link ClassPathResource}.
  32. *
  33. * <p>Detects the "file" protocol as well as the JBoss "vfs" protocol in URLs,
  34. * resolving file system references accordingly.
  35. *
  36. * @author Juergen Hoeller
  37. * @since 3.0
  38. */
  39. public abstract class AbstractFileResolvingResource extends AbstractResource {
  40.  
  41. @Override
  42. public File getFile() throws IOException { // 通过资源的URL得到资源本身,是文件就返回文件,否则返回描述
  43. URL url = getURL();
  44. if (url.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
  45. return VfsResourceDelegate.getResource(url).getFile();
  46. }
  47. return ResourceUtils.getFile(url, getDescription());
  48. }
  49.  
  50. @Override
  51. protected File getFileForLastModifiedCheck() throws IOException { //从<压缩文件地址>中获取文件
  52. URL url = getURL();
  53. if (ResourceUtils.isJarURL(url)) {
  54. URL actualUrl = ResourceUtils.extractJarFileURL(url);
  55. if (actualUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
  56. return VfsResourceDelegate.getResource(actualUrl).getFile();
  57. }
  58. return ResourceUtils.getFile(actualUrl, "Jar URL");
  59. }
  60. else {
  61. return getFile();
  62. }
  63. }
  64.  
  65. protected File getFile(URI uri) throws IOException { // 通过资源uri获取文件
  66. if (uri.getScheme().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
  67. return VfsResourceDelegate.getResource(uri).getFile();
  68. }
  69. return ResourceUtils.getFile(uri, getDescription());
  70. }
  71.  
  72. @Override
  73. public boolean exists() { //判断资源是否存在,如果是文件Url,直接获取文件判断,否则,建立连接来判断。
  74. try {
  75. URL url = getURL();
  76. if (ResourceUtils.isFileURL(url)) {
  77. // Proceed with file system resolution...
  78. return getFile().exists();
  79. }
  80. else {
  81. // Try a URL connection content-length header...
  82. URLConnection con = url.openConnection();
  83. ResourceUtils.useCachesIfNecessary(con);
  84. HttpURLConnection httpCon =
  85. (con instanceof HttpURLConnection ? (HttpURLConnection) con : null);
  86. if (httpCon != null) {
  87. httpCon.setRequestMethod("HEAD");
  88. int code = httpCon.getResponseCode();
  89. if (code == HttpURLConnection.HTTP_OK) {
  90. return true;
  91. }
  92. else if (code == HttpURLConnection.HTTP_NOT_FOUND) {
  93. return false;
  94. }
  95. }
  96. if (con.getContentLength() >= 0) {
  97. return true;
  98. }
  99. if (httpCon != null) {
  100. // no HTTP OK status, and no content-length header: give up
  101. httpCon.disconnect();
  102. return false;
  103. }
  104. else {
  105. // Fall back to stream existence: can we open the stream?
  106. InputStream is = getInputStream();
  107. is.close();
  108. return true;
  109. }
  110. }
  111. }
  112. catch (IOException ex) {
  113. return false;
  114. }
  115. }
  116.  
  117. @Override
  118. public boolean isReadable() { // 是否可读
  119. try {
  120. URL url = getURL();
  121. if (ResourceUtils.isFileURL(url)) {
  122. // Proceed with file system resolution...
  123. File file = getFile();
  124. return (file.canRead() && !file.isDirectory());
  125. }
  126. else {
  127. return true;
  128. }
  129. }
  130. catch (IOException ex) {
  131. return false;
  132. }
  133. }
  134.  
  135. @Override
  136. public long contentLength() throws IOException {
  137. URL url = getURL();
  138. if (ResourceUtils.isFileURL(url)) {
  139. // Proceed with file system resolution...
  140. return getFile().length();
  141. }
  142. else {
  143. // Try a URL connection content-length header...
  144. URLConnection con = url.openConnection();
  145. ResourceUtils.useCachesIfNecessary(con);
  146. if (con instanceof HttpURLConnection) {
  147. ((HttpURLConnection) con).setRequestMethod("HEAD");
  148. }
  149. return con.getContentLength();
  150. }
  151. }
  152.  
  153. @Override
  154. public long lastModified() throws IOException {
  155. URL url = getURL();
  156. if (ResourceUtils.isFileURL(url) || ResourceUtils.isJarURL(url)) {
  157. // Proceed with file system resolution...
  158. return super.lastModified();
  159. }
  160. else {
  161. // Try a URL connection last-modified header...
  162. URLConnection con = url.openConnection();
  163. ResourceUtils.useCachesIfNecessary(con);
  164. if (con instanceof HttpURLConnection) {
  165. ((HttpURLConnection) con).setRequestMethod("HEAD");
  166. }
  167. return con.getLastModified();
  168. }
  169. }
  170.  
  171. /**
  172. * Inner delegate class, avoiding a hard JBoss VFS API dependency at runtime.
  173. */
  174. private static class VfsResourceDelegate {
  175.  
  176. public static Resource getResource(URL url) throws IOException {
  177. return new VfsResource(VfsUtils.getRoot(url));
  178. }
  179.  
  180. public static Resource getResource(URI uri) throws IOException {
  181. return new VfsResource(VfsUtils.getRoot(uri));
  182. }
  183. }
  184.  
  185. }

  这个抽象类的子类都需要重写继承自AbstractResource的getURL()方法。因为绝大多数方法都依赖这个方法,进行资源在url上的操作。

  所以在查看资源情况的时候,需要根据url建立连接来查看。

  PS:框架感觉大都是这样,不难,设计模式也运用的不多。却有一种大巧不工、重剑无锋的感觉,因为代码运用真的非常简练。

    分析源码,很大程度上也锻炼了自己的英文文档阅读能力。共勉。

Spring源码分析——资源访问利器Resource之接口和抽象类分析的更多相关文章

  1. Spring源码分析——资源访问利器Resource之实现类分析

    今天来分析Spring的资源接口Resource的各个实现类.关于它的接口和抽象类,参见上一篇博文——Spring源码分析——资源访问利器Resource之接口和抽象类分析 一.文件系统资源 File ...

  2. 攻城狮在路上(贰) Spring(三)--- Spring 资源访问利器Resource接口

    Spring为了更好的满足各种底层资源的访问需求.设计了一个Resource接口,提供了更强的访问底层资源的能力.Spring框架使用Resource装载各种资源,包括配置文件资源.国际化属性文件资源 ...

  3. Ioc容器BeanPostProcessor-Spring 源码系列(3)

    Ioc容器BeanPostProcessor-Spring 源码系列(3) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Io ...

  4. 【spring源码分析】IOC容器初始化(二)

    前言:在[spring源码分析]IOC容器初始化(一)文末中已经提出loadBeanDefinitions(DefaultListableBeanFactory)的重要性,本文将以此为切入点继续分析. ...

  5. spring源码分析系列 (5) spring BeanFactoryPostProcessor拓展类PropertyPlaceholderConfigurer、PropertySourcesPlaceholderConfigurer解析

    更多文章点击--spring源码分析系列 主要分析内容: 1.拓展类简述: 拓展类使用demo和自定义替换符号 2.继承图UML解析和源码分析 (源码基于spring 5.1.3.RELEASE分析) ...

  6. Spring源码分析(八)AbstractBeanDefinition属性

    摘要:本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 在上一篇中已经完成了XML文档到GenericBeanDefiniton ...

  7. Spring源码解析02:Spring IOC容器之XmlBeanFactory启动流程分析和源码解析

    一. 前言 Spring容器主要分为两类BeanFactory和ApplicationContext,后者是基于前者的功能扩展,也就是一个基础容器和一个高级容器的区别.本篇就以BeanFactory基 ...

  8. Spring源码解析 | 第二篇:Spring IOC容器之XmlBeanFactory启动流程分析和源码解析

    一. 前言 Spring容器主要分为两类BeanFactory和ApplicationContext,后者是基于前者的功能扩展,也就是一个基础容器和一个高级容器的区别.本篇就以BeanFactory基 ...

  9. Spring源码分析-从@ComponentScan注解配置包扫描路径到IoC容器中的BeanDefinition,经历了什么(一)?

    阅前提醒 全文较长,建议沉下心来慢慢阅读,最好是打开Idea,点开Spring源码,跟着下文一步一步阅读,更加便于理解.由于笔者水平优先,编写时间仓促,文中难免会出现一些错误或者不准确的地方,恳请各位 ...

随机推荐

  1. 001.Getting Started -- 【入门指南】

    Getting Started 入门指南 662 of 756 people found this helpful Meng.Net 自译 1. Install .NET Core 到官网安装 .NE ...

  2. CSS Hack解决浏览器IE部分属性兼容性问题

    1.Css Hack 不同厂商的流览器或某浏览器的不同版本(如IE6-IE11,Firefox/Safari/Opera/Chrome等),对CSS的支持.解析不一样,导致在不同浏览器的环境中呈现出不 ...

  3. 深入理解及应用Position

    position俗称定位,主要取值及作用如下: static 默认值.没有定位,出现在正常文档流中 absolute 绝对定位,相对于position为absolute.relative.fixed的 ...

  4. Thinkcmf 二次开发

    一.   创建模板 demo 1 Tpl下创建demo文件-----后台启用新的模板 (网站信息--模板方案) 2 在模板在tpl/demo目录下创建Portal目录,然后在Portal目录下创建in ...

  5. CSS3过渡详解-遁地龙卷风

    第二版 0.环境准备 (1)过渡需要浏览器的支持,使用这些属性要加上浏览器厂商的前缀,我用的chrome49已经不需要前缀了, -o- Opera -webkit- Safari.Chrome -mo ...

  6. Java泛型介绍——HashMap总结

    今天在编程中,需要使用到Hashmap来存储和传递数据,发现自己学习Java这么久,实际上对泛型依旧知之甚少,搜索整理了一下HashMap的使用. HashMap的声明初始化,因为泛型的原因,起两个参 ...

  7. 关于arcgis engine的注记显示与关闭问题

    1.注记的添加需要拿到IGeoFeatureLayer接口下的AnnotationProperties属性,转为IAnnotationLayerPropertiesCollection接口,并创建一个 ...

  8. python的基础知识

    Python文件命名时不要有中文,不然在dos中不能执行 D:\Program Files\Py>Python hellyy.pyYear:2016Month(1-12):1Day(1-31): ...

  9. iOS之加密的三种方法

    //需要导入   #import <CommonCrypto/CommonCryptor.h> ==============MD5加密============ NSString *str ...

  10. 解决xcode升级之后安装的插件失效

    title: 解决xcode升级之后安装的插件失效date: 2015-08-23 11:07:53categories: 编辑工具 tags: xcode 我的博客:http://daycoding ...