博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【Spring4揭秘 基础3】统一抽象资源---Resource
阅读量:4290 次
发布时间:2019-05-27

本文共 8104 字,大约阅读时间需要 27 分钟。

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资源将返回“”,因为只返回文件路径。
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如何获取资源:

@Overridepublic 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接口,所以它们也具有载入资源

你可能感兴趣的文章
JDK1.8 HashMap中put源码分析
查看>>
JAVA中ListIterator和Iterator详解与辨析
查看>>
Mysql 在 mybatis 环境下 批量新增 并 将已存在的数据更新
查看>>
Java 多线程详解(四)------生产者和消费者
查看>>
聊聊分布式事务,再说说解决方案
查看>>
Spring AOP 使用介绍,从前世到今生
查看>>
并发工具类(三)控制并发线程数的Semaphore
查看>>
SpringBoot -- 熔断机制 Circuit Breaker
查看>>
玩转spring boot——负载均衡与session共享
查看>>
SpringMVC处理请求流程
查看>>
Java集合:HashMap源码剖析
查看>>
MySql 优化
查看>>
java队列——queue详细分析
查看>>
Java并发编程:线程池的使用
查看>>
SpringMVC重要注解(二)@ControllerAdvice
查看>>
Spring cloud系列十四 分布式链路监控Spring Cloud Sleuth
查看>>
Spring IOC讲解
查看>>
不错的资源
查看>>
Java ConcurrentModificationException异常原因和解决方法
查看>>
通过Nginx+tomcat+redis实现反向代理 、负载均衡及session同步
查看>>