三、资源访问

Spring框架大量使用了Resource来访问底层资源。
Resource接口提供的方法:

  • getInputStream():定位打开资源,返回资源对应的输入流,每次调用都返回新的输入流,调用者必须负责关闭输入流。
  • exists():返回Resource所指的资源是否存在。
  • isOpen():返回资源文件是否打开,如果资源文件不能多次读取,每次读取结束时应该显示关闭,防止资源泄露。
  • getDescription():返回资源的描述星星,用于资源处理出错时输出该信息,通常是全限定名或实际URL。
  • getFile():返回资源对应的File对象。
  • getURL:返回资源对应的URL对象。

Resource实现类:

Spring提供的Resource接口的大量实现类:

  • UrlResource:访问网络资源的实现类。
  • ClassPathResource:访问类加载路径里资源的实现类。
  • FileSystemResource:访问文件系统里资源的实现类。
  • ServletContextResource:访问相对于ServletContext路径下的资源的实现类。
  • InputStreamResource:访问输入流资源的实现类。
  • ByteArrayResource:访问字节数组资源的实现类。

1、访问网络资源:

通过UrlResource类实现。

package test;

import java.net.MalformedURLException;

import org.springframework.core.io.UrlResource;

public class UrlResourceTest {

    public static void main(String[] args) throws Exception{
        //创建对象,指定从文件系统读取资源
        UrlResource ur=new UrlResource("file:books.xml");
        
        //获取资源的简单信息
        System.out.println(ur.getFilename());
        System.out.println(ur.getDescription());
    }
    
}

通过使用file:前缀可访问本地磁盘资源,如果需要访问网络资源,可以使用如下两个前缀:

  • http:用于访问基于HTTP协议的网络资源。
  • ftp:用于访问基于FTP的网络资源

2、访问类加载路径下的资源:

ClassPathResource用来访问类加载路径下的资源,相对于其他的Resource实现类,其主要优势是方便访问类加载路径下的资源,尤其对web应用,ClassPathResource可自动搜索位于WEB-INF/classes下的资源文件,无需使用绝对路径访问。

public class ClassPathResourceTest{
    public static void main(String[] args){
        //创建一个Resource对象,从类加载路径下读取资源
        ClassPathResource cr=new ClassPathResource("books.xml");
        //获取资源的简单信息。
        ..........
    }
}

ClassPathResource实例可使用ClassPathResource实例可使用ClassPathResource构造器显示地创建,但更多时候它都是隐式创建的。当执行Spring的某个方法时,该方法接受一个代表资源路径的字符串参数,当Spring识别该字符串参数中包含classpath:前缀后,系统将会自动创建ClassPathResource对象。

3、访问文件系统的资源:

public class ClassPathResourceTest{
    public static void main(String[] args){
        //默认从文件系统的当前路径加载books.xml资源
        FileSystemResource fr=new FileSystemResource("books.xml");
        //获取资源的简单信息。
        ..........
    }
}

与前两种使用Resource进行资源访问的区别在于:资源字符串确定的资源,位于本地系统类,而且无需使用任何前缀。

4、访问应用相关资源:

public class ClassPathResourceTest{
    public static void main(String[] args){
        //从WEB Context下的WEB-INF路径下读取books.xml资源
        ServletContextResource src=new ServletContextResource("WEB-INF/books.xml");
        //获取资源的简单信息。
        ..........
    }
}

当程序试图直接通过File来访问Web Context下相对路径下的资源时,应该先使用ServletContext的getRealPath()方法来取得资源的绝对路径,再以该绝对路径来创建File对象。

5、访问字节数组资源:

InputStreamResource用于访问二进制输入流资源,InputStreamResource是一个总是被打开的Resource,所以isOpen()方法总是返回true。如果需要多次读取某个流,就不要使用InputStreamResource,创建InputStreamResource应该提供一个InputStream参数。InputStreamResource虽然是适应性很广的的Resource实现,但效率不好,而应尽量使用ByteArrayResource或FileSystemResource代替它。对于需要采用InputStreamResource访问的资源,可先从InputStream流中读出字节数组,然后以字节数组来创建ByteArrayResource。

ResourceLoader接口和ResourceLoaderAware接口:

