当前位置:   article > 正文

spring学习(二)——资源获取_spring 中获取资源是啥意思

spring 中获取资源是啥意思

参考文章:

http://www.iocoder.cn/

 

资源描述:Resource 

统一的接口

  1. public interface Resource extends InputStreamSource {
  2. /**
  3. * 资源是否存在
  4. */
  5. boolean exists();
  6. /**
  7. * 资源是否可读
  8. */
  9. default boolean isReadable() {
  10. return exists();
  11. }
  12. /**
  13. * 是否打开
  14. */
  15. default boolean isOpen() {
  16. return false;
  17. }
  18. /**
  19. * 是否是文件
  20. */
  21. default boolean isFile() {
  22. return false;
  23. }
  24. /**
  25. * 返回资源的URL地址
  26. */
  27. URL getURL() throws IOException;
  28. /**
  29. * 返回资源的URI地址
  30. */
  31. URI getURI() throws IOException;
  32. /**
  33. * 获得资源的文件标识
  34. */
  35. File getFile() throws IOException;
  36. /**
  37. * byte读取通道
  38. */
  39. default ReadableByteChannel readableChannel() throws IOException {
  40. return Channels.newChannel(getInputStream());
  41. }
  42. /**
  43. * 资源长度
  44. */
  45. long contentLength() throws IOException;
  46. /**
  47. * 最后修改时间
  48. */
  49. long lastModified() throws IOException;
  50. /**
  51. * 根据资源的相对路径创建新资源
  52. */
  53. Resource createRelative(String relativePath) throws IOException;
  54. /**
  55. * 资源名称
  56. */
  57. @Nullable
  58. String getFilename();
  59. /**
  60. * 资源描述
  61. */
  62. String getDescription();
  63. }

类图

抽象类

org.springframework.core.io.AbstractResource ,为 Resource 接口的默认抽象实现。它实现了 Resource 接口的大部分的公共实现,自定义资源的时候,最好是继承AbstractResource而不是实现Resource

实现类

  • FileSystemResource :对 java.io.File 类型资源的封装,只要是跟 File 打交道的,基本上与 FileSystemResource 也可以打交道。支持文件和 URL 的形式,实现 WritableResource 接口,且从 Spring Framework 5.0 开始,FileSystemResource 使用 NIO2 API进行读/写交互。
  • ByteArrayResource :对字节数组提供的数据的封装。如果通过 InputStream 形式访问该类型的资源,该实现会根据字节数组的数据构造一个相应的 ByteArrayInputStream。
  • UrlResource :对 java.net.URL类型资源的封装。内部委派 URL 进行具体的资源操作。
  • ClassPathResource :class path 类型资源的实现。使用给定的 ClassLoader 或者给定的 Class 来加载资源。
  • InputStreamResource :将给定的 InputStream 作为一种资源的 Resource 的实现类。

 

资源加载:ResourceLoader 

统一接口

  1. public interface ResourceLoader {
  2. /** Pseudo URL prefix for loading from the class path: "classpath:". */
  3. // CLASSPATH URL 前缀。默认为:"classpath:"
  4. String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
  5. //根据所提供资源的路径 location 返回 Resource 实例
  6. Resource getResource(String location);
  7. @Nullable
  8. ClassLoader getClassLoader();
  9. }

类图

子类

  • DefaultResourceLoader:org.springframework.core.io.DefaultResourceLoader 是 ResourceLoader 的默认实现
  • FileSystemResourceLoader:它继承 DefaultResourceLoader ,且覆写了 #getResourceByPath(String) 方法,使之从文件系统加载资源并以 FileSystemResource 类型返回。
  • ClassRelativeResourceLoader: DefaultResourceLoader 的另一个子类的实现。和 FileSystemResourceLoader 类似,在实现代码的结构上类似,也是覆写 #getResourceByPath(String path) 方法,并返回其对应的 ClassRelativeContextResource 的资源类型
  • ResourcePatternResolver:ResourceLoader 的扩展,它支持根据指定的资源路径匹配模式每次返回多个 Resource 实例
  • PathMatchingResourcePatternResolver:除了支持 ResourceLoader 和 ResourcePatternResolver 新增的 "classpath*:" 前缀外,还支持 Ant 风格的路径匹配模式

