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,从而方便多次读取。