从今天开始,一步步走上源码分析的路。刚开始肯定要从简单着手。我们先从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,这个接口只有一个方法,用以返回一个输入流:

InputStream getInputStream() throws IOException;

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

public interface Resource extends InputStreamSource {boolean exists();      //  资源是否存在boolean isReadable();  //  资源是否可读boolean isOpen();      //  资源所代表的句柄是否被一个stream打开了
URL getURL() throws IOException;   //  返回资源的URL的句柄
URI getURI() throws IOException;   //  返回资源的URI的句柄
File getFile() throws IOException; //  返回资源的File的句柄long contentLength() throws IOException;   //  资源内容的长度long lastModified() throws IOException;    //  资源最后的修改时间
Resource createRelative(String relativePath) throws IOException;   //根据资源的相对路径创建新资源
String getFilename();  //  资源的文件名
String getDescription();   //资源的描述

}

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

二、抽象类AbstractResource

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

public abstract class AbstractResource implements Resource {public boolean exists() {  //判断文件是否存在,若判断过程产生异常(因为会调用SecurityManager来判断),就关闭对应的流try {return getFile().exists();}catch (IOException ex) {try {InputStream is = getInputStream();  //getInputStream()方法会被子类重写,
                is.close();return true;}catch (Throwable isEx) {return false;   }}}public boolean isReadable() {  //  直接返回true,可读return true;}public boolean isOpen() {  //  直接返回false,未被打开return false;}public URL getURL() throws IOException {        //  留给子类重写throw new FileNotFoundException(getDescription() + " cannot be resolved to URL");}public URI getURI() throws IOException {   //返回urlURL url = getURL();try {return ResourceUtils.toURI(url);     //将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;}// 这是Resource接口所没有的方法,注释的意思是“返回文件,给时间戳检查”,要求子类重写...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;}@Overridepublic String toString() {     //  toString返回文件描述return getDescription();}@Overridepublic boolean equals(Object obj) {    //  equals比较的就是2个资源描述是否一样return (obj == this ||(obj instanceof Resource && ((Resource) obj).getDescription().equals(getDescription())));}@Overridepublic int hashCode() {    //  返回资源描述的HashCodereturn getDescription().hashCode();   }}

结论:

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

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

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

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

三、Resource的子接口ContextResource和WritableResource

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

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

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

  WritableResource接口增加了2个方法:

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

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

四、重要的抽象类AbstractFileResolvingResource

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

/** Copyright 2002-2011 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package org.springframework.core.io;import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;import org.springframework.util.ResourceUtils;/*** Abstract base class for resources which resolve URLs into File references,* such as {@link UrlResource} or {@link ClassPathResource}.** <p>Detects the "file" protocol as well as the JBoss "vfs" protocol in URLs,* resolving file system references accordingly.** @author Juergen Hoeller* @since 3.0*/
public abstract class AbstractFileResolvingResource extends AbstractResource {@Overridepublic File getFile() throws IOException { //  通过资源的URL得到资源本身,是文件就返回文件,否则返回描述URL url = getURL();if (url.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {return VfsResourceDelegate.getResource(url).getFile();}return ResourceUtils.getFile(url, getDescription());}@Overrideprotected File getFileForLastModifiedCheck() throws IOException {  //从<压缩文件地址>中获取文件URL url = getURL();if (ResourceUtils.isJarURL(url)) {URL actualUrl = ResourceUtils.extractJarFileURL(url);if (actualUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {return VfsResourceDelegate.getResource(actualUrl).getFile();}return ResourceUtils.getFile(actualUrl, "Jar URL");}else {return getFile();}}protected File getFile(URI uri) throws IOException {   //  通过资源uri获取文件if (uri.getScheme().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {return VfsResourceDelegate.getResource(uri).getFile();}return ResourceUtils.getFile(uri, getDescription());}@Overridepublic boolean exists() {  //判断资源是否存在,如果是文件Url,直接获取文件判断,否则,建立连接来判断。try {URL url = getURL();if (ResourceUtils.isFileURL(url)) {// Proceed with file system resolution...return getFile().exists();}else {// Try a URL connection content-length header...URLConnection con = url.openConnection();ResourceUtils.useCachesIfNecessary(con);HttpURLConnection httpCon =(con instanceof HttpURLConnection ? (HttpURLConnection) con : null);if (httpCon != null) {httpCon.setRequestMethod("HEAD");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?InputStream is = getInputStream();is.close();return true;}}}catch (IOException ex) {return false;}}@Overridepublic boolean isReadable() {  //  是否可读try {URL url = getURL();if (ResourceUtils.isFileURL(url)) {// Proceed with file system resolution...File file = getFile();return (file.canRead() && !file.isDirectory());}else {return true;}}catch (IOException ex) {return false;}}@Overridepublic long contentLength() throws IOException {URL url = getURL();if (ResourceUtils.isFileURL(url)) {// Proceed with file system resolution...return getFile().length();}else {// Try a URL connection content-length header...URLConnection con = url.openConnection();ResourceUtils.useCachesIfNecessary(con);if (con instanceof HttpURLConnection) {((HttpURLConnection) con).setRequestMethod("HEAD");}return con.getContentLength();}}@Overridepublic long lastModified() throws IOException {URL url = getURL();if (ResourceUtils.isFileURL(url) || ResourceUtils.isJarURL(url)) {// Proceed with file system resolution...return super.lastModified();}else {// Try a URL connection last-modified header...URLConnection con = url.openConnection();ResourceUtils.useCachesIfNecessary(con);if (con instanceof HttpURLConnection) {((HttpURLConnection) con).setRequestMethod("HEAD");}return con.getLastModified();}}/*** Inner delegate class, avoiding a hard JBoss VFS API dependency at runtime.*/private static class VfsResourceDelegate {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));}}}

View Code

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

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

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

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

转载于:https://www.cnblogs.com/zrtqsk/p/4015323.html

Spring源码分析——资源访问利器Resource之接口和抽象类分析相关推荐

  1. spring源码依赖注入的核心方法populateBean(beanName, mbd, instanceWrapper)分析

    spring源码依赖注入的核心方法populateBean(beanName, mbd, instanceWrapper)分析:通过源码我们发现在分析这个方法之前,此对象已经创建完成实例,内存开辟了空 ...

  2. 从源码角度解析线程池中顶层接口和抽象类

    摘要:我们就来看看线程池中那些非常重要的接口和抽象类,深度分析下线程池中是如何将抽象这一思想运用的淋漓尽致的. 本文分享自华为云社区<[高并发]深度解析线程池中那些重要的顶层接口和抽象类> ...

  3. 筑基期第一式:深入Spring源码之第二节getBean全流程【循环依赖分析+扩展点分析】

    getBean整体逻辑 AbstractBeanFactory#doGetBean方法 protected <T> T doGetBean(String name, @Nullable C ...

  4. libed2k源码导读:(二)Session接口以及实现分析

    第二章 Session接口以及实现分析 目录 第二章 Session接口以及实现分析 2.1 获取会话状态 2.2 管理会话中所有的传输任务 2.3 管理点对点连接 2.4 管理alert 2.4.1 ...

  5. Spring源码深度解析(郝佳)-学习-ASM 类字节码解析

    我们在Java字节码文件结构剖析(二)中己经对MyTest35_1这个类的字节码做了完整的解析,今天,我们来看看Spring的ASM技术是如何来解析Java类字节码的.话不多说,先上实例. MyTes ...

  6. spring源码解读系列(八):观察者模式--spring监听器详解

    一.前言 在前面的文章spring源码解读系列(七)中,我们继续剖析了spring的核心refresh()方法中的registerBeanPostProcessors(beanFactory)(完成B ...

  7. 【Spring源码】ClassPathResource 资源文件源码分析

    上一篇文章我们主要介绍了开发 Spring 应用涉及到的一些核心组件,在文章的最后搭建了开发环境.那么接下来我们开始分析 Spring 源码部分,本篇文章首先分析 Spring 是如何封装资源文件的. ...

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

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

  9. spring源码分析之BeanDefinition相关

    目录 前言: BeanDefinition的家族系列 1.BeanDefintion的UML类图 2.BeanDefintion家族类详解 2.1.通用接口 2.2.BeanDefintion接口 2 ...

最新文章

  1. linux同步到对象存储,将Cpanel备份配置为S3对象存储的方法
  2. 在OpenCV中利用卷积进行图像滤波
  3. 《现代操作系统》第4章读书笔记--文件系统(未完成)
  4. 为什么国内软件行业普遍不如国外?
  5. 深度学习框架TensorFlow(1.安装和简介)
  6. 结合WebSocket编写WebGL综合场景示例
  7. lamp mysql大小限制_LAMP 调优之:MySQL 服务器调优
  8. ubuntu 12.04 samba 服务器搭建
  9. Datawha组队——Pandas(下)综合练习(打卡)
  10. python ansible模块_ansible常用模块
  11. java游戏暂停弹出字体_小白写了个java的小游戏 想加个暂停的功能 无从下手 求大佬们帮...
  12. 关于Arduino 步进电机Stepper库的一些想法
  13. 我在阿里工作9年,今天我离职了
  14. 从零开始使用Nadam进行梯度下降优化
  15. 在idea配置jetty和创建(包、文件)javaWeb以及Servlet简单实现
  16. 欧姆龙cp1h指令讲解_欧姆龙plc指令讲解.ppt
  17. 在 QNAP(威联通)NAS 上自动查找和删除 重复文件 的方法
  18. 大学计算机考核方案,课程实验考核方案
  19. 关于BOM表的一些事
  20. 微信会员卡系统怎么开通,微信会员卡开发,会员管理系统,CSS :first-line 伪元素

热门文章

  1. DNS详解: A记录,子域名,CNAME别名,PTR,MX,TXT,SRV,TTL
  2. [译][Tkinter 教程14] menu 菜单
  3. webservice 出现No service was found
  4. 如何用 Gitlab 做团队内的 Code Review
  5. DFS水题 URAL 1152 False Mirrors
  6. IPSEC非单播流量处理
  7. 前华远房地产任大炮任志强:我一定不要清华的毕业生
  8. Expression Designer系列工具汇总 [转载]
  9. 2019.04.06 电商04 模板嵌套
  10. 01_常用 Linux 命令的基本使用