【Spring4揭秘 基础3】统一抽象资源---Resource

JDK操纵底层资源基本就是 java.net.URL 、java.io.File 等。处理这些资源需要使用不同的接口,这就增加了我们系统的复杂性,正因为这样,Spring开发者抽象了一个Resource接口 ,提供了更强大的访问外部资源的能力。
上一章中的PropertySource表示Spring内部的key-value资源,而Resource表示一个外部资源。PropertySource可以来源于一个Resource。

一. Resource简介

InputStreamSource接口

Resouce接口继承了 InputStreamSource.

public interface InputStreamSource {  
    InputStream getInputStream() throws IOException;  
}  

getInputStream()方法:每次调用都将返回一个当前资源对应的java.io. InputStream字节流,调用者在使用完毕后必须关闭该资源

Resource 接口

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

exists():用于判断对应的资源是否真的存在。
isReadable():用于判断对应资源的内容是否可读。需要注意的是当其结果为true的时候,其内容未必真的可读,但如果返回false,则其内容必定不可读。
isOpen():用于判断当前资源是否代表一个已打开的输入流,如果结果为true,则表示当前资源的输入流不可多次读取,而且在读取以后需要对它进行关闭,以防止内存泄露。该方法主要针对于实现类InputStreamResource,实现类中只有它的返回结果为true,其他都为false。
getURL():返回当前资源对应的URL。如果当前资源不能解析为一个URL则会抛出异常。如ByteArrayResource就不能解析为一个URL。
getURI():返回当前资源对应的URI。如果当前资源不能解析为一个URL则会抛出异常。
getFile():返回当前资源对应的File。如果当前资源不能以绝对路径解析为一个File则会抛出异常。
contentLength() :资源的长度
lastModified:返回当前Resource代表的底层资源的最后修改时间。
createRelative:用于创建相对于当前Resource代表的底层资源的资源,比如当前Resource代表文件资源“d:/abc/”则createRelative(“xyz.txt”)将返回表文件资源“d:/abc/xyz.txt”
getFilename:返回当前Resource代表的底层文件资源的文件路径,比如File资源“file://d:/test.txt”将返回“d:/test.txt”,而URL资源http://www.javass.cn将返回“”,因为只返回文件路径。
getDescription:返回当前Resource代表的底层资源的描述信息。

二. Resource的主要实现类

抛开Resource接口及其子类复杂的类结构图。我们看一下它的主要实现类。

抽象类AbstractResource

实现了Resource接口,是大多数Resource的实现类的基类,提供了很多通用的方法。
比如exists方法会检查是否一个文件或者输入流能够被打开。isOpen永远返回false。”getURL()” 和”getFile()”方法会抛出异常。
toString将会返回描述信息。

ClassPathResource

ClassPathResource代表classpath路径的资源,将使用给定的Class或ClassLoader进行加载classpath 资源 。“isOpen”永远返回false,表示可多次读取资源。

JDK获取资源有两种方式

第一种 使用Class对象的getResource(String path)获取资源URL,getResourceAsStream(String path)获取资源流。 参数既可以是当前class文件相对路径(以文件夹或文件开头),也可以是当前class文件的绝对路径(以“/”开头,相对于当前classpath根目录)
第二种 使用ClassLoader对象的getResource(String path)获取资源URL,getResourceAsStream(String path)获取资源流。参数只能是绝对路径,但以“/”开头

ClassPathResource加载资源替代了Class类和ClassLoader类加载类路径资源方法,提供一致的访问方式。

ClassPathResource的三个实例变量:

private final String path;
private ClassLoader classLoader;
private Class clazz;

ClassPathResource提供了三个public构造器:

public ClassPathResource(String path):使用ClassUtils.getDefaultClassLoader()指定例变量classLoader
public ClassPathResource(String path, ClassLoader classLoader): 指定例变量classLoader
public ClassPathResource(String path, Class< ?> clazz):指定实例变量Class< ?> clazz

ClassPathResource如何获取资源:

@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;
}
protected URL resolveURL() {
        if (this.clazz != null) {
            return this.clazz.getResource(this.path);
        }
        else if (this.classLoader != null) {
            return this.classLoader.getResource(this.path);
        }
        else {
            return ClassLoader.getSystemResource(this.path);
        }
}

通过getURL()和resolveURL()可知,获取URL资源的顺序为:
如果存在实例变量this.clazz ,则使用Class对象的getResource获取资源
不存在clazz,则通过this.classLoader的 getResource获取资源
不存在this.classLoader则使用 ClassLoader.getSystemResource获取资源。
++获取资源流与获取资源URL的方法一致

FileSystemResource

可用来获取文件系统里面的资源。我们可以通过对应资源文件的文件路径来构建一个FileSystemResource。FileSystemResource还可以往对应的资源文件里面写内容,当然前提是当前资源文件是可写的,这可以通过其isWritable()方法来判断。FileSystemResource对外开放了对应资源文件的输出流,可以通过getOutputStream()方法获取到。
可用来代表URL对应的资源,它对URL做了一个简单的封装。通过给定一个URL地址,我们就能构建一个UrlResource。

ByteArrayResource

继承自AbstractResource,ByteArrayResource代表byte[]数组资源,对于“getInputStream”操作将返回一个ByteArrayInputStream。