实现类

DefaultResourceLoader

org.springframework.core.io.DefaultResourceLoader 是 ResourceLoader 的默认实现

构造函数

  1. /**
  2. * 使用的 ClassLoader 为默认的 ClassLoader(一般 Thread.currentThread()#getContextClassLoader() )
  3. */
  4. public DefaultResourceLoader() {
  5. this.classLoader = ClassUtils.getDefaultClassLoader();
  6. }
  7. /**
  8. * 在使用带参数的构造函数时,可以通过 ClassUtils#getDefaultClassLoader()获取。
  9. */
  10. public DefaultResourceLoader(@Nullable ClassLoader classLoader) {
  11. this.classLoader = classLoader;
  12. }

getResource 方法

ResourceLoader 中最核心的方法为 #getResource(String location) ,它根据提供的 location 返回相应的 Resource 。而 DefaultResourceLoader 对该方法提供了核心实现

  1. @Override
  2. public Resource getResource(String location) {
  3. Assert.notNull(location, "Location must not be null");
  4. // 首先,通过 ProtocolResolver 来加载资源
  5. for (ProtocolResolver protocolResolver : this.protocolResolvers) {
  6. Resource resource = protocolResolver.resolve(location, this);
  7. if (resource != null) {
  8. return resource;
  9. }
  10. }
  11. // 其次,以 / 开头,返回 ClassPathContextResource 类型的资源
  12. if (location.startsWith("/")) {
  13. return getResourceByPath(location);
  14. }
  15. // 再次,以 classpath: 开头,返回 ClassPathResource 类型的资源
  16. else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
  17. return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
  18. }
  19. // 然后,根据是否为文件 URL ,是则返回 FileUrlResource 类型的资源,否则返回 UrlResource 类型的资源
  20. else {
  21. try {
  22. // Try to parse the location as a URL...
  23. URL url = new URL(location);
  24. return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
  25. }
  26. catch (MalformedURLException ex) {
  27. // 最后,返回 ClassPathContextResource 类型的资源
  28. // No URL -> resolve as resource path.
  29. // location.startsWith("/") 其实又回到最初的判断了
  30. return getResourceByPath(location);
  31. }
  32. }
  33. }

ProtocolResolver

用户自定义协议资源解决策略,作为 DefaultResourceLoader 的 SPI:它允许用户自定义资源加载协议,而不需要继承 ResourceLoader 的子类

ProtocolResolver 接口,仅有一个方法 Resource resolve(String location, ResourceLoader resourceLoader)

  1. @Nullable
  2. Resource resolve(String location, ResourceLoader resourceLoader);

 

 

FileSystemResourceLoader

它继承 DefaultResourceLoader ,且覆写了 #getResourceByPath(String) 方法,使之从文件系统加载资源并以 FileSystemResource 类型返回,这样我们就可以得到想要的资源类型

  1. @Override
  2. protected Resource getResourceByPath(String path) {
  3. //截取第一个/分界线
  4. if (path.startsWith("/")) {
  5. path = path.substring(1);
  6. }
  7. 创建 FileSystemContextResource 类型的资源
  8. return new FileSystemContextResource(path);
  9. }

FileSystemContextResource

  1. /**
  2. * 实现 ContextResource 接口,并实现对应的 #getPathWithinContext() 接口方法
  3. */
  4. private static class FileSystemContextResource extends FileSystemResource implements ContextResource {
  5. public FileSystemContextResource(String path) {
  6. super(path);
  7. }
  8. @Override
  9. public String getPathWithinContext() {
  10. return getPath();
  11. }
  12. }

 

ClassRelativeResourceLoader

 DefaultResourceLoader 的另一个子类的实现。和 FileSystemResourceLoader 类似,在实现代码的结构上类似,也是覆写 #getResourceByPath(String path) 方法,并返回其对应的 ClassRelativeContextResource 的资源类型

ClassRelativeResourceLoader 扩展的功能是,可以根据给定的class 所在包或者所在包的子包下加载资源。

ResourcePatternResolver

