Spring源码之Resource加载源码解析(二)

Resource接口

//简单的接口对象:使用流InputStreamreSource 
public interface InputStreamSource {

    InputStream getInputStream() throws IOException;

}

//接口对象:用于从实际资源类型抽象出来的资源描述符的接口,如文件或类路径资源。
public interface Resource extends InputStreamSource {

    //是否存在
    boolean exists();

    //是否可读
    boolean isReadable();

    //返回此资源是否表示具有开放流的句柄。如果是真的,无法读取InputStream的多次,并必须予以封闭,以防止资源泄漏。
    boolean isOpen();

    //返回此资源的URL句柄。
    URL getURL() throws IOException;

    //返回此资源的URI句柄。
    URI getURI() throws IOException;

    //返回此资源的文件句柄。
    File getFile() throws IOException;

    //返回此资源的内容长度。
    long contentLength() throws IOException;

     //返回此资源的最后修改时间戳。
    long lastModified() throws IOException;

    //创建与此资源相对应的资源,并返回对应资源的资源句柄。relativepath相对路径(相对于这个资源)
    Resource createRelative(String relativePath) throws IOException;

    //确定此资源的文件名,通常是最后部分,如:test.txt
    String getFilename();

    //返回此资源的描述,用于在处理资源时进行错误输出。
    String getDescription();

}

基础实现抽象类AbstractResource


//Resource的基础实现抽象类
public abstract class AbstractResource implements Resource {

    //资源是否存在
    public boolean exists() {
        // Try file existence: can we find the file in the file system?
        try {
            return getFile().exists();
        }
        catch (IOException ex) {
            // Fall back to stream existence: can we open the stream?
            try {
                InputStream is = getInputStream();
                is.close();
                return true;
            }
            catch (Throwable isEx) {
                return false;
            }
        }
    }

    //默认实现:可读
    public boolean isReadable() {
        return true;
    }

    //默认实现:资源不含有开放的句柄
    public boolean isOpen() {
        return false;
    }

    //默认实现报错,需子类自己实现
    public URL getURL() throws IOException {
        throw new FileNotFoundException(getDescription() + " cannot be resolved to URL");
    }