Spring提供如下两个标志行接口:

  • ResourceLoader:该接口实现类的实例可以获得一个Resource实例。
  • ResourceLoaderAware:该接口实现类的实例可以获得一个ResourceLoader的引用。

ResourceLoader接口里的方法:

  • Resource getResource(String location):该接口仅包含这个方法,该方法用于返回一个Resource实例。ApplicationContext的实现类都实现ResourceLoader接口,因此ApplicationContext可用于直接获取Resource实例。

某个ApplicationContext实例获取Resource实例时,默认采用与ApplicationContext相同的资源访问策略。

//通过ApplicationContext访问资源
Resource res=ctx.getResource("some/resource/path/myTemple.txt");

从上面的代码中无法确定Spring用哪个实现类来访问指定的资源,Spring采用和ApplicationContext相同的策略来访问资源。如果ApplicationContext是FileSystemXmlApplicationContext,res就是FileSystemResource实例;如果ApplicationContext是ClassPathXmlApplicationContext,res就是ClassSystemResource实例;如果ApplicationContext是XmlWebApplicationContext,res就是ServletContextResource实例;

package test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.Resource;

public class ResourceLoaderTest {

    //创建ApplicationContext实例
    ApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml");
    //res是ClassPathResource实例
    Resource res=ctx.getResource("books.xml");
    //获取资源的简单信息
    ......
}

也可以不理会ApplicationContext的实现类,强制使用指定的ClassPathResource、FileSystemResource等实现类,这可通过不同的前缀来指定。

//通过classpath:前缀,强制使用ClassPathResource
Resource res=ctx.getResource("classpath:beans.xml");

常见的前缀及对应的访问策略:

  • classpath:以ClassPathResource实例访问类加载路径下的资源。
  • file:以UrlResource实例访问本地文件系统的资源。
  • http:以UrlResource实例访问基于HTTP协议的网络资源。
  • 无前缀:有ApplicationContext的是眼泪来决定访问策略。

ResourceLoaderAware完全类似于Spring提供的BeanFactoryAware、BeanNameAware几口,ResourceLoaderAware也提供了一个setResourceLoader()方法,该方法将由Spring容器负责调用,Spring容器将一个ResourceLoader对象作为该方法的参数传入。如果把实现ResourceLoaderAware接口的Bean类部署在Spring容器中,Spring容器会将自身当成ResourceLoader作为setResourceLoader()方法的参数传入。由于ApplicationContext的实现类都实现了ResourceLoader接口,Spring容器本身完全可以作为ResourceLoader使用。

如下的Bean类实现了ResourceLoaderAware接口:

package test;

import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.ResourceLoader;

public class TestBean implements ResourceLoaderAware {

    private ResourceLoader rd;
    /*
     * 实现ResourceLoaderAware接口必须实现该方法
     * 如果把该Bean部署在Spring容器中,该方法将会由Spring容器负责调用
     * Spring容器调用该方法时,Spring会将自身作为参数传给该方法
     * */
    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        // TODO Auto-generated method stub
        this.rd=resourceLoader;
    }
    
    //返回ResourceLoader对象的引用
    public ResourceLoader getResourceLoader(){
        return rd;
    }

}

将该类部署到AppliactionContext中,然后使用如下主程序调用:

package test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ResourceLoader;

public class SpringTest {

    public static void main(String[] args) {
        //创建ApplicationContext容器
        ApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml");
        //获取容器中名为test的Bean实例
        TestBean tb=ctx.getBean("test", TestBean.class);
        //通过tb实例来获取ResourceLoader实例
        ResourceLoader r1=tb.getResourceLoader();
        //判断程序获得的ResourceLoader和容器是否相同
        System.out.println(r1==ctx);
    }
}

输出true,表明Spring容器确实是将自身注入到ResourceLoaderAware Bean中。

使用Resource作为属性:

当引用程序中的Bean实例需要访问资源时,Spring有更好的解决方法:直接利用依赖注入。如果Bean实例需要访问资源,有如下两种解决方案:

  • 在代码中获取Resource实例。
  • 使用依赖注入。

当程序获取Resource实例时,总需要提供Resource所在的位置,资源所在的物理位置将被耦合到代码中,如果资源位置发生改变,则必须改写程序。因此,通常采用第二种方法,让Spring为Bean实例依赖注入资源。

public class TestBean{
   private Resource res
   //res的setter方法
   public void setRes(Resource res){
       this.res=res;
    }
   ........
}

