Spring-资源加载(源码分析)

在 Java 中,将不一样来源的资源抽象成 URL ,经过注册不一样的 handler ( URLStreamHandler ) 来处理不一样来源的资源的读取逻辑。web

然而 URL 没有默认定义相对 Classpath 或 ServletContext 等资源的 handler ,虽然能够注册本身的 URLStreamHandler 来解析特定的 URL 前缀(协议)。可是 URL 也没有提供基本的方法、如检查当前资源是否存在,检查资源是否存在等方法。面试

URL: 我能够加载各类的资源.......XXXspring

Spring: 你是个好人设计模式

Spring 实现了本身的资源加载策略数组

  • 职能划分清楚。资源的定义和资源的加载有明确的界限
  • 统一的抽象,统一的资源定义和资源加载策略。

统一资源

org.springframework.core.io.Resource 接口抽象了全部 Spring 内部使用到的底层资源,如 File、URL、Classpath。网络

public interface Resource extends InputStreamSource {
 .......  ....... } 复制代码

org.springframework.core.io.InputStreamSource 封装任何能返回 InputStream 的类、如 File、Classpath 下的资源和 ByteArray,该接口只有一个方法 getInputStreamapp

public interface InputStreamSource {
  /**  * 表示任意形式的资源均可以被转换成输入流、最好每一次调用这个方法的时候都是返回一个新的InputStream  * 看子类{@link FileSystemResource}符合这个要求  */  InputStream getInputStream() throws IOException;  } 复制代码

Resource 接口提供了比 URL 更加多和便捷的方法框架

idea 截图
idea 截图

对于不一样来源的资源文件都有相应的 Resource 实现编辑器

  • 文件 FileSystemResource
  • classpath 资源 ClassPathResource
  • url 资源 URLResource
  • InputStream 资源 InputStreamResource
  • byte 数组资源 ByteArrayResource
idea 类图
idea 类图

其中 AbstractResource 实现了 Resource 接口中大多数的方法,若是咱们要自定义 Resource ,能够继承这个类,而不是直接实现 Resourceide

idea 截图
idea 截图

统一资源加载

org.springframework.core.io.ResourceLoader 是 Spring 资源加载的抽象

public interface ResourceLoader {
 /**  * Pseudo URL prefix for loading from the class path: "classpath:".  */  String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;  /**  * 根据 资源路径 返回一个Resource 句柄、但不确保这个Resource 必定存在、须要调用 Resource exists 方法 * 判断  * URL位置资源,如”file:C:/test.dat”  * ClassPath位置资源,如”classpath:test.dat”  * 相对路径资源,如”WEB-INF/test.dat”,此时返回的Resource实例根据实现不一样而不一样  *  * 要可重用、可屡次调用 getInputStream  */  Resource getResource(String location);  @Nullable  ClassLoader getClassLoader();  } 复制代码
idea 类图
idea 类图

主要实现类就是 DefaultResourceLoader ,其中最核心的方法就是 getResource

