Spring实现了自己的资源加载策略
- 职能划分,资源的定义和资源的加载要有一个清晰的界限
- 统一的抽象,统一的资源定义和资源加载策略。
统一的资源:Resource
-
为 Spring 框架所有资源的抽象和访问接口,它继承org.springframework.core.io.Resource
接口。作为所有资源的统一抽象,Resource 定义了一些通用的方法,由子类org.springframework.core.io.InputStreamSource
提供统一的默认实现AbstractResource
IoC之Spring统一资源加载策略 - 从上图可以看到,Resource 根据资源的不同类型提供不同的具体实现,如下:
- FileSystemResource :对
类型资源的封装,只要是跟 File 打交道的,基本上与 FileSystemResource 也可以打交道。支持文件和 URL 的形式,实现 WritableResource 接口,且从 Spring Framework 5.0 开始,FileSystemResource 使用 NIO2 API进行读/写交互。java.io.File
- ByteArrayResource :对字节数组提供的数据的封装。如果通过 InputStream 形式访问该类型的资源,该实现会根据字节数组的数据构造一个相应的 ByteArrayInputStream。
- UrlResource :对
类型资源的封装。内部委派 URL 进行具体的资源操作。java.net.URL
- ClassPathResource :class path 类型资源的实现。使用给定的 ClassLoader 或者给定的 Class 来加载资源。
- InputStreamResource :将给定的 InputStream 作为一种资源的 Resource 的实现类
统一的资源加载:ResourceLoader
- ResourceLoader提供了统一的抽象,具体的实现由相应的子类来负责实现,类结构图如下:
IoC之Spring统一资源加载策略 - ResourceLoader 中最核心的方法是:
Resource getResource(String location);
- DefaultResourceLoader对getResource提供了默认实现,代码如下:
@Override public Resource getResource(String location) { Assert.notNull(location, "Location must not be null"); for (ProtocolResolver protocolResolver : this.protocolResolvers) { Resource resource = protocolResolver.resolve(location, this); if (resource != null) { return resource; } } // 如果是"/"开头, 那么就通过ClassPathContextResource if (location.startsWith("/")) { return getResourceByPath(location); } else if (location.startsWith(CLASSPATH_URL_PREFIX)) { //如果以classpath为开头,那么就通过ClassPathResource return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader()); } //然后,根据是否为文件 URL ,是则返回 FileUrlResource 类型的资源,否则返回 UrlResource 类型的资源 else { try { // Try to parse the location as a URL... URL url = new URL(location); return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url)); } catch (MalformedURLException ex) { // No URL -> resolve as resource path. return getResourceByPath(location); } } }
protected Resource getResourceByPath(String path) { return new ClassPathContextResource(path, getClassLoader()); }
public ClassPathResource(String path, @Nullable ClassLoader classLoader) { Assert.notNull(path, "Path must not be null"); String pathToUse = StringUtils.cleanPath(path); if (pathToUse.startsWith("/")) { pathToUse = pathToUse.substring(1); } this.path = pathToUse; this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader()); }
protected Resource getResourceByPath(String path) { return new ClassPathContextResource(path, getClassLoader()); }
- ResourceLoader的getResource(String location)每次只能根据location返回一个Resource资源,当需要加载多个资源的时候,就可以使用ResourcePartternResolver,它支持根据指定的资源路径匹配模式每次返回多个Resource实例
小结:
- Spring 提供了 Resource 和 ResourceLoader 来统一抽象整个资源及其定位。使得资源与资源的定位有了一个更加清晰的界限,并且提供了合适的 Default 类,使得自定义实现更加方便和清晰。
- AbstractResource 为 Resource 的默认抽象实现,它对 Resource 接口做了一个统一的实现,子类继承该类后只需要覆盖相应的方法即可,同时对于自定义的 Resource 我们也是继承该类。
- DefaultResourceLoader 同样也是 ResourceLoader 的默认实现,在自定 ResourceLoader 的时候我们除了可以继承该类外还可以实现 ProtocolResolver 接口来实现自定资源加载协议。
- DefaultResourceLoader 每次只能返回单一的资源,所以 Spring 针对这个提供了另外一个接口 ResourcePatternResolver ,该接口提供了根据指定的 locationPattern 返回多个资源的策略。其子类 PathMatchingResourcePatternResolver 是一个集大成者的 ResourceLoader ,因为它即实现了
方法,也实现了Resource getResource(String location)
方法。Resource[] getResources(String locationPattern)
ps:该文为芋道源码学习笔记
转载于:https://my.oschina.net/u/4055223/blog/3098311