Spring的Resource接口与ResourceLoader接口

概述:
在Java中,将不同来源的资源抽象成URL,通过注册不同的handler(URLStramHandler)来处理不同来源的资源读取逻辑,一般handler的类型使用不同的前缀(协议,Protocol)来识别,如“file:”,“http:”,“jar:”等,然而URL没有默认定义相对Classpath或ServletContext等资源的handler,虽然可以注册自己的URLStreamHandler来解析特定的URL前缀(协议),比如”classpath:”,然而需要了解URL实现机制。因而Spring对其内部使用到的资源实现了自己的抽象结构:Resource接口来封装底层资源:

public interface InputStreamSource {
    InputStream getInputStream() throws IOException;
}
public interface Resource extends InputStreamSource {
    boolean exists();
    boolean isReadable();
    boolean isOpen();
    URL getURL() throws IOException;
    URI getURI() throws IOException;
    File getFile() throws IOException;
    long contentLength() throws IOException;
    long lastModified() throws IOException;
    Resource createRelative(String relativePath) throws IOException;
    String getFilename();
    String getDescription();
}

对不同来源的资源文件都有相应的Resource实现:文件(FileSystemResource)、ClassPath资源(ClassPathResource)、URL资源(UrlResource)、InputStream资源(InputStreamResource)、Byte数组(ByteArrayResource)等,相关类图如下所示:
这里写图片描述

Resource

其中,最常用的有四个:

  1. ClassPathResource:通过 ClassPathResource 以类路径的方式进行访问;
  2. FileSystemResource:通过 FileSystemResource 以文件系统绝对路径的方式进行访问;
  3. ServletContextResource:通过 ServletContextResource 以相对于Web应用根目录的方式进行访问;
  4. UrlResource :通过java.net.URL来访问资源,当然它也支持File格式,如“file:”。

ClassPathResource

@Override
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;
}

通过ClassLoader#getResourceAsStream(String);Class#getResourceAsStream(String)获得流信息

ClassLoader#getResourceAsStream(String)的目录为class
Class#getResourceAsStream(String)的目录为当前Class文件目录
TestResource.class.getClassLoader().getResourceAsStream("")
TestResource.class.getClassLoader().getResourceAsStream("/")
TestResource.class.getResourceAsStream("")
TestResource.class.getResourceAsStream("/")
输出
file:/E:/Workspaces/base_Java/target/classes/
null
file:/E:/Workspaces1/myProject/base_Java/target/classes/com/code/resource/
file:/E:/Workspaces1/myProject/base_Java/target/classes/

FileSystemResource

@Override
public InputStream getInputStream() throws IOException {
    return new FileInputStream(this.file);
}

通过创建FileInputStream输入流获得流信息

ServletContextResource

@Override
public InputStream getInputStream() throws IOException {
    InputStream is = this.servletContext.getResourceAsStream(this.path);
    if (is == null) {
        throw new FileNotFoundException("Could not open " + getDescription());
    }
    return is;
}

通过ServletContext#getResourceAsStream返回资源文件的读取字节流

在web工程中,我们一般来说,是不能采取类加载器读取webRoot(webapp目录下)目录下配置文件的通过SevletContext#getResourceAsStream(“/imgs/test.properties”)获得输入流或通过SevletContext#getRealPath(“/imgs/Sunset.jpg”)获得绝对路径

UrlResource

@Override
public InputStream getInputStream() throws IOException {
    URLConnection con = this.url.openConnection();
    ResourceUtils.useCachesIfNecessary(con);
    try {
        return con.getInputStream();
    }
    catch (IOException ex) {
        // Close the HTTP connection (if applicable).
        if (con instanceof HttpURLConnection) {
            ((HttpURLConnection) con).disconnect();
        }
        throw ex;
    }
}

UrlResource 封装了java.net.URL,它能够被用来访问任何通过URL可以获得的对象,例如:文件、HTTP对象、FTP对象等。

ResourceLoader

类图结构

DefaultResourceLoader

@Override
public Resource getResource(String location) {
    Assert.notNull(location, "Location must not be null");

    for (ProtocolResolver protocolResolver : this.protocolResolvers) {
        Resource resource = protocolResolver.resolve(location, this);
        if (resource != null) {
            return resource;
        }
    }

    if (location.startsWith("/")) {
        return getResourceByPath(location);
    }
    else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
        return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
    }
    else {
        try {
            // Try to parse the location as a URL...
            URL url = new URL(location);
            return new UrlResource(url);
        }
        catch (MalformedURLException ex) {
            // No URL -> resolve as resource path.
            return getResourceByPath(location);
        }
    }
}

在Spring里面还定义有一个ResourceLoader接口,该接口中只定义了一个用于获取Resource的getResource(String location)方法。DefaultResourceLoader在获取Resource时:首先判断指定的location是否含有“classpath:”前缀,如果有则把location去掉“classpath:”前缀返回对应的ClassPathResource;否则就把它当做一个URL来处理,封装成一个UrlResource进行返回;如果当成URL处理也失败的话就把location对应的资源当成是一个ClassPathResource进行返回。

PathMatchingResourcePatternResolver
解决路径下有一个或多个资源文件,源路径可能是一个简单的路径或者可能含有特殊的{classpath*:}前缀和或Ant-style正则表达式(请看 org.springframework.util.AntPathMatcher类

Resource[] re = pmrp.getResources("com/spring/**/*.class");
    for(int i = 0;i < re.length;i++) {
        System.out.println(re[i]);
}
输出class/com/spring下的所有文件:
file [E:\Workspaces\base_Java\target\classes\com\spring\springbean\SpringBeanTest.class]
file [E:\Workspaces\base_Java\target\classes\com\spring\springbean\SpringBeanTest2.class]
file [E:\Workspaces\base_Java\target\classes\com\spring\springbean\SpringBeanTest3.class]

Ant-style正则表达式:
? 匹配任何单字符
* 匹配0或者任意数量的字符
** 匹配0或者更多的目录

例如:
/app/*.x 匹配(Matches)所有在app路径下的.x文件
/app/p?ttern 匹配(Matches) /app/pattern 和 /app/pXttern,但是不包括/app/pttern
/**/example 匹配(Matches) /app/example, /app/foo/example, 和 /example
/app/**/dir/file. 匹配(Matches) /app/dir/file.jsp, > /app/foo/dir/file.html,/app/foo/bar/dir/file.pdf, 和 /app/dir/file.java
/**/*.jsp 匹配(Matches)任何的.jsp 文件

阅读更多

没有更多推荐了,返回首页