在 Java 中,将不一样来源的资源抽象成 URL
,经过注册不一样的 handler
( URLStreamHandler
) 来处理不一样来源的资源的读取逻辑。java
然而 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,该接口只有一个方法 getInputStream网络
public interface InputStreamSource { /** * 表示任意形式的资源均可以被转换成输入流、最好每一次调用这个方法的时候都是返回一个新的InputStream * 看子类{@link FileSystemResource}符合这个要求 */ InputStream getInputStream() throws IOException; }
Resource 接口提供了比 URL 更加多和便捷的方法app
对于不一样来源的资源文件都有相应的 Resource 实现框架
其中 AbstractResource 实现了 Resource 接口中大多数的方法,若是咱们要自定义 Resource ,能够继承这个类,而不是直接实现 Resource ide
org.springframework.core.io.ResourceLoader
是 Spring 资源加载的抽象this
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(); }
主要实现类就是 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); }
在 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 对应着不一样的资源加载策略
在 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); }
这个不就是策略模式吗
相关文章