(造轮子)手写Spring框架-资源和资源加载器

139 阅读2分钟

资源和资源加载器

代码路径:github.com/WangChao-ly…

建议订阅博主专栏,从下到上系统手写spring源码,体会其中过程!

本代码实现了spring中资源路径加载需求

  1. 资源加载总共分三种方法进行加载,一种是classpath:,一种是文件系统,还有一种是URL资源定位方式,我们需要为这三个类编写一个抽象的资源接口,对外展示该接口,不涉及具体的实现。编写Resource接口如下:
/**
 * 资源的抽象和访问接口
 * @author WangChao
 * @date 2023/6/17 19:53
 */
public interface Resource {
    InputStream getInputStream() throws IOException;
}

以下三种方法都是显示了该接口,为具体的实现类:

  • classpath下的资源,编写ClassPathResource如下,该类是通过类加载器获取类路径下的资源文件的输入流:
public class ClassPathResource implements Resource{
    private final String path;

    public ClassPathResource(String path){
        this.path = path;
    }

    @Override
    public InputStream getInputStream() throws IOException {
        InputStream is = this.getClass().getClassLoader().getResourceAsStream(this.path);
        if(is == null){
            throw new FileNotFoundException(this.path + " cannot be opened because it does not exist");
        }
        return is;
    }
}
  • 文件系统下的资源,先通过File类的toPath方法获取Path类实例,再通过File类的newInputStream方法获取到文件的输入流:
public class FileSystemResource implements Resource{
    private final String path;

    public FileSystemResource(String path){
        this.path = path;
    }

    @Override
    public InputStream getInputStream() throws IOException {
        try{
            Path path = new File(this.path).toPath();
            return Files.newInputStream(path);
        }catch (NoSuchFileException ex){
            throw new FileNotFoundException(ex.toString());
        }
    }
}
  • URL下的资源,该方法通过得到URLConnection类来获取文件输入流,具体代码如下:
public class UrlResource implements Resource {
    private final URL url;

    public UrlResource(URL url){
        this.url = url;
    }

    @Override
    public InputStream getInputStream() throws IOException {
        URLConnection urlConnection = this.url.openConnection();
        try{
            return urlConnection.getInputStream();
        }catch (IOException ex) {
            throw ex;
        }
    }
}
  1. 编写资源加载器接口,该接口是用来获取Resource对象,从而获取path路径对应的资源文件输入流:
public interface ResourceLoader {
    Resource getResource(String location);
}
  • 编写该接口的实现类,通过解析localtion变量,决定使用哪种资源加载器来加载资源文件,代码如下:
public class DefaultResourceLoader implements ResourceLoader{
    public static final String CLASSPATH_URL_PREFIX = "classpath:";

    @Override
    public Resource getResource(String location) {
        if(location.startsWith(CLASSPATH_URL_PREFIX)){
            //classpath下的资源
            return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()));
        }else{
            try {
                //尝试当成url来处理
                URL url = new URL(location);
                return new UrlResource(url);
            }catch (MalformedURLException e){
                //当成文件系统下的资源处理
                return new FileSystemResource(location);
            }
        }
    }
}
  1. 总结测试:
public class ResourceAndResourceLoaderTest {
    @Test
    public void testResourceLoader() throws Exception {
        DefaultResourceLoader resourceLoader = new DefaultResourceLoader();

        //加载classpath下的资源
        Resource resource = resourceLoader.getResource("classpath:hello.txt");
        InputStream inputStream = resource.getInputStream();
        String content = IoUtil.readUtf8(inputStream);
        System.out.println(content);
        Assert.equals(content,"hello world!");

        //加载文件系统资源
        resource = resourceLoader.getResource("src/test/resources/hello.txt");
        Assert.isInstanceOf(FileSystemResource.class,resource);
        inputStream = resource.getInputStream();
        content = IoUtil.readUtf8(inputStream);
        System.out.println(content);

        //加载url资源
        resource = resourceLoader.getResource("https://www.baidu.com");
        Assert.isInstanceOf(UrlResource.class,resource);
        inputStream = resource.getInputStream();
        content = IoUtil.readUtf8(inputStream);
        System.out.println(content);
    }
}