    //默认实现:先获取URL,再转URI
    public URI getURI() throws IOException {
        URL url = getURL();
        try {
            return ResourceUtils.toURI(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;
    }
    //返回File对象,调用getFile()
    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() {
        return getDescription();
    }

    @Override
    public boolean equals(Object obj) {
        return (obj == this ||
            (obj instanceof Resource && ((Resource) obj).getDescription().equals(getDescription())));
    }

    @Override
    public int hashCode() {
        return getDescription().hashCode();
    }

}

文件的抽象操作AbstractFileResolvingResource


//抽象File资源操作抽象类
public abstract class AbstractFileResolvingResource extends AbstractResource {

    //获取文件对象
    @Override
    public File getFile() throws IOException {
        URL url = getURL();
        //url协议为"vfs",交给VfsResourceDelegate处理
        if (url.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
            return VfsResourceDelegate.getResource(url).getFile();
        }
        //其他则由ResourceUtils处理
        return ResourceUtils.getFile(url, getDescription());
    }

    /**
     * This implementation determines the underlying File
     * (or jar file, in case of a resource in a jar/zip).
     */
    //这个实现决定了底层文件(或jar文件,如果是JAR / zip中的资源)
    @Override
    protected File getFileForLastModifiedCheck() throws IOException {
        //获取URL
        URL url = getURL();
        //是否为jar
        if (ResourceUtils.isJarURL(url)) {
            //提取jar的URL
            URL actualUrl = ResourceUtils.extractJarFileURL(url);
            if (actualUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
                //url协议为"vfs",交给VfsResourceDelegate处理
                return VfsResourceDelegate.getResource(actualUrl).getFile();
            }
            //由ResourceUtils处理,获取File对象
            return ResourceUtils.getFile(actualUrl, "Jar URL");
        }
        else {
            return getFile();
        }
    }

    //通过给定uri,获取File对象
    protected File getFile(URI uri) throws IOException {
        //url协议为"vfs",交给VfsResourceDelegate处理
        if (uri.getScheme().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
            return VfsResourceDelegate.getResource(uri).getFile();
        }
        //否则由ResourceUtils处理,获取File对象
        return ResourceUtils.getFile(uri, getDescription());
    }

    //是否存在
    @Override
    public boolean exists() {
        try {
            //或如URL
            URL url = getURL();
            //1.文件路径,由文件系统解析
            if (ResourceUtils.isFileURL(url)) {
                // Proceed with file system resolution...
                return getFile().exists();
            }
            else {
                //2.否则,采用尝试获取URL链接请求中header的content-length属性
                // Try a URL connection content-length header...
                URLConnection con = url.openConnection();
                customizeConnection(con);
                HttpURLConnection httpCon =
                        (con instanceof HttpURLConnection ? (HttpURLConnection) con : null);
                //2.1 httpCon对象非空,尝试获取链接,并获取返回状体码
                if (httpCon != null) {
                    //获取状态码
                    int code = httpCon.getResponseCode();
                    if (code == HttpURLConnection.HTTP_OK) { //链接陈功
                        return true;
                    }
                    else if (code == HttpURLConnection.HTTP_NOT_FOUND) { //未找到
                        return false;
                    }
                }
                //2.2 链接失败,获取返content-length的值是否大于等于0
                if (con.getContentLength() >= 0) {
                    return true;
                }
                //2.3 httpCon对象非空,但HTTP状态码不是OK/NOT_FOUND状态
                if (httpCon != null) {
                    //没有HTTP OK状态,也没有获取到content-length header,则放弃链接,返回false
                    httpCon.disconnect();
                    return false;
                }
                else {
                    //2.4 尝试获取文件流,成功,则存在
                    InputStream is = getInputStream();
                    is.close();
                    return true;
                }
            }
        }
        catch (IOException ex) {
            return false;
        }
    }

    //是否可读
    @Override
    public boolean isReadable() {
        try {
            URL url = getURL();
            //1.文件路径,由文件系统解析
            if (ResourceUtils.isFileURL(url)) {
                // Proceed with file system resolution...
                File file = getFile();
                return (file.canRead() && !file.isDirectory());
            }
            else {
                //2.否则返回true
                return true;
            }
        }
        catch (IOException ex) {
            return false;
        }
    }

    @Override
    public long contentLength() throws IOException {
        URL url = getURL();
        //1.文件路径,由文件系统解析
        if (ResourceUtils.isFileURL(url)) {
            // Proceed with file system resolution...
            return getFile().length();
        }
        else {
            //2.否则,采用尝试获取URL链接请求中header的content-length属性
            // Try a URL connection content-length header...
            URLConnection con = url.openConnection();
            customizeConnection(con);
            return con.getContentLength();
        }
    }

    @Override
    public long lastModified() throws IOException {
        URL url = getURL();
        //1.文件或jar,调用父类方法进行处理
        if (ResourceUtils.isFileURL(url) || ResourceUtils.isJarURL(url)) {
            // Proceed with file system resolution...
            return super.lastModified();
        }
        else {
            //2.否则,采用尝试获取URL链接请求中header的last-modified属性
            // Try a URL connection last-modified header...
            URLConnection con = url.openConnection();
            customizeConnection(con);
            return con.getLastModified();
        }
    }


    //自定义连接
    protected void customizeConnection(URLConnection con) throws IOException {
        ResourceUtils.useCachesIfNecessary(con);
        if (con instanceof HttpURLConnection) {
            customizeConnection((HttpURLConnection) con);
        }
    }

    //自定义连接
    protected void customizeConnection(HttpURLConnection con) throws IOException {
        //设置method为HEAD,则表明只返回请求头部
        con.setRequestMethod("HEAD");
    }

    //内部委托类,在运行时避免硬JBoss VFS API的依赖。
    private static class VfsResourceDelegate {

        //委托给VfsResource处理
        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));
        }
    }

}

ClassPathResource源码阅读

//对classpath下的xml文件进行解析,默认使用ClassLoader来加载资源
public class ClassPathResource extends AbstractFileResolvingResource {
    //路径
    private final String path;
    //类加器
    private ClassLoader classLoader;
    //字节码对象
    private Class<?> clazz;

    //构造方法
    public ClassPathResource(String path) {
        this(path, (ClassLoader) null);
    }