ResourceLoader 的扩展,它支持根据指定的资源路径匹配模式每次返回多个 Resource 实例

  1. public interface ResourcePatternResolver extends ResourceLoader {
  2. /**
  3. * 新的路径前缀
  4. */
  5. String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
  6. /**
  7. * ResourcePatternResolver 在 ResourceLoader 的基础上增加了 #getResources(String locationPattern) 方法,
  8. * 以支持根据路径匹配模式返回多个 Resource 实例
  9. * @param locationPattern
  10. * @return
  11. * @throws IOException
  12. */
  13. Resource[] getResources(String locationPattern) throws IOException;
  14. }

PathMatchingResourcePatternResolver

除了支持 ResourceLoader 和 ResourcePatternResolver 新增的 "classpath*:" 前缀外,还支持 Ant 风格的路径匹配模式

 

构造函数

  1. /**
  2. * 可以指定一个 ResourceLoader,如果不指定的话,它会在内部构造一个 DefaultResourceLoader
  3. */
  4. private final ResourceLoader resourceLoader;
  5. /**
  6. * Ant 路径匹配器
  7. */
  8. private PathMatcher pathMatcher = new AntPathMatcher();
  9. /**
  10. * 使用默认资源加载
  11. */
  12. public PathMatchingResourcePatternResolver() {
  13. this.resourceLoader = new DefaultResourceLoader();
  14. }
  15. /**
  16. * 资源加载器
  17. */
  18. public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
  19. Assert.notNull(resourceLoader, "ResourceLoader must not be null");
  20. this.resourceLoader = resourceLoader;
  21. }
  22. /**
  23. * 类加载器
  24. */
  25. public PathMatchingResourcePatternResolver(@Nullable ClassLoader classLoader) {
  26. this.resourceLoader = new DefaultResourceLoader(classLoader);
  27. }

getResource 方法

  1. @Override
  2. public Resource getResource(String location) {
  3. return getResourceLoader().getResource(location);
  4. }

getResources 方法

  1. @Override
  2. public Resource[] getResources(String locationPattern) throws IOException {
  3. Assert.notNull(locationPattern, "Location pattern must not be null");
  4. //以classpath*: 开头
  5. if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
  6. // 路径包含通配符
  7. // a class path resource (multiple resources for same name possible)
  8. if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
  9. // a class path resource pattern
  10. return findPathMatchingResources(locationPattern);
  11. }
  12. // 路径不包含通配符
  13. else {
  14. // all class path resources with the given name
  15. return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
  16. }
  17. }
  18. // 不以 "classpath*:" 开头
  19. else {
  20. // 通常只在这里的前缀后面查找模式
  21. // Generally only look for a pattern after a prefix here,
  22. // Tomcat 上只有在 “*/ ”分隔符之后才为其 “war:” 协议
  23. // and on Tomcat only after the "*/" separator for its "war:" protocol.
  24. int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 :
  25. locationPattern.indexOf(':') + 1);
  26. // 路径包含通配符
  27. if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
  28. // a file pattern
  29. return findPathMatchingResources(locationPattern);
  30. }
  31. // 路径不包含通配符
  32. else {
  33. // a single resource with the given name
  34. return new Resource[] {getResourceLoader().getResource(locationPattern)};
  35. }
  36. }
  37. }

findAllClassPathResources

地址不含通配符的时候

  1. /**
  2. * 该方法返回 classes 路径下和所有 jar 包中的所有相匹配的资源。
  3. * "classpath*:" 开头但是不包含通配符
  4. */
  5. protected Resource[] findAllClassPathResources(String location) throws IOException {
  6. String path = location;
  7. if (path.startsWith("/")) {
  8. path = path.substring(1);
  9. }
  10. //真正执行加载的地方
  11. Set<Resource> result = doFindAllClassPathResources(path);
  12. if (logger.isTraceEnabled()) {
  13. logger.trace("Resolved classpath location [" + location + "] to resources " + result);
  14. }
  15. return result.toArray(new Resource[0]);
  16. }
  17. /**
  18. * Find all class location resources with the given path via the ClassLoader.
  19. * Called by {@link #findAllClassPathResources(String)}.
  20. * @param path the absolute path within the classpath (never a leading slash)
  21. * @return a mutable Set of matching Resource instances
  22. * @since 4.1.1
  23. */
  24. protected Set<Resource> doFindAllClassPathResources(String path) throws IOException {
  25. Set<Resource> result = new LinkedHashSet<>(16);
  26. //根据 ClassLoader 加载路径下的所有资源
  27. ClassLoader cl = getClassLoader();
  28. //如果当前父类加载器不为 null ,则通过父类向上迭代获取资源,否则调用 #getBootstrapResources()
  29. Enumeration<URL> resourceUrls = (cl != null ? cl.getResources(path) : ClassLoader.getSystemResources(path));
  30. while (resourceUrls.hasMoreElements()) {
  31. URL url = resourceUrls.nextElement();
  32. // 将 URL 转换成 UrlResource
  33. result.add(convertClassLoaderURL(url));
  34. }
  35. //加载所有jar包 传入的为/的时候
  36. if ("".equals(path)) {
  37. addAllClassLoaderJarRoots(cl, result);
  38. }
  39. return result;
  40. }

