Spring源码分析——资源访问利器Resource之接口和抽象类分析
从今天开始,一步步走上源码分析的路。刚开始肯定要从简单着手。我们先从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 { //返回url
URL 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;
} @Override
public String toString() { // toString返回文件描述
return getDescription();
} @Override
public boolean equals(Object obj) { // equals比较的就是2个资源描述是否一样
return (obj == this ||
(obj instanceof Resource && ((Resource) obj).getDescription().equals(getDescription())));
} @Override
public int hashCode() { // 返回资源描述的HashCode
return 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 { @Override
public 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());
} @Override
protected 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());
} @Override
public 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;
}
} @Override
public 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;
}
} @Override
public 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();
}
} @Override
public 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));
}
} }
这个抽象类的子类都需要重写继承自AbstractResource的getURL()方法。因为绝大多数方法都依赖这个方法,进行资源在url上的操作。
所以在查看资源情况的时候,需要根据url建立连接来查看。
PS:框架感觉大都是这样,不难,设计模式也运用的不多。却有一种大巧不工、重剑无锋的感觉,因为代码运用真的非常简练。
分析源码,很大程度上也锻炼了自己的英文文档阅读能力。共勉。
Spring源码分析——资源访问利器Resource之接口和抽象类分析的更多相关文章
- Spring源码分析——资源访问利器Resource之实现类分析
今天来分析Spring的资源接口Resource的各个实现类.关于它的接口和抽象类,参见上一篇博文——Spring源码分析——资源访问利器Resource之接口和抽象类分析 一.文件系统资源 File ...
- 攻城狮在路上(贰) Spring(三)--- Spring 资源访问利器Resource接口
Spring为了更好的满足各种底层资源的访问需求.设计了一个Resource接口,提供了更强的访问底层资源的能力.Spring框架使用Resource装载各种资源,包括配置文件资源.国际化属性文件资源 ...
- Ioc容器BeanPostProcessor-Spring 源码系列(3)
Ioc容器BeanPostProcessor-Spring 源码系列(3) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Io ...
- 【spring源码分析】IOC容器初始化(二)
前言:在[spring源码分析]IOC容器初始化(一)文末中已经提出loadBeanDefinitions(DefaultListableBeanFactory)的重要性,本文将以此为切入点继续分析. ...
- spring源码分析系列 (5) spring BeanFactoryPostProcessor拓展类PropertyPlaceholderConfigurer、PropertySourcesPlaceholderConfigurer解析
更多文章点击--spring源码分析系列 主要分析内容: 1.拓展类简述: 拓展类使用demo和自定义替换符号 2.继承图UML解析和源码分析 (源码基于spring 5.1.3.RELEASE分析) ...
- Spring源码分析(八)AbstractBeanDefinition属性
摘要:本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 在上一篇中已经完成了XML文档到GenericBeanDefiniton ...
- Spring源码解析02:Spring IOC容器之XmlBeanFactory启动流程分析和源码解析
一. 前言 Spring容器主要分为两类BeanFactory和ApplicationContext,后者是基于前者的功能扩展,也就是一个基础容器和一个高级容器的区别.本篇就以BeanFactory基 ...
- Spring源码解析 | 第二篇:Spring IOC容器之XmlBeanFactory启动流程分析和源码解析
一. 前言 Spring容器主要分为两类BeanFactory和ApplicationContext,后者是基于前者的功能扩展,也就是一个基础容器和一个高级容器的区别.本篇就以BeanFactory基 ...
- Spring源码分析-从@ComponentScan注解配置包扫描路径到IoC容器中的BeanDefinition,经历了什么(一)?
阅前提醒 全文较长,建议沉下心来慢慢阅读,最好是打开Idea,点开Spring源码,跟着下文一步一步阅读,更加便于理解.由于笔者水平优先,编写时间仓促,文中难免会出现一些错误或者不准确的地方,恳请各位 ...
随机推荐
- 哈哈:sqlserver2008附加数据库时操作系统错误5(拒绝访问)错误5012的解决办法
老师说:无论干什么,出错了,先大笑三声.如果人多了,在心里也要大笑三声.哈哈哈!!! 刚刚重装系统完后,然后想学习.自己去安装sqlserver2008数据库,完之后想附加之前的数据库.可是当我一点确 ...
- 使用Object.create 克隆对象以及实现单继承
var Plane = function () { this.blood = 100; this.attack = 1; this.defense = 1; }; var plane = new Pl ...
- Mvc与WebForm优缺点及Mvc的使用
关于Mvc与WebForm的优缺点在网上的评论可谓不胜枚举,但脱离了我们的项目来谈这些意义就不大了.以我们这次改版来看,WebForm的优势有以下几点: 一,可以使用<#include>, ...
- jquery attr()方法
在JS中设置节点的属性与属性值用到setAttribute(),获得节点的属性与属性值用到getAttribute(),而在jquery中,用一个attr()就可以全部搞定了,赞一个先 ^^ jque ...
- iOS多线程之5.GCD的基本使用
上一篇文章我对GCD的几个基本概念做了介绍,但是大家看完了可能觉得对理解GCD并没有什么卵用.其实会用GCD其实很简单,只要记住两条就可以了. 1. 主队列里的任务必须在异步函数中执行. 主队 ...
- 你真的了解UITabBarController吗?
一:首先查看一下关于UITabBarController的定义 NS_CLASS_AVAILABLE_IOS(2_0) @interface UITabBarController : UIViewCo ...
- UItableView与UICollectionView
UITableView 1. UITableViewStyleGrouped 分区表格样式创建表格 .separatorStyle = UITableViewCellSeparatorStyleSin ...
- XIB 上的控件不显示怎么办
原文:http://www.cnblogs.com/sandyzhang/p/5660061.html 午休时间遇到有人求助:说是XIB 上内容都有的,但是看不到,demo 运行的话控件都存在的. ...
- 使用 UICollectionView 实现日历签到功能
概述 在 App 中,日历通常与签到功能结合使用.是提高用户活跃度的一种方式,同时,签到数据中蕴含了丰富的极其有价值的信息.下面我们就来看看如何在 App 中实现日历签到功能. 效果图 ..... 思 ...
- Android 框架学习之 第一天 okhttp & Retrofit
最近面试,一直被问道新技术新框架,这块是短板,慢慢补吧. 关于框架的学习,分几个步骤 I.框架的使用 II.框架主流使用的版本和Android对应的版本 III.框架的衍生使用比如okhttp就会有R ...