Spring的Resource接口与ResourceLoader接口理解

时间:2021-04-03 17:53:36

Spring的Resource接口位于包org.springframework.core.io中;

 

Spring定义Resource接口是为了提供更强的访问底层资源能力的抽象;

 

对spring来说Resource接口代表着物理存在的任何资源。

 

1.先看一下resource接口及其实现的类层次关系图: Spring的Resource接口与ResourceLoader接口理解 对应的UML类图为: Spring的Resource接口与ResourceLoader接口理解
 其中,最常用的有四个:

ClassPathResource:通过 ClassPathResource 以类路径的方式进行访问;
FileSystemResource:通过 FileSystemResource 以文件系统绝对路径的方式进行访问;
ServletContextResource:通过 ServletContextResource 以相对于Web应用根目录的方式进行访问;
UrlResource :通过java.net.URL来访问资源,当然它也支持File格式,如“file:”。
1、先看一下Resource接口的定义:
Java代码  Spring的Resource接口与ResourceLoader接口理解
  1. public interface Resource extends InputStreamSource {  
  2.     boolean exists();  
  3.     boolean isReadable();  
  4.     boolean isOpen();  
  5.     URL getURL() throws IOException;  
  6.     URI getURI() throws IOException;  
  7.     File getFile() throws IOException;  
  8.     long contentLength() throws IOException;  
  9.     long lastModified() throws IOException;  
  10.     Resource createRelative(String relativePath) throws IOException;  
  11.     String getFilename();  
  12.     String getDescription();  
  13. }  

 

它是spring访问资源最基本的接口。实际访问的时候直接用Resource接口就可以,不必使用其子类。其实经常用到的(resource的真正目的)方法是public InputStream getInputStream()

 

2、FileSystemResource
Java代码  Spring的Resource接口与ResourceLoader接口理解
  1. public InputStream getInputStream() throws IOException {  
  2.              return new FileInputStream( this. file);  
  3.       }  

 

这里的file是使用传入(构造函数中)的path生成的File,然后使用该File构造FileInputStream作为方法的输出。

 

这里的path一般要给出绝对路径,当然也可以是相对路径,如果是相对路径要注意其根目录。例如在eclipse中,它的根目录就是你工程目录作为你的根目录。

 

3、ClassPathResource

 

Java代码  Spring的Resource接口与ResourceLoader接口理解
  1. public InputStream getInputStream() throws IOException {  
  2.             InputStream is;  
  3.              if ( this. clazz != null) {  
  4.                   is = this. clazz.getResourceAsStream( this. path);  
  5.             }  
  6.              else {  
  7.                   is = this. classLoader.getResourceAsStream( this. path);  
  8.             }  
  9.              if (is == null) {  
  10.                    throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");  
  11.             }  
  12.              return is;  
  13.       }  

 

 这里是通过Class或者ClassLoader的getResourceAsStream()方法来获得InputStream的。其path一般都是以“classpath:”开头,如果以“classpath*:”开头表示所有与给定名称匹配的classpath资源都应该被获取。

 

4、ServletContextResource

 

Java代码  Spring的Resource接口与ResourceLoader接口理解
  1. public InputStream getInputStream() throws IOException {  
  2.         InputStream is = this.servletContext.getResourceAsStream(this.path);  
  3.         if (is == null) {  
  4.             throw new FileNotFoundException("Could not open " + getDescription());  
  5.         }  
  6.         return is;  
  7.     }  

 

ServletContextResource通过ServletContext的getResourceAsStream()来取得InputStream,这里path必须以“/”开头,并且相对于当前上下文的根目录。如常用的path="/WEB-INF/web.xml"。

 

5、UrlResource

 

Java代码  Spring的Resource接口与ResourceLoader接口理解
  1. public InputStream getInputStream() throws IOException {  
  2.             URLConnection con = this. url.openConnection();  
  3.             ResourceUtils. useCachesIfNecessary(con);  
  4.              try {  
  5.                    return con.getInputStream();  
  6.             }  
  7.              catch (IOException ex) {  
  8.                    // Close the HTTP connection (if applicable).  
  9.                    if (con instanceof HttpURLConnection) {  
  10.                         ((HttpURLConnection) con).disconnect();  
  11.                   }  
  12.                    throw ex;  
  13.             }  
  14.  }  

 

 UrlResource 封装了java.net.URL,它能够被用来访问任何通过URL可以获得的对象,例如:文件、HTTP对象、FTP对象等。

 

所有的URL都有个标准的 String表示,这些标准前缀可以标识不同的URL类型,包括file:访问文件系统路径,http: 通过HTTP协议访问的资源,ftp: 通过FTP访问的资源等等。

 

在Spring配置文件中,我们只需要给出字符串类型的path即可,Spring会通过ResourceEditor(java.beans.PropertyEditor的子类)和ResourceLoader把给定的path转换为相应的Resource。

 

转换规则如下:


