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

来源:互联网 发布:360软件管家字体模糊 编辑:程序博客网 时间:2024/05/17 04:38

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();    }}
原创粉丝点击