org.springframework.core.io包内的源码分析

前些日子看《深入理解Javaweb开发》时,看到第一章java的io流,发觉自己对io流真的不是很熟悉。然后看了下JDK1.7中io包的一点点代码,又看了org.springframework.core.io包的一些类和组织方式,当作是学习吧。总结一下。

先挂下spring.core.io包的类图,其中接口是方框表示,抽象类带了abstract前缀,剩下那个两个框重贴的则代表实现类。没怎么划过类图,如果有好的画类图工具请推荐给我。

画得不好的地方就见谅了。注:以下源码匹配的是spring-core-4.1.6Release.jar里面的org.springframework.core.io包。

 

先看处于最上层的接口,叫InputStreamSource,里面只有一个抽象方法

接下是resources,这个接口我们一般都会用到,贴源码:

public interface Resource extends InputStreamSource {

    boolean exists();//文件是否存在
    
    boolean isReadable();//是否可读

    boolean isOpen();//资源是否被一个inputstream打开,如果已被打开,则不允许其他流再打开

    URL getURL() throws IOException;//获取资源url路径,当不能以url描述时抛出ioException异常

    
    URI getURI() throws IOException;//获取资源uri路径,当不能以url描述时抛出ioException异常

    File getFile() throws IOException;//获取file,file在IO流中仅仅是一个指向作用

    long contentLength() throws IOException;//资源的字节长度,可以拿来算资源的大小

    long lastModified() throws IOException;//资源最后修改时间

    Resource createRelative(String relativePath) throws IOException;//根据资源相对路径创建资源

    String getFilename();//返回文件名

    String getDescription();//资源描述

}

 

后面是abstractResource,这是个挺重要的类,主要是对resource接口的基本实现,

public abstract class AbstractResource implements Resource {

    @Override
    public boolean exists() {
        //看是否能在硬盘上找到
        try {
            return getFile().exists();
        }
        catch (IOException ex) {
            // 试一下能不能打开输出流
            try {
                InputStream is = getInputStream();
                is.close();
                return true;
            }
            catch (Throwable isEx) {
                return false;
            }
        }
    }

    /**
     * 一般都是可读的,所以默认方法是true
     */
    @Override
    public boolean isReadable() {
        return true;
    }

    /**
     * 默认值是false,没有inputStream来读时的默认状态
     */
    @Override
    public boolean isOpen() {
        return false;
    }

    /**
     * url这个属性一般只出现在web的IO资源中,网络resource需要override这个方法,其他类型资源敢访问这个方法,抛个异常给他
     */
    @Override
    public URL getURL() throws IOException {
        throw new FileNotFoundException(getDescription() + " cannot be resolved to URL");
    }

    
    @Override
    public URI getURI() throws IOException {
        URL url = getURL();
        try {
                        //url可以转成uri,uri不能转url
            return ResourceUtils.toURI(url);
        }
        catch (URISyntaxException ex) {
            throw new NestedIOException("Invalid URI [" + url + "]", ex);
        }
    }

    /**
     * JDK的File类都是返回绝对路径的File,当一个资源没有绝对路径时,抛个异常给它没毛病
     */
    @Override
    public File getFile() throws IOException {
        throw new FileNotFoundException(getDescription() + " cannot be resolved to absolute file path");
    }

    /**
     * inputStream读取byte[]字节流,并返回该数组的长度,相当于把文件读了一次
     */
    @Override
    public long contentLength() throws IOException {
        InputStream is = this.getInputStream();
                //Assert应该是叫断言
        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 {
                              //一定要在finally中关闭流
                is.close();
            }
            catch (IOException ex) {
            }
        }
    }

    /**
     * 返回getFileForLastModifiedCheck().lastModified()的值,该职为0L,抛个异常给他没毛病
     */
    @Override
    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;
    }

    /**
     * 调用getFile方法
     */
    protected File getFileForLastModifiedCheck() throws IOException {
        return getFile();
    }

    /**
     * 直接假定相对资源创建不了,再抛个异常给他
     */
    @Override
    public Resource createRelative(String relativePath) throws IOException {
        throw new FileNotFoundException("Cannot create a relative resource for " + getDescription());
    }

    /**
     * 又是一个假定,假定filename文件名为null
     */
    @Override
    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())));
    }

    /**
     * 返回hashCode
     */
    @Override
    public int hashCode() {
        return getDescription().hashCode();
    }

}
    

至于其他实现类其实都是在接口和抽象类的基础上去拓展,所以我并不是读得很仔细,先这样子吧。

小总结:其实为什么要分好几层去继承刚开始我是很不懂的,后面看了源代码小伙伴的博文后便豁然开朗了。

1.resources是高度抽象的接口,里面是对所有资源文件的具体方法抽象,但是并不是每个资源都有这个抽象里面的所有方法,所以abstractResource对其进行了一般的实现,

对于一些并不是所有的Resources都会有方法,例如非网络资源没有url和uri属性,默认方法就直接抛异常了,简单粗暴。举个栗子,不是所有的动物都会游泳,一只猪想游泳,先抛个异常给猪接着先。

2.resources里面有isOpen(),isReadAble()接口,这是个小技巧吧,类似与模版方法模式的钩子方法,也是很值得我们学习的。

 

赞 (0) 评论 分享 ()