@Override
 public Resource getResource(String location) {  Assert.notNull(location, "Location must not be null");   // 协议解释  for (ProtocolResolver protocolResolver : getProtocolResolvers()) {  Resource resource = protocolResolver.resolve(location, this);  if (resource != null) {  return resource;  }  }   // 若是以/ 开头、则建立 ClassPathContextResource、其实也是一个 ClassPathResource 是他的子类、 类路径的资源  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());  } 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);  }  }  } 复制代码

其中 ProtocolResolver

@FunctionalInterface
public interface ProtocolResolver {   @Nullable  Resource resolve(String location, ResourceLoader resourceLoader); } 复制代码

主要用于处理用户自定义资源协议的,咱们能够经过实现此接口来解释咱们自定义的资源协议,只须要将实现类对象添加到 DefaultResourceLoader

public void addProtocolResolver(ProtocolResolver resolver) {
 Assert.notNull(resolver, "ProtocolResolver must not be null");  this.protocolResolvers.add(resolver);  } 复制代码

ResourcePatternResolver

在 ResourceLoader 接口的基础上增长了 Resource[] getResources(String locationPattern) 方法,支持根据指定的路径匹配模式每次返回多个 Resource 实例。

@Override
 public Resource[] getResources(String locationPattern) throws IOException {  Assert.notNull(locationPattern, "Location pattern must not be null");  // classpath*:  if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {  // a class path resource (multiple resources for same name possible)  // 路径中包含通配符  if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {  // a class path resource pattern  return findPathMatchingResources(locationPattern);  }  else {  // all class path resources with the given name  return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));  }  }  else {  // Generally only look for a pattern after a prefix here,  // and on Tomcat only after the "*/" separator for its "war:" protocol.  int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 :  locationPattern.indexOf(':') + 1);  if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {  // a file pattern  return findPathMatchingResources(locationPattern);  }  else {  // a single resource with the given name  return new Resource[] {getResourceLoader().getResource(locationPattern)};  }  }  } 复制代码

涉及到的设计模式

面试的时候

面试官: 看过什么框架的源码吗

菜鸡的我: 我看过 Spring 的源码

面试官: 那你说说 Spring 中用到了什么设计模式

菜鸡的我: 额....好像...有....我忘记了

责任链模式

在资源定义和资源加载这一块中,咱们能够看到在 DefaultResourceLoader 中的 ProtocolResolver,

private final Set<ProtocolResolver> protocolResolvers = new LinkedHashSet<>(4);
 public Resource getResource(String location) {  Assert.notNull(location, "Location must not be null");   // 协议解释  for (ProtocolResolver protocolResolver : getProtocolResolvers()) {  Resource resource = protocolResolver.resolve(location, this);  if (resource != null) {  return resource;  }  }  .....  ..... 复制代码

一个加载资源的请求,会被其中一个 ProtocolResolver 去解释成一个 Resource 或者没有一个 ProtocolResolver 能处理这个请求并返回一个 Resource (最终被后面的默认解析成Resource)。这一个过程其实算是使用到了责任链模式的,只是否是一个纯正的责任链模式。

图片来源于网络
图片来源于网络

阎宏博士的《JAVA与模式》一书中是这样描述责任链(Chain of Responsibility)模式的

责任链模式是一种对象的行为模式。在责任链模式里,不少对象由每个对象对其下家的引用而链接起来造成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪个对象最终处理这个请求,这使得系统能够在不影响客户端的状况下动态地从新组织和分配责任。

纯与不纯的责任链模式

  • 纯的责任链模式: 要么处理这个请求、要么彻底不处理这个请求并将这个请求传递给下一个处理器;不纯的责任链模式:某个处理器处理了一部分这个请求的业务,而后又将剩下的传递给下一个处理器让其继续处理
  • 纯的责任链模式: 一个请求必须被一个请求处理器所处理;不纯的责任链模式:这个请求可能没有被任何一个处理器处理

而对于怎么存储下一个 handler 的引用,固然能够在当前 handler 中存有下一个 handler 的引用,可是更加经常使用的仍是使用数组或者列表将全部 handler 存储起来,而后进行链式调用处理请求,如 servlet 的filter即便如此。

策略模式

不一样的 ResourceLoader 对应着不一样的资源加载策略

idea 类图
idea 类图

org.springframework.context.support.GenericApplicationContext 类中

public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
  private final DefaultListableBeanFactory beanFactory;   @Nullable  private ResourceLoader resourceLoader;  public void setResourceLoader(ResourceLoader resourceLoader) {  this.resourceLoader = resourceLoader;  }   @Override  public Resource getResource(String location) {  if (this.resourceLoader != null) {  return this.resourceLoader.getResource(location);  }  // 最终调用 DefaultResourceLoader 的 getResource  return super.getResource(location);  } 复制代码

这个不就是策略模式吗

图片来源自维基百科
图片来源自维基百科

相关文章

Spring 专辑

此次必定?
此次必定?

本文使用 mdnice 排版

相关文章
相关标签/搜索