创建Spring容器时通常需要访问XML配置文件,除此之外,我们可能有大量地方需要访问各种类型的文件,二进制——Spring把这些文件,二进制流等统称为资源。
在 SUN所提供的标准API里,资源访问通常由java.net.URL和文件IO完成,尤其是当我们需要访问来自网络的资源时,通常会选择URL类。
URL类可以处理一些常规的资源访问问题,但依然不能很好地满足所有底层资源的访问需要,比如,暂时还无法在类加载路径,或相对于ServletContext的路径中访问资源,虽然Java允许使用特定的如URL前缀注册新的处理类(例如已有http:前缀的处理类),但是这样做通常比较复杂,而且URL接口还缺少一些有用的功能,比如检查所指向的资源是否存在等。
Spring改进了Java资源策略,Spring为资源访问提供了一个Resource接口,该接口提供了更强的资源访问能力,Spring框架本身大量使用了Resource来访问底层资源。
Resource本身是一个接口,是具体资源访问策略的抽象,也是所有资源访问类所实现的接口。
Resource接口主要提供如下几个方法:
》getInputStream(): 定位并打开资源,返回资源对应的输入流。每次调用都返回新的输入流。调用者必须负责关闭输入流。
》exists():返回Resource所指向的资源是否存在。
》isOpen():返回资源文件是否打开,如果资源文件不能多次读取,每次读取结束时应该显示关闭,以防止资源泄露。
》getDescription():返回资源的描述信息,用于资源处理出错时输出该信息,通常是全限定文件名或实际URL
》getFile():返回资源对应的File对象。
》getURL():返回资源对应的URL对象。
最后两个方法通常无须使用,仅在通过简单方式访问无法实现时,Resource才能提供传统的资源访问功能。
Resource接口本身没有提供任何访问底层资源的实现逻辑,针对不同的底层资源,Spring将会提供不同的Resource实现类,不同的实现类负责不同的资源访问逻辑。
Spring的Resource设计是一种典型的策略模式,通过使用Resource接口,客户端程序可以在不同的资源访问策略之间自由切换。
Resource不仅可在Spring的项目中使用,也可直接作为资源访问的工具类使用。意思是说:即使不适用Spring框架,也可以使用Resource作为工具类,来代替URL。当然,使用Resource接口会让代码Spring的接口耦合在一起,但这种耦合只是部分工具集的耦合,不会造成太大的代码污染。
Resource实现类
Resource接口是Spring资源访问的接口,具体的资源访问由该接口的实现类完成。Spring提供了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。下面代码示范了使用UrlResource访问文件系统资源的实例:
1 // 创建一个Resource对象 2 UrlResource url = new UrlResource(MainTest.class.getClassLoader().getResource("book.xml")); 3 // 获取该资源的简单信息 4 System.out.println(url.getFilename()); 5 System.out.println(url.getDescription()); 6 // 创建Dom4j的解析器 7 // 定义xml文件解析器 8 SAXReader reader = new SAXReader(); 9 try { 10 // 开始解析xml文件 11 Document doc = reader.read(url.getFile()); 12 // 获取根元素 13 Element rootElement = doc.getRootElement(); 14 // 从根元素找它所有叫 "武侠小说" 的子元素 15 List<Element> list = rootElement.elements("武侠小说"); 16 // 便利所有武侠小说子元素 17 for (Element book : list) { 18 List<Element> authors = book.elements("作者"); 19 List<String> authorNames = new ArrayList<String>(); 20 for (Element author : authors) { 21 authorNames.add(author.getText()); 22 } 23 // 获得isbn属性 24 Attribute isbnAttribute = book.attribute("isbn"); 25 Attribute hotAttribute = book.attribute("hot"); 26 String hot = hotAttribute != null ? hotAttribute.getName() 27 + "=" + hotAttribute.getValue() : " "; 28 Element bookNameElement = book.element("书名"); 29 Attribute langAttribute = bookNameElement.attribute("lang"); 30 String lang = langAttribute != null ? langAttribute.getName() 31 + "=" + langAttribute.getValue() : " "; 32 System.out.println("书名:" + bookNameElement.getName() + " " 33 + lang + " " + isbnAttribute.getName() + "=" 34 + isbnAttribute.getValue() + " 作者:" 35 + Arrays.toString(authorNames.toArray()) + " email:" 36 + book.elementText("email") + " 价格:" 37 + book.elementText("价格") + " " + hot); 38 // 相当于取出名字是武侠小说的子元素集合,并迭代 39 // Iterator<Element> its = rootElement.elementIterator(); 40 } 41 } catch (DocumentException e) { 42 e.printStackTrace(); 43 }
下面是book.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE 我的书 SYSTEM "book.dtd"> 3 <我的书> 4 <武侠小说 isbn="1001" hot="true"> 5 <书名 lang="zh"><<天龙八部>></书名> 6 <作者>金庸</作者> 7 <作者>黄易</作者> 8 <email>jinyong@163.com</email> 9 <家庭地址>北京</家庭地址> 10 <价格>50</价格> 11 <简介>这是一本好书</简介> 12 </武侠小说> 13 <br ></br> 14 <武侠小说 isbn="1002"> 15 <书名 lang="zh"><<笑傲江湖>></书名> 16 <作者>金庸</作者> 17 <作者>黄易</作者> 18 <email>jinyong@163.com</email> 19 <家庭地址>北京</家庭地址> 20 <价格>50</价格> 21 <简介>这是一本好书</简介> 22 </武侠小说> 23 <br /> 24 </我的书>
boo.dtd
<?xml version="1.0" encoding="UTF-8"?> <!ELEMENT 我的书 (武侠小说,br)+> <!ELEMENT 武侠小说 (书名,作者+,email?,(办公室地址|家庭地址),价格,简介)> <!ELEMENT 书名 (#PCDATA)> <!ELEMENT 价格 (#PCDATA)> <!ELEMENT 简介 (#PCDATA)> <!ELEMENT 作者 (#PCDATA)> <!ELEMENT email (#PCDATA)> <!ELEMENT 办公室地址 (#PCDATA)> <!ELEMENT 家庭地址 (#PCDATA)> <!ELEMENT br EMPTY> <!ATTLIST 武侠小说 isbn CDATA #REQUIRED hot CDATA #IMPLIED> <!ATTLIST 书名 lang CDATA #REQUIRED>
上面的程序使用了UrlResource来访问本地磁盘资源,虽然UrlResource是为访问网络资源而设计的,但是通过使用file前缀也是可访问本地磁盘资源的。如果访问网络资源,可使用相应的网络前缀。例如访问HTTP资源:
1 // 创建一个Resource对象 2 UrlResource url = new UrlResource("http://www.cnblogs.com"); 3 // 获取资源的InputStream 4 InputStream in = url.getInputStream(); 5 // 解码方案 6 String charsetName = "UTF-8"; 7 // 选择合适的IO接口,因为这里我们要读取的是网页,所以选择Scanner接口比较合适 8 // Scanner:一个可以使用正则表达式来分析基本类型和字符串的简单文本扫描器。 9 Scanner scanner = new Scanner(in, charsetName); 10 while (scanner.hasNextLine()) 11 System.out.println(scanner.nextLine());
由于UrlResource是对java.net.URL的封装,所以UrlResource支持的前缀与URL类所支持的前缀完全相同。
访问类加载路径下的资源
ClassPathResource用来加载类加载路径下的资源,相对于其他Resource实现类,其主要优势是方便访问类接在路径里面的资源,尤其是相对于Web运用,ClassPath可自动搜素位于WEB-INF/classes下的资源文件,无需使用绝路路径访问:
上面的程序只用把创建资源的哪一行代码改成如下代码,其他完全相同:
1 // 创建一个Resource对象 2 ClassPathResource url = new ClassPathResource("book.xml");
使用ClassPathResource访问类加载路径下的book.xml文件,对比与前面进行资源访问的两个实例程序,我们发现除了创建Resource对象的代码有所区别外,其他代码完全一样,这就是Spring访问资源的优势;Spring的资源访问下消除了底层资源访问的差异,允许程序以一致的方式来访问不同的底层资源。
ClassPath实例可使用ClassPathResource构造器显示地创建,但更多的时候它都是隐式创建的。当执行Spring的某个方法时,该方法接收一个代表资源路径的字符串参数,当Spring识别该字符串中包含classpath:前缀后,系统将会自动创建ClassPathResource对象。
访问文件系统资源:FileSystemResource
该类的用法跟File类的用法一样。FileSystemResource在Spring用法跟ClassPathResource用法一样也是隐式创建,只不过它的前缀是file:
访问运用相关资源:
Spring提供了ServletContextResource类来访问Web Context下相对路径下的资源,ServletContextResource构造器接收一个代表资源位置的字符串参数,该资源位置是相对于Web运用根路径的位置。
使用ServletContextResource访问的资源,也可以通过文件IO访问或URL访问。通过java.io.File访问要求资源被压缩,而且在本地文件系统中;但使用ServletContextResource进行访问时无需关心资源是否被解压出来,或者直接放在JAR文件中,总可通过Servlet容器访问。
当程序试图直接通过File来访问Web Context下相对路径的资源时,应该改先使用ServletContext的getRealPath()方法来取得资源绝对路径,再以该路径来创建File对象。
下面我们把book.xml文件放在Web运用的WEB-INF路径下,然后通过JSP页面来直接访问book.xmll文件。要说么的是:在默认情况下,JSP不能直接访问WEB-INF路径下的任何资源,所以该运用的JSP页面需要使用ServletContextResource来访问资源, 下面是JSP页面代码:
1 <%@page import="org.dom4j.DocumentException"%> 2 <%@page import="org.dom4j.Attribute"%> 3 <%@page import="org.dom4j.Element"%> 4 <%@page import="org.dom4j.Document"%> 5 <%@page import="org.dom4j.io.SAXReader"%> 6 <%@page 7 import="org.springframework.web.context.support.ServletContextResource"%> 8 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> 9 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 10 <html> 11 <head> 12 13 14 <title>index</title> 15 </head> 16 17 <body> 18 <% 19 // 创建一个Resource对象 20 ServletContextResource url = new ServletContextResource( 21 application, "WEB-INF/book.xml"); 22 // 获取该资源的简单信息 23 System.out.println(url.getFile().getPath()); 24 System.out.println(url.getDescription()); 25 // 创建Dom4j的解析器 26 // 定义xml文件解析器 27 SAXReader reader = new SAXReader(); 28 try { 29 // 开始解析xml文件 30 Document doc = reader.read(url.getFile()); 31 // 获取根元素 32 Element rootElement = doc.getRootElement(); 33 // 从根元素找它所有叫 "武侠小说" 的子元素 34 @SuppressWarnings("unchecked") 35 List<Element> list = rootElement.elements("武侠小说"); 36 // 便利所有武侠小说子元素 37 for (Element book : list) { 38 @SuppressWarnings("unchecked") 39 List<Element> authors = book.elements("作者"); 40 List<String> authorNames = new ArrayList<String>(); 41 for (Element author : authors) { 42 authorNames.add(author.getText()); 43 } 44 // 获得isbn属性 45 Attribute isbnAttribute = book.attribute("isbn"); 46 Attribute hotAttribute = book.attribute("hot"); 47 String hot = hotAttribute != null ? hotAttribute.getName() 48 + "=" + hotAttribute.getValue() : " "; 49 Element bookNameElement = book.element("书名"); 50 Attribute langAttribute = bookNameElement.attribute("lang"); 51 String lang = langAttribute != null ? langAttribute 52 .getName() + "=" + langAttribute.getValue() : " "; 53 out.println("书名:" + bookNameElement.getName() + " " + lang 54 + " " + isbnAttribute.getName() + "=" 55 + isbnAttribute.getValue() + " 作者:" 56 + Arrays.toString(authorNames.toArray()) 57 + " email:" + book.elementText("email") + " 价格:" 58 + book.elementText("价格") + " " + hot); 59 // 相当于取出名字是武侠小说的子元素集合,并迭代 60 // Iterator<Element> its = rootElement.elementIterator(); 61 } 62 } catch (DocumentException e) { 63 e.printStackTrace(); 64 } 65 %> 66 </body> 67 </html>
将book.xml文件放到WEB-INF/路径下,通过使用ServletContextResource页面就可以直接访问WEB-INF下的资源了。