美文网首页框架学习
三、资源访问

三、资源访问

作者: 数独题 | 来源:发表于2017-03-07 17:12 被阅读65次

    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。相对路径以当前工作路径为起点,绝对路径以文件系统根路径为路径起点。

    相关文章

      网友评论

        本文标题:三、资源访问

        本文链接:https://www.haomeiwen.com/subject/zhrugttx.html