findPathMatchingResources

地址包含通配符的时候

  1. /**
  2. * 确定目录,获取该目录下得所有资源。
  3. * 在所获得的所有资源后,进行迭代匹配获取我们想要的资源。
  4. */
  5. protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
  6. // 确定根路径、子路径
  7. String rootDirPath = determineRootDir(locationPattern);
  8. String subPattern = locationPattern.substring(rootDirPath.length());
  9. //获取根路径下的资源
  10. Resource[] rootDirResources = getResources(rootDirPath);
  11. Set<Resource> result = new LinkedHashSet<>(16);
  12. //遍历
  13. for (Resource rootDirResource : rootDirResources) {
  14. rootDirResource = resolveRootDirResource(rootDirResource);
  15. URL rootDirUrl = rootDirResource.getURL();
  16. //bundle 资源类型
  17. if (equinoxResolveMethod != null && rootDirUrl.getProtocol().startsWith("bundle")) {
  18. URL resolvedUrl = (URL) ReflectionUtils.invokeMethod(equinoxResolveMethod, null, rootDirUrl);
  19. if (resolvedUrl != null) {
  20. rootDirUrl = resolvedUrl;
  21. }
  22. rootDirResource = new UrlResource(rootDirUrl);
  23. }
  24. // vfs 资源类型
  25. if (rootDirUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
  26. result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirUrl, subPattern, getPathMatcher()));
  27. }
  28. //jar资源类型
  29. else if (ResourceUtils.isJarURL(rootDirUrl) || isJarResource(rootDirResource)) {
  30. result.addAll(doFindPathMatchingJarResources(rootDirResource, rootDirUrl, subPattern));
  31. }
  32. //其他资源类型
  33. else {
  34. result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));
  35. }
  36. }
  37. if (logger.isTraceEnabled()) {
  38. logger.trace("Resolved location pattern [" + locationPattern + "] to resources " + result);
  39. }
  40. // 转换成 Resource 数组返回
  41. return result.toArray(new Resource[0]);
  42. }

 

determineRootDir

主要是用于确定根路径

  1. protected String determineRootDir(String location) {
  2. // 找到冒号的后一位
  3. int prefixEnd = location.indexOf(':') + 1;
  4. //根目录结束位置
  5. int rootDirEnd = location.length();
  6. // 在从冒号开始到最后的字符串中,循环判断是否包含通配符,如果包含,则截断最后一个由”/”分割的部分。
  7. // 例如:在我们路径中,就是最后的ap?-context.xml这一段。再循环判断剩下的部分,直到剩下的路径中都不包含通配符。
  8. while (rootDirEnd > prefixEnd && getPathMatcher().isPattern(location.substring(prefixEnd, rootDirEnd))) {
  9. rootDirEnd = location.lastIndexOf('/', rootDirEnd - 2) + 1;
  10. }
  11. // 如果查找完成后,rootDirEnd = 0 了,则将之前赋值的 prefixEnd 的值赋给 rootDirEnd ,也就是冒号的后一位
  12. if (rootDirEnd == 0) {
  13. rootDirEnd = prefixEnd;
  14. }
  15. //截图更目录
  16. return location.substring(0, rootDirEnd);
  17. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/66388
推荐阅读
相关标签
  

闽ICP备14008679号