    //构造方法
    public ClassPathResource(String path, ClassLoader classLoader) {
        Assert.notNull(path, "Path must not be null");
        String pathToUse = StringUtils.cleanPath(path);
        if (pathToUse.startsWith("/")) {
            pathToUse = pathToUse.substring(1);
        }
        this.path = pathToUse;
        this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
    }

    //构造方法
    public ClassPathResource(String path, Class<?> clazz) {
        Assert.notNull(path, "Path must not be null");
        this.path = StringUtils.cleanPath(path);
        this.clazz = clazz;
    }

    //构造方法
    protected ClassPathResource(String path, ClassLoader classLoader, Class<?> clazz) {
        this.path = StringUtils.cleanPath(path);
        this.classLoader = classLoader;
        this.clazz = clazz;
    }


    public final String getPath() {
        return this.path;
    }

    //获取类加载器
    public final ClassLoader getClassLoader() {
        //clazz不为空,获取clazz的类加载,否则返回classLoader
        return (this.clazz != null ? this.clazz.getClassLoader() : this.classLoader);
    }


    @Override
    public boolean exists() {
        return (resolveURL() != null);
    }

    //解析底层类路径资源的URL。
    protected URL resolveURL() {
        //1. path不以'/'开头时,默认是从此类所在的包下取资源;path以'/'开头时,则是从项目的ClassPath根下获取资源,获取path的资源路径
        if (this.clazz != null) {
            return this.clazz.getResource(this.path);
        }
        //2.path不能以'/'开头时,path是指类加载器的加载范围(在资源加载的过程中,使用的逐级向上委托的形式加载的),'/'表示Boot ClassLoader中的加载范围,获取path的资源路径
        else if (this.classLoader != null) {
            return this.classLoader.getResource(this.path);
        }
        //3.加载当前类的类加器,获取path的资源路径
        else {
            return ClassLoader.getSystemResource(this.path);
        }
    }

    //获取文件流
    public InputStream getInputStream() throws IOException {
        InputStream is;
        if (this.clazz != null) {
            is = this.clazz.getResourceAsStream(this.path);
        }
        else if (this.classLoader != null) {
            is = this.classLoader.getResourceAsStream(this.path);
        }
        else {
            is = ClassLoader.getSystemResourceAsStream(this.path);
        }
        if (is == null) {
            throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
        }
        return is;
    }

    //此实现返回底层类路径资源的URL,如果可用的话。
    @Override
    public URL getURL() throws IOException {
        URL url = resolveURL();
        if (url == null) {
            throw new FileNotFoundException(getDescription() + " cannot be resolved to URL because it does not exist");
        }
        return url;
    }


    //给定相对于该描述符的底层资源的路径,创建一个ClassPathResource对象。
    @Override
    public Resource createRelative(String relativePath) {
        String pathToUse = StringUtils.applyRelativePath(this.path, relativePath);
        return new ClassPathResource(pathToUse, this.classLoader, this.clazz);
    }

    //获取文件名称
    @Override
    public String getFilename() {
        return StringUtils.getFilename(this.path);
    }

    //获取文件描述
    public String getDescription() {
        StringBuilder builder = new StringBuilder("class path resource [");
        String pathToUse = path;
        if (this.clazz != null && !pathToUse.startsWith("/")) {
            builder.append(ClassUtils.classPackageAsResourcePath(this.clazz));
            builder.append('/');
        }
        if (pathToUse.startsWith("/")) {
            pathToUse = pathToUse.substring(1);
        }
        builder.append(pathToUse);
        builder.append(']');
        return builder.toString();
    }

    /**
     * This implementation compares the underlying class path locations.
     */
    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (obj instanceof ClassPathResource) {
            ClassPathResource otherRes = (ClassPathResource) obj;
            return (this.path.equals(otherRes.path) &&
                    ObjectUtils.nullSafeEquals(this.classLoader, otherRes.classLoader) &&
                    ObjectUtils.nullSafeEquals(this.clazz, otherRes.clazz));
        }
        return false;
    }

    /**
     * This implementation returns the hash code of the underlying
     * class path location.
     */
    @Override
    public int hashCode() {
        return this.path.hashCode();
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值