0
点赞
收藏
分享

微信扫一扫

010-Spring 资源Resource接口


Resource

接口定义如下

package org.springframework.core.io;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import org.springframework.lang.Nullable;

public interface Resource extends InputStreamSource {
    boolean exists();

    default boolean isReadable() {
        return this.exists();
    }

    default boolean isOpen() {
        return false;
    }

    default boolean isFile() {
        return false;
    }

    URL getURL() throws IOException;

    URI getURI() throws IOException;

    File getFile() throws IOException;

    default ReadableByteChannel readableChannel() throws IOException {
        return Channels.newChannel(this.getInputStream());
    }

    long contentLength() throws IOException;

    long lastModified() throws IOException;

    Resource createRelative(String var1) throws IOException;

    @Nullable
    String getFilename();

    String getDescription();
}

如果你读过Spring源码,那么对Resource接口你就不会陌生,特别在资源加载环节,到处是Resource的身影。

常见Resource

  • UrlResource:包装了java.net.URL,可以访问通过URL访问的任何对象。比如:文件、Http链接地址、FTP等等。
  • ClassPathResource:从类路径中获取的资源。它使用线程上下文类加载器、给定的类加载器或给定的类来加载资源。
  • FileSystemResource:支持file:///开头的读取系统文件(如:file:///c:/work/aaa.txt)

直接使用Resource对象

@Test
    public void testUrlResource(){
        try {
            // 读取CSDN博客文章
            UrlResource urlResource = new UrlResource("");
            print(urlResource);
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testClassPathResource() {
        // 读取resource目录下(classPath根路径)的test.properties文件
        ClassPathResource classPathResource = new ClassPathResource("test.properties");
        print(classPathResource);
    }

    @Test
    public void testFileSystemResource() {
        // 读取系统文件c:/work/aaa.txt
        FileSystemResource classPathResource = new FileSystemResource("c:/work/aaa.txt");
        print(classPathResource);
    }

    public void print(Resource resource) {
        try (BufferedReader r = new BufferedReader(new InputStreamReader(resource.getInputStream()))){
            String str;
            while ((str = r.readLine()) != null) {
                System.out.println(str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

如果aaa.txt文件中包含中文,我们的代码打印可能会出现乱码,请使用InputStreamReader的带编码参数的构造函数来指定编码格式。

注:打印代码中我们使用了try-with-resouce机制(jdk1.7版本及以上才支持)

ResourceLoader

接口定义如下

package org.springframework.core.io;

import org.springframework.lang.Nullable;

public interface ResourceLoader {
    String CLASSPATH_URL_PREFIX = "classpath:";

    Resource getResource(String var1);

    @Nullable
    ClassLoader getClassLoader();
}

ResourceLoader定义了如何获取Resource,它支持一些特定的前缀,如"classpath:"等。

我们前面是有的ApplicationContext都实现了该接口。意味着我们可以通过context.getResource方法来获取我们想要的Resource对象。

@Test
    public void ctxTest1() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
        Resource resource = context.getResource("");
        System.out.println(resource.getClass());// UrlResource
        print(resource);
    }

    @Test
    public void ctxTest2() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
        Resource resource = context.getResource("file:///c:/work/aaa.txt");
        System.out.println(resource.getClass()); // FileUrlResource
        print(resource);
    }

    @Test
    public void ctxTest3() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
        Resource resource = context.getResource("test.properties");
        System.out.println(resource.getClass()); // DefaultResourceLoader$ClassPathContextResource
        print(resource);
    }
    
    @Test
    public void ctxTest4() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
        Resource resource = context.getResource("classpath:test.properties");
        System.out.println(resource.getClass()); // ClassPathResource
        print(resource);
    }

可以看到我们使用了同样的方法,但是却得到了不同的Resource实现,根据我们的String参数的不同得到的Resource实现也不同。我们示例中有https开头、file开头、classpath:开头,对应得到的Resource实现也不一样。这是由PropertyEditor最终决定的。PropertyEditor我们将在后续讲解。

ResourcePatternResolver接口

public interface ResourcePatternResolver extends ResourceLoader {

    String CLASSPATH_ALL_URL_PREFIX = "classpath*:";

    Resource[] getResources(String locationPattern) throws IOException;
}

上面实例中我们使用了classpath:,ResourcePatternResolver接口中定义了calasspath*:,它们有什么区别吗。我们先来看看如下的demo

@Test
    public void ctxTest5() throws IOException {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
        Resource[] resource = context.getResources("classpath*:test.properties");
        System.out.println(resource[0].getClass()); // UrlResource
        print(resource[0]);
    }

    @Test
    public void ctxTest6() throws IOException {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
        Resource[] resource = context.getResources("classpath*:*.properties");
        if(resource != null) {
            System.out.println(resource.length);
            for(int i = 0 ; i < resource.length;i++) {
                System.out.println("========================");
                System.out.println(resource[i].getFilename());
                System.out.println(resource[i].getClass());
                print(resource[i]);
            }
        }
    }

注意:

  1. classpath*:,需要使用getResources方法
  2. getResource方法使用classpath*:将会报错
  3. classpath*:可以获取在依赖的jar包中的资源
  4. classpath*:可以与通配符共用来实现模糊匹配,如classpath:spring-*.properties
  5. classpath:不能与*通配符共用。

使用注解来获取Resource对象

package com.yyoo.boot.annotation;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;

@Component
public class MyResourceBean {
    
    @Value("${my.resource.path}")
    public Resource[] resources;
    
    public Resource[] getResources(){
        return this.resources;
    }
    
}

注意:我们的示例配置my.resource.path = classpath*:test.properties,如果路径是classpath*:,则需要使用Resource[] 数组接收,否则会报错。

注意:将Resource这里的资源获取跟@PropertySource注解导入配置区分开来。Resource可以不止是Properties配置文件,还可以是其他任何的https、ftp或本地、classpath路径下的任意文件。

总结

我们在使用中尽量不要自己new Resource,通过ApplicationContext的getResource或getResources方法获取,或者直接使用@Value注解配合配置文件来实现。
classpath*:支持通配符匹配,而且可以扫描不同位置的多个同名资源。

举报

相关推荐

0 条评论