采用依赖注入,允许动态配置资源位置,无需将资源文件位置写入代码中,当资源文件位置发生变化时,直接修改配置文件即可。

在ApplicationContext中使用资源:

ApplicationContext确定资源访问策略通常有两种方法:

  • 使用ApplicationContext实现类指定访问策略。
  • 使用前缀指定访问策略。

1、使用ApplicationContext实现类指定访问策略:

  • ClassPathXmlApplicationContext:对应使用ClassPathResource进行资源访问。
  • FileSystemXmlApplicationContext:对应使用FileSystemResource进行资源访问。
  • XmlWebApplicationContext:对应使用ServletContextResource进行资源访问。

2、使用前缀指定访问策略:

Spring通过使用http:、ftp:、classpath:等前缀来确定对应资源的访问策略。
如果程序需要使用ApplicationContext访问资源,建议显式采用对应的实现类来加载配置文件,而不是通过前缀来指定资源文件策略。

public class SpringTest{
   public static void main(String[] args){
      //通过类加载路径下的资源访问创建ApplicationContext,
      //因为使用了classpath:前缀强制搜索类加载路径
      ApplicationContext ctx=new FileSystemXmlApplicationContext("classpath:beans.xml");
      System.out.println(ctx);
      //使用ApplicationContext加载资源,通过classpath:前缀指定访问策略
      Resource r=ctx.getResource("classpath:books.xml");
      //输出Resource描述
      System.out.println(r.getDescription());
   }
}

输出:

class path resource [books.xml]

由此可见,如果每次进行资源访问时都指定前缀,则系统会采用前缀相应的资源访问策略。

3、classpath*:前缀的用法:

classpath:前缀提供加载多个XML配置文件的能力,当使用classpath:前缀来指定XML配置文件时,系统将搜索类加载路径,找出所有与文件名匹配的文件,分别加载文件中的配置定义,最后合并成一个ApplicationContext。

public class SpringTest{
    public static void main(String[] args){
        //使用classpath*:加载多个配置文件
        ApplicationContext ctx=new FileSystemXmlApplicationContext("classpath*:beans.xml");
       System.out.println(ctx);
    }
}

如果不采用classpath:前缀,而是改用classpath:前缀,Spring只按搜索路径加载一个符合条件的XML文件。classpath:前缀仅对ApplicationContext有效。实际情况时,创建ApplicationContext时,分别访问多个配置文件(通过ClassLoader的getResource方法实现)。因此,classpath:前缀不可用与Resource,使用classpath:前缀一次性访问多个资源是行不通的。**
通过配置文件时指定通配符也可以一次加载多个配置文件:

ApplicationContext ctx=new ClassPathXmlApplicationContext("beans*.xml");

也可以将classpath:前缀和通配符结合使用:*

ApplicationContext ctx=new ClassPathXmlApplicationContext("classpath*:beans*.xml");

4、file:前缀的用法:

public class SpringTest{
    public static void main(String[] args){
         //采用相对路径的写法
         ApplicationContext ctx=new FileSystemXmlApplicationContext("beans.xml");
         //采用绝对路径的写法
         ApplicationContext ctx=new FileSystemXmlApplicationContext("/beans.xml");
         System.out.println(ctx);
    }
}

当FileSystemXmlApplicationContext作为ResourceLoader使用时,它会发生变化,FileSystemApplicationContext会简单的让所有绑定的FileSystemResource实例把绝对路径当成相对路径来处理。

ApplicationContext ctx=new FileSystemXmlApplicationContext("file:beans.xml");
ApplicationContext ctx=new FileSystemXmlApplicationContext("file:/beans.xml");

上面第一条语句访问相对路径下的beans.xml,第二条路径访问绝对路径下的beans.xml。相对路径以当前工作路径为起点,绝对路径以文件系统根路径为路径起点。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,601评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,748评论 6 342
  • spring官方文档:http://docs.spring.io/spring/docs/current/spri...
    牛马风情阅读 1,648评论 0 3
  • 上周看了潘煜班长非常精彩的教书分享,《早起不难,最难的是你改变习惯的勇气》,早起,属于自己的时间,思考,自律,健康...
    凯荔阅读 285评论 0 3
  • new shop~
    小船same阅读 181评论 0 0