8.3.1 Resource实现类
Resource
接口是Spring
资源访问的接口,具体的资源访问由该接口的实现类完成。 Spring
提供了Resource
接口的大量实现类。
Resource 接口实现类 |
描述 |
UrlResource |
访问网络资源的实现类。 |
ClassPathResource |
访问类加载路径里资源的实现类。 |
FileSystemResource |
访问文件系统里资源的实现类。 |
ServletContextResource |
访问相对于ServletContext 路径下的资源的实现类. |
InputStreamResource |
访问输入流资源的实现类。 |
ByteArrayResource |
访问字节数组资源的实现类. |
针对不同的底层资源,这些Resource
实现类提供了相应的资源访问逻辑,并提供便捷的包装,以利于客户端程序的资源访问。
1. 访问网络资源
访问网络资源通过UrlResource
类实现, UrlResource
是java.net.URL
类的包装,主要用于访问之前通过URL
类访问的资源对象。URL
资源通常应该提供标准的协议前缀。例如:file:
用于访问文件系统;http:
于通过HTTP
协议访问资源;ftp:
用于通过FTP
协议访问资源等。
UrlResource
类实现了Resource
接口,对Resource
的全部方法提供了实现,完全支持Resource
的全部API
。
程序示例
1 2 3 4 5 6 7
| E:\workspace_QingLiangJiJavaEEQiYeYingYongShiZhang5\UrlResource ├─book.xml ├─lib\ │ └─dom4j-1.6.1.jar └─src\ └─lee\ └─UrlResourceTest.java
|
下面的代码示范了使用UrlResource
访问文件系统资源的示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| package lee;
import org.springframework.core.io.UrlResource; import org.dom4j.*; import org.dom4j.io.*; import java.util.*;
public class UrlResourceTest { public static void main(String[] args) throws Exception { UrlResource ur = new UrlResource("file:book.xml"); System.out.println(ur.getFilename()); System.out.println(ur.getDescription()); SAXReader reader = new SAXReader(); Document doc = reader.read(ur.getFile()); Element el = doc.getRootElement(); List l = el.elements(); for (Iterator it = l.iterator(); it.hasNext();) { Element book = (Element) it.next(); List ll = book.elements(); for (Iterator it2 = ll.iterator(); it2.hasNext();) { Element eee = (Element) it2.next(); System.out.println(eee.getText()); } } } }
|
上面程序中的粗体字代码使用UrlResource
来访问本地磁盘资源,虽然UrlResource
是为访问网络资源而设计的,但通过使用file:
前缀也可访问本地磁盘资源。如果需要访问网络资源,则可以使用如下两个常用前缀。
http:
,该前缀用于访问基于HTTP
协议的网络资源。
ftp:
,该前缀用于访问基于FTP
协议的网络资源。
由于UrIResource
是对java.net.URL
的封装,所以UrIResource
支持的前缀与URL
类所支持的前缀完全相同。
将应用所需的book.xml
访问放在应用的当前路径下,运行该程序,即可看到使用UrlResource
访问本地磁盘资源的效果。
1 2 3 4 5 6
| book.xml URL [file:book.xml] 疯狂Java讲义 李刚 轻量级Java EE企业应用实战 李刚
|
2. 访问类加载路径下的资源
ClassPathResource
用来访问类加载路径
下的资源,相对于其他的Resource
实现类,其主要优势是方便访问类加载路径下的资源,尤其对于Web
应用, ClassPathResource
可自动搜索位于WEB-INF/classes
下的资源文件,无须使用绝对路径访问.
程序示例
1 2 3 4 5 6 7
| E:\workspace_QingLiangJiJavaEEQiYeYingYongShiZhang5\ClassPathResource ├─lib\ │ └─dom4j-1.6.1.jar └─src\ ├─book.xml └─lee\ └─ClassPathResourceTest.java
|
下面示例程序示范了将book xml
放在类加载路径(src目录)下,然后使用如下程序访问它。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| package lee;
import java.util.Iterator; import java.util.List; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; import org.springframework.core.io.ClassPathResource;
public class ClassPathResourceTest { public static void main(String[] args) throws Exception { ClassPathResource cr = new ClassPathResource("book.xml"); System.out.println(cr.getFilename()); System.out.println(cr.getDescription()); SAXReader reader = new SAXReader(); Document doc = reader.read(cr.getFile()); Element el = doc.getRootElement(); List l = el.elements(); for (Iterator it = l.iterator();it.hasNext() ; ) { Element book = (Element)it.next(); List ll = book.elements(); for (Iterator it2 = ll.iterator();it2.hasNext() ; ) { Element eee = (Element)it2.next(); System.out.println(eee.getText()); } } } }
|
上面程序中的粗体字代码用于访问类加载路径下的book.xml
文件,对比前面进行资源访问的示例程序,发现两个程序除了进行资源访问的代码有所区别之外,其他程序代码基本一致,这就是Spring
资源访问的优势— Spring
的资源访问消除了底层资源访问的差异,允许程序以一致的方式来访问不同的底层资源。
3. 访问文件系统资源
Spring
提供的File SystemResource
类用于访问文件系统资源。不过使用FileSystemResource
来访问文件系统资源并没有太大的优势,因为Java
提供的File
类也可用于访问文件系统资源。
当然,使用FileSystemResource
也可消除底层资源访问的差异,程序通过统一的Resource API
来进行资源访问。
程序示例
1 2 3 4 5 6 7
| E:\workspace_QingLiangJiJavaEEQiYeYingYongShiZhang5\FileSystemResource ├─book.xml ├─lib\ │ └─dom4j-1.6.1.jar └─src\ └─lee\ └─FileSystemResourceTest.java
|
下面的程序是使用FileSystemResource
来访问文件系统资源的示例程序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| package lee;
import java.util.Iterator; import java.util.List; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; import org.springframework.core.io.FileSystemResource;
public class FileSystemResourceTest { public static void main(String[] args) throws Exception { FileSystemResource fr = new FileSystemResource("book.xml"); System.out.println(fr.getFilename()); System.out.println(fr.getDescription()); SAXReader reader = new SAXReader(); Document doc = reader.read(fr.getFile()); Element el = doc.getRootElement(); List l = el.elements(); for (Iterator it = l.iterator();it.hasNext() ; ) { Element book = (Element)it.next(); List ll = book.elements(); for (Iterator it2 = ll.iterator();it2.hasNext() ; ) { Element eee = (Element)it2.next(); System.out.println(eee.getText()); } } } }
|
与前两种使用Resource
进行资源访问的区别在于:用于确定的资源的字符串写法不同,位于本地文件系统内,而且无须使用任何前缀。
FileSystemResource
实例可使用FileSystemResource
构造器显式地创建,但更多的时候它都是隐式创建的。执行Spring
的某个方法时,该方法接受一个代表资源路径的字符串参数,当Spring
识别该字符串参数中包含file:
前缀后,系统将会自动创建FileSystemResource
对象。
4. 访问应用相关资源
Spring
提供了ServletContextResource
类来访问Web Context
下相对路径下的资源,ServletContextResource
构造器接受一个代表资源位置的字符串参数,该资源位置是相对于Web
应用根路径的位置。
使用ServletContextResource
访问的资源,也可通过文件IO
访问或URL
访问。
访问Web Context下的资源时使用File类ServletContextResource的区别
- 通过
java.io.File
访问要求资源被解压缩,而且在本地文件系统中;
- 但使用
ServletContextResource
进行访问时则无须关心资源是否被解压缩出来,或者直接存放在JAR
文件中,总可通过Servlet
容器访问。
当程序试图直接通过File
来访问Web Context
下相对路径下的资源时,应该先使用ServletContext
的getRealPath()
方法来取得资源绝对路径,再以该绝对路径来创建File
对象。
程序示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| E:\workspace_QingLiangJiJavaEEQiYeYingYongShiZhang5\ServletContextResource ├─src\ └─WebContent\ ├─META-INF\ │ └─MANIFEST.MF ├─test.jsp └─WEB-INF\ ├─book.xml ├─lib\ │ ├─dom4j-1.6.1.jar │ ├─spring-aop-5.0.2.RELEASE.jar │ ├─spring-aspects-5.0.2.RELEASE.jar │ ├─spring-beans-5.0.2.RELEASE.jar │ ├─spring-context-5.0.2.RELEASE.jar │ ├─spring-context-indexer-5.0.2.RELEASE.jar │ ├─spring-context-support-5.0.2.RELEASE.jar │ ├─spring-core-5.0.2.RELEASE.jar │ ├─spring-expression-5.0.2.RELEASE.jar │ ├─spring-instrument-5.0.2.RELEASE.jar │ ├─spring-jcl-5.0.2.RELEASE.jar │ ├─spring-jdbc-5.0.2.RELEASE.jar │ ├─spring-jms-5.0.2.RELEASE.jar │ ├─spring-messaging-5.0.2.RELEASE.jar │ ├─spring-orm-5.0.2.RELEASE.jar │ ├─spring-oxm-5.0.2.RELEASE.jar │ ├─spring-test-5.0.2.RELEASE.jar │ ├─spring-tx-5.0.2.RELEASE.jar │ ├─spring-web-5.0.2.RELEASE.jar │ ├─spring-webflux-5.0.2.RELEASE.jar │ ├─spring-webmvc-5.0.2.RELEASE.jar │ └─spring-websocket-5.0.2.RELEASE.jar └─web.xml
|
下面把book.xml
文件放在web
应用的WEB-INF
路径下,然后通过JSP
页面来直接访问该book.xm
文件。值得指出的是,在默认情况下,JSP
不能直接访问WEB-INF
路径下的任何资源,所以该应用中的JSP
页面需要使用ServletContextResource
来访问该资源。
下面是JSP
页面代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| <%@ page contentType="text/html; charset=GBK" language="java" errorPage=""%> <%@ page import="org.springframework.web.context.support.ServletContextResource"%> <%@ page import="org.dom4j.*,org.dom4j.io.*,java.util.*"%> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>测试ServletContextResource</title> </head> <body> <h3>测试ServletContextResource</h3> <% ServletContextResource src = new ServletContextResource(application, "WEB-INF/book.xml"); System.out.println(src.getFilename()); System.out.println(src.getDescription()); SAXReader reader = new SAXReader(); Document doc = reader.read(src.getFile()); Element el = doc.getRootElement(); List l = el.elements(); for (Iterator it = l.iterator(); it.hasNext();) { Element book = (Element) it.next(); List ll = book.elements(); for (Iterator it2 = ll.iterator(); it2.hasNext();) { Element eee = (Element) it2.next(); out.println(eee.getText()); out.println("<br/>"); } } %> </body> </html>
|
上面程序中的粗体字代码指定应用从Web Context
下的WEB-INF
路径下读取book.xml
资源,该示例恰好将book.xml
文件放在应用的WEB-INF/
路径下,通过使用ServletContextResource
就可让JSP
页面直接访问WEB-INF
下的资源了。
将应用部署在Tomcat
中,然后启动Tomcat
,再打开浏览器访问该JSP
页面,将看到浏览器显示内容如下:
1 2 3 4 5 6
| 测试ServletContextResource
疯狂Java讲义 李刚 轻量级Java EE企业应用实战 李刚
|
5. 访问字节数组资源
Spring
提供了InputStreamResource
来访问二进制输入流资源, InputSteamResource
是针对输入流
的Resource
实现,只有当没有合适的Resource
实现时,才考虑使用该InputSteamResource
。在通常情况下,优先考虑使用ByteArrayResource
,或者基于文件的Resource
实现.
与其他Resource
实现不同的是, nputSteamResource
是一个总是被打开的Resource
,所以isOpen
方法总是返回true
。因此如果需要多次读取某个流,就不要使用InputSteamResource
.
在创建InputStreamResource
实例时要提供一个InputStream
参数。
在一些个别的情况下, InputStreamResource
是有用的。例如从数据库中读取一个Blob
对象,程序需要获取该Blob
对象的内容,就可先通过Blob
的getBinaryStream()
方法获取二进制输入流,再将该二进制输入流包装成Resource
对象,然后就可通过该Resource
对象来访问该Blob
对象所包含的资源了。
InputStreamResource
虽然是适应性很广的Resource
实现,但效率并不好。因此,尽量不要使用InputStreamResource
作为参数,而应尽量使用ByteArrayResource
或FileSystemResource
代替它。
程序示例
1 2 3 4 5 6
| E:\workspace_QingLiangJiJavaEEQiYeYingYongShiZhang5\ByteArrayResource ├─lib\ │ └─dom4j-1.6.1.jar └─src\ └─lee\ └─ByteArrayResourceTest.java
|
如下程序示范了如何使用ByteArrayResource
来读取字节数组资源。出于演示目的,程序中字节数组直接通过字符串来获得
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| package lee;
import java.util.Iterator; import java.util.List; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; import org.springframework.core.io.ByteArrayResource;
public class ByteArrayResourceTest { public static void main(String[] args) throws Exception { String file = "<?xml version='1.0' encoding='UTF-8'?>" + "<计算机书籍列表><书><书名>疯狂Java讲义" + "</书名><作者>李刚</作者></书><书><书名>" + "轻量级Java EE企业应用实战</书名><作者>李刚" + "</作者></书></计算机书籍列表>"; byte[] fileBytes = file.getBytes(); ByteArrayResource bar = new ByteArrayResource(fileBytes); System.out.println(bar.getDescription()); SAXReader reader = new SAXReader(); Document doc = reader.read(bar.getInputStream()); Element el = doc.getRootElement(); List l = el.elements(); for (Iterator it = l.iterator(); it.hasNext();) { Element book = (Element) it.next(); List ll = book.elements(); for (Iterator it2 = ll.iterator(); it2.hasNext();) { Element eee = (Element) it2.next(); System.out.println(eee.getText()); } } } }
|
上面程序中的粗体字代码用于根据字节数组来创建ByteArrayResource
对象,接下来就可通过该Resource
对象来访问该字节数组资源了。访问字节数组资源时, Resource
对象的getFile()
和getFilename()
两个方法不可用—这是可想而知的事情—因为此时访问的资源是字节数组,当然不存在对应的File
对象和文件名了.
在实际应用中,字节数组可能通过网络传输获得,也可能通过管道流获得,还可能通过其他方式获得……只要得到了代表资源的字节数组,程序就可通过ByteArrayResource
将字节数组包装成Resource
实例,并利用Resource
来访问该资源。
对于需要采用InputStreamResource
访问的资源,可先从InputStream
流中读出字节数组,然后以字节数组来创建ByteArrayResource
。这样, InputStreamResource
也可被转换成ByteArrayResource
,从而方便多次读取。