java访问文件的抽象类_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的类结构。

一、类结构

xxx4sSJEydOnLjZceafAwAAMBvPWwEAAMxGPgcAAGA28jkAAACzkc8BAACYjXwOAADAbORzAAAAZmP+OeLEiRMnTpw4cbPjzD8HAABgNp63AgAAmI18DgAAwGzkcwAAAGYjnwMAADAb+RwAAIDZyOcAAADM9v8BCK5qXoNC5b0AAAAASUVORK5CYII=

一、Resource接口

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

InputStream getInputStream() throws IOException;

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

public interface Resource extendsInputStreamSource {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 implementsResource {public boolean exists() { //判断文件是否存在,若判断过程产生异常(因为会调用SecurityManager来判断),就关闭对应的流

try{returngetFile().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() throwsIOException {

InputStream is= this.getInputStream();

Assert.state(is!= null, "resource input stream must not be null"); //断言

try{long size = 0;byte[] buf = new byte[255];intread;while((read = is.read(buf)) != -1) {

size+=read;

}returnsize;

}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");

}returnlastModified;

}//这是Resource接口所没有的方法,注释的意思是“返回文件,给时间戳检查”,要求子类重写...

protected File getFileForLastModifiedCheck() throwsIOException {returngetFile();

}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返回文件描述

returngetDescription();

}

@Overridepublic boolean equals(Object obj) { //equals比较的就是2个资源描述是否一样

return (obj == this ||(objinstanceof Resource &&((Resource) obj).getDescription().equals(getDescription())));

}

@Overridepublic int hashCode() { //返回资源描述的HashCode

returngetDescription().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的大部分方法。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

/** 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.*/

packageorg.springframework.core.io;importjava.io.File;importjava.io.IOException;importjava.io.InputStream;importjava.net.HttpURLConnection;importjava.net.URI;importjava.net.URL;importjava.net.URLConnection;importorg.springframework.util.ResourceUtils;/*** Abstract base class for resources which resolve URLs into File references,

* such as {@linkUrlResource} or {@linkClassPathResource}.

*

*

Detects the "file" protocol as well as the JBoss "vfs" protocol in URLs,

* resolving file system references accordingly.

*

*@authorJuergen Hoeller

*@since3.0*/

public abstract class AbstractFileResolvingResource extendsAbstractResource {

@Overridepublic File getFile() throws IOException { //通过资源的URL得到资源本身,是文件就返回文件,否则返回描述

URL url =getURL();if(url.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {returnVfsResourceDelegate.getResource(url).getFile();

}returnResourceUtils.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)) {returnVfsResourceDelegate.getResource(actualUrl).getFile();

}return ResourceUtils.getFile(actualUrl, "Jar URL");

}else{returngetFile();

}

}protected File getFile(URI uri) throws IOException { //通过资源uri获取文件

if(uri.getScheme().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {returnVfsResourceDelegate.getResource(uri).getFile();

}returnResourceUtils.getFile(uri, getDescription());

}

@Overridepublic boolean exists() { //判断资源是否存在,如果是文件Url,直接获取文件判断,否则,建立连接来判断。

try{

URL url=getURL();if(ResourceUtils.isFileURL(url)) {//Proceed with file system resolution...

returngetFile().exists();

}else{//Try a URL connection content-length header...

URLConnection con =url.openConnection();

ResourceUtils.useCachesIfNecessary(con);

HttpURLConnection httpCon=(coninstanceof 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() throwsIOException {

URL url=getURL();if(ResourceUtils.isFileURL(url)) {//Proceed with file system resolution...

returngetFile().length();

}else{//Try a URL connection content-length header...

URLConnection con =url.openConnection();

ResourceUtils.useCachesIfNecessary(con);if (con instanceofHttpURLConnection) {

((HttpURLConnection) con).setRequestMethod("HEAD");

}returncon.getContentLength();

}

}

@Overridepublic long lastModified() throwsIOException {

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 instanceofHttpURLConnection) {

((HttpURLConnection) con).setRequestMethod("HEAD");

}returncon.getLastModified();

}

}/*** Inner delegate class, avoiding a hard JBoss VFS API dependency at runtime.*/

private static classVfsResourceDelegate {public static Resource getResource(URL url) throwsIOException {return newVfsResource(VfsUtils.getRoot(url));

}public static Resource getResource(URI uri) throwsIOException {return newVfsResource(VfsUtils.getRoot(uri));

}

}

}

View Code

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

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

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值