Spring的Resource接口与ResourceLoader接口理解


  2.  通过ResourceLoader获取资源

       在Spring里面还定义有一个ResourceLoader接口,该接口中只定义了一个用于获取Resource的getResource(String location)方法。它的实现类有很多,这里我们先挑一个DefaultResourceLoader来讲。DefaultResourceLoader在获取Resource时采用的是这样的策略:首先判断指定的location是否含有“classpath:”前缀,如果有则把location去掉“classpath:”前缀返回对应的ClassPathResource;否则就把它当做一个URL来处理,封装成一个UrlResource进行返回;如果当成URL处理也失败的话就把location对应的资源当成是一个ClassPathResource进行返回。

Java代码  Spring的Resource接口与ResourceLoader接口理解
  1. @Test  
  2. public void testResourceLoader() {  
  3.    ResourceLoader loader = new DefaultResourceLoader();  
  4.    Resource resource = loader.getResource("http://www.google.com.hk");  
  5.    System.out.println(resource instanceof UrlResource); //true  
  6.    //注意这里前缀不能使用“classpath*:”,这样不能真正访问到对应的资源,exists()返回false  
  7.    resource = loader.getResource("classpath:test.txt");  
  8.    System.out.println(resource instanceof ClassPathResource); //true  
  9.    resource = loader.getResource("test.txt");  
  10.    System.out.println(resource instanceof ClassPathResource); //true  
  11. }  

 

       ApplicationContext接口也继承了ResourceLoader接口,所以它的所有实现类都实现了ResourceLoader接口,都可以用来获取Resource。

       对于ClassPathXmlApplicationContext而言,它在获取Resource时继承的是它的父类DefaultResourceLoader的策略。

       FileSystemXmlApplicationContext也继承了DefaultResourceLoader,但是它重写了DefaultResourceLoader的getResourceByPath(String path)方法。所以它在获取资源文件时首先也是判断指定的location是否包含“classpath:”前缀,如果包含,则把location中“classpath:”前缀后的资源从类路径下获取出来,当做一个ClassPathResource;否则,继续尝试把location封装成一个URL,返回对应的UrlResource;如果还是失败,则把location指定位置的资源当做一个FileSystemResource进行返回。

 

3       在bean中获取Resource的方式

       通过上面内容的介绍,我们知道,在bean中获取Resource主要有以下几种方式:

       1.直接通过new各种类型的Resource来获取对应的Resource。

       2.在bean里面获取到对应的ApplicationContext,再通过ApplicationContext的getResource(String path)方法获取对应的Resource。

     3.直接创建DefaultResourceLoader的实例,再调用其getResource(String location)方法获取对应的Resource。

       4.通过依赖注入的方式把Resource注入到bean中。示例如下:

 

类ClassA:

Java代码  Spring的Resource接口与ResourceLoader接口理解
  1. public class ClassA {  
  2.    
  3.    //持有一个Resource属性  
  4.    private Resource resource;  
  5.     
  6.    public void printContent() {  
  7.       if (resource != null && resource.exists()) {  
  8.          if (resource.isReadable()) {  
  9.             InputStream is;  
  10.             try {  
  11.                 is = resource.getInputStream();  
  12.                 BufferedReader br = new BufferedReader(new InputStreamReader(is));  
  13.                 String line;  
  14.                 while ((line=br.readLine()) != null) {  
  15.                    System.out.println(line);  
  16.                 }  
  17.                 if (is != null) {  
  18.                    is.close();  
  19.                 }  
  20.                 if (br != null) {  
  21.                    br.close();  
  22.                 }  
  23.             } catch (IOException e) {  
  24.                 e.printStackTrace();  
  25.             }  
  26.          }  
  27.       }  
  28.    }  
  29.     
  30.    public void setResource(Resource resource) {  
  31.       this.resource = resource;  
  32.    }  
  33.     
  34. }  

 

applicationContext.xml文件:

Xml代码  Spring的Resource接口与ResourceLoader接口理解
  1. <?xml version="1.0" encoding="UTF-8"?>   
  2. <beans xmlns="http://www.springframework.org/schema/beans"   
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"   
  4.     xsi:schemaLocation="http://www.springframework.org/schema/beans   
  5.          http://www.springframework.org/schema/beans/spring-beans-3.0.xsd   
  6.          http://www.springframework.org/schema/context   
  7.          http://www.springframework.org/schema/context/spring-context-3.0.xsd">   
  8.    
  9.    <bean id="classA" class="com.xxx.ClassA">  
  10.       <property name="resource">  
  11.          <value>classpath:applicationContext.xml</value>  
  12.       </property>  
  13.    </bean>  
  14.    
  15. </beans>  

 

       从上面可以看到我们有一个类ClassA,其持有一个Resource属性,在Spring bean配置文件中我们直接给ClassA注入了属性resource。其对应的测试代码如下:

Java代码  Spring的Resource接口与ResourceLoader接口理解
  1. @RunWith(SpringJUnit4ClassRunner.class)  
  2. @ContextConfiguration("classpath:applicationContext.xml")  
  3. public class Test1 {  
  4.    
  5.    @Autowired  
  6.    private ClassA classA;  
  7.     
  8.    @Test  
  9.    public void test() {  
  10.       classA.printContent();  
  11.    }  
  12.     
  13. }