天天看點

010-Spring 資源Resource接口Resource常見Resource直接使用Resource對象ResourceLoaderResourcePatternResolver接口使用注解來擷取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("https://blog.csdn.net/forlinkext/article/details/118727625");
            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("https://blog.csdn.net/forlinkext/article/details/118727625");
        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*:支援通配符比對,而且可以掃描不同位置的多個同名資源。

上一篇:009-Spring IoC 國際化(i18n)

下一篇:待續

繼續閱讀