使用:

public static void main(String[] args) throws Exception {
        Resource resource=new ByteArrayResource("hello world".getBytes());

        //1.getInputStream()
        InputStream inputStream= resource.getInputStream();
        byte[] bytes=new byte[inputStream.available()];
        inputStream.read(bytes);
        System.out.println(new String(bytes));//hello world
        inputStream.close();

        //2.其他方法
        System.out.println(resource.isOpen());//false
        System.out.println(resource.contentLength());//11
        //...

        //3.抛出异常方法 lastModified() getFile()...等方法
        //System.out.println(resource.lastModified()); 
        //System.out.println(resource.getFile());


    }

ServletContextResource

是针对于ServletContext封装的资源,用于访问ServletContext环境下的资源。ServletContextResource持有一个ServletContext的引用,其底层是通过ServletContext的getResource()方法和getResourceAsStream()方法来获取资源的。

InputStreamResource

是针对于输入流封装的资源,它的构建需要一个输入流。 对于“getInputStream ”操作将直接返回该字节流,因此只能读取一次该字节流,即“isOpen”永远返回true。

UrlResource

UrlResource代表URL资源,用于简化URL资源访问。
UrlResource一般支持如下资源访问:
-http:通过标准的http协议访问web资源,如new UrlResource(“http://地址”);
-ftp:通过ftp协议访问资源,如new UrlResource(“ftp://地址”);
-file:通过file协议访问本地文件系统资源,如new UrlResource(“file:d:/test.txt”);

二. ResourceLoader

在Spring里面还定义有一个ResourceLoader接口,该接口中只定义了一个用于获取Resource的getResource(String location)方法。这个接口的作用是判断传入的Resource相关字符串(位置,URL等),根据location参数返回不同的Resource。
ResourceLoader可以看做是Resource的工厂类

DefaultResourceLoader

DefaultResourceLoader是ResourceLoader最基本的实现类。
DefaultResourceLoader在获取Resource时采用的是这样的策略:
-首先判断指定的location是否含有“/”前缀有则返回ClassPathContextResource;
-然后判断指定的location是否含有“classpath:”前缀,如果有则把location去掉“classpath:”前缀返回对应的ClassPathResource;
-否则就把它当做一个URL来处理,封装成一个UrlResource进行返回;
-如果当成URL处理也失败的话就把location对应的资源当成是一个ClassPathContextResource进行返回。
源码如下:

public Resource getResource(String location) {
        Assert.notNull(location, "Location must not be null");
        if (location.startsWith("/")) {
            return getResourceByPath(location);
            //返回new ClassPathContextResource(path, getClassLoader());
        }
        else if (location.startsWith("classpath:")) {
            return new ClassPathResource(location.substring("classpath:".length()), getClassLoader());
        }
        else {
            try {
                // 转换为URL...
                URL url = new URL(location);
                return new UrlResource(url);
            }
            catch (MalformedURLException ex) {
                // url转换失败 
                return getResourceByPath(location);
                //返回new ClassPathContextResource(path, getClassLoader());
            }
        }
}

测试代码:

ResourceLoader loader = new DefaultResourceLoader();
Resource resource1 = loader.getResource("/application.properties");
System.out.println(resource1.getClass());
//class org.springframework.core.io.DefaultResourceLoader$ClassPathContextResource   

Resource resource2 = loader.getResource("classpath:application.properties");
System.out.println(resource2.getClass());
// class org.springframework.core.io.ClassPathResource 

Resource resource3 = loader.getResource("http://blog.csdn.net/isea533/article/details/42102297");
System.out.println(resource3.getClass());
//class org.springframework.core.io.UrlResource 

Resource resource4 = loader.getResource("dsfsdfsdfawewrwrwr");
System.out.println(resource4.getClass());
//class org.springframework.core.io.DefaultResourceLoader$ClassPathContextResource  

ResourcePatternResolver

Spring提供ResourcePatternResolver接口来加载多个Resource,该接口继承了ResourceLoader并添加了“Resource[] getResources(String locationPattern)”用来加载多个Resource:

locationPattern的前缀:

classpath: 用于加载类路径(包括jar包)中的一个且仅一个资源;
classpath* : 用于加载类路径(包括jar包)中的所有匹配的资源,可使用Ant路径模式。

Ant路径通配符支持“?”、“*”、“**”,注意通配符匹配不包括目录分隔符“/”:
“?”:匹配一个字符,如“config?.xml”将匹配“config1.xml”;
“*”:匹配零个或多个字符串,如“cn/*/config.xml”将匹配“cn/javass/config.xml”,但不匹配匹配“cn/config.xml”;而“cn/config-*.xml”将匹配“cn/config-dao.xml”;
“**”:匹配路径中的零个或多个目录,如“cn/**/config.xml”将匹配“cn /config.xml”,也匹配“cn/javass/spring/config.xml”;而“cn/javass/config-**.xml”将匹配“cn/javass/config-dao.xml”,即把“**”当做两个“*”处理。

ApplicationContext

大多数ApplicationContext都实现了ResourceLoader接口,所以它们也具有载入资源

你可能感兴趣的:(【Spring】,Spring4揭秘)