ResourceLoader 主要是用于返回(即加载) Resource 对象,主要定义:
public interface ResourceLoader { /** Pseudo URL prefix for loading from the class path: "classpath:". */ String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX; /** * 返回指定路径的资源处理器 * 必须支持完全限定的网址: "file:C:/test.dat" * 必须支持ClassPath 的 URL :"classpath:test.dat" * 必须支持相对路径 : "WEB-INF/test.dat" * 并不能保证资源是否物理存在,需要自己去检测通过existence * 再spring中所有的应用上下文都去实现这个接口,可以进行资源的加载 */ Resource getResource(String location); /** * 返回当前类的 ClassLoader 对象 */ @Nullable ClassLoader getClassLoader(); }
应用上下文即容器都有实现 ResourceLoader 这个接口,所有的上下文都可以用于获取 Resource 实例对象
我们可以在特定的应用上下文中通过 getResource() 来获取特定类型的 Resource 实例,但是的保证 location 路径没有特殊的前缀,如 classpatch: 等,如果有特定前缀慢么会强制使用相应的资源类型,与上下文无关。
Prefix Example Explanationclasspath: classpath:com/myapp/config.xml 从类路径加载
file: file:///data/config.xml 从文件系统作为 URL 加载
http: https://myserver/logo.png 按照URL形式加载
(none) /data/config.xml 取决于应用上下文
ResourceLoader 的子类结构:
3.1 DefaultResourceLoader这个类是 ResourceLoader 的默认实现类,与 Resource 接口的 AbstractResource 一样,
3.1.1. 构造函数提供有参和无参的构造函数,有参构造函数接受 ClassLoader 类型,如不带参数则使用默认的 ClassLoader , Thread.currentThread()#getContextClassLoader()
核心代码代码,部分省去:
public class DefaultResourceLoader implements ResourceLoader { @Nullable private ClassLoader classLoader; private final Set<ProtocolResolver> protocolResolvers = new LinkedHashSet<>(4); private final Map<Class<?>, Map<Resource, ?>> resourceCaches = new ConcurrentHashMap<>(4); /** * 无参构造函数 * @see java.lang.Thread#getContextClassLoader() */ public DefaultResourceLoader() { this.classLoader = ClassUtils.getDefaultClassLoader(); } /** * 带ClassLoader的有参构造函数 */ public DefaultResourceLoader(@Nullable ClassLoader classLoader) { this.classLoader = classLoader; } /** * 设置 ClassLoader */ public void setClassLoader(@Nullable ClassLoader classLoader) { this.classLoader = classLoader; } /** * Return the ClassLoader to load class path resources with.、 * @see ClassPathResource */ @Override @Nullable public ClassLoader getClassLoader() { return (this.classLoader != null ? this.classLoader : ClassUtils.getDefaultClassLoader()); } /** * Obtain a cache for the given value type, keyed by {@link Resource}. * @param valueType the value type, e.g. an ASM {@code MetadataReader} * @return the cache {@link Map}, shared at the {@code ResourceLoader} level * @since 5.0 */ @SuppressWarnings("unchecked") public <T> Map<Resource, T> getResourceCache(Class<T> valueType) { return (Map<Resource, T>) this.resourceCaches.computeIfAbsent(valueType, key -> new ConcurrentHashMap<>()); } /** * Clear all resource caches in this resource loader. * @since 5.0 * @see #getResourceCache */ public void clearResourceCaches() { this.resourceCaches.clear(); } } 3.1.2 getResource() 核心方法是 ResourceLoader 中最核心的方法,他根据传入的 location 来返回相应的Resource,而 DefaultResourceLoader 对其做了核心实现, 子类都没覆盖该方法,所以我们可以断定 ResourceLoader 加载资源的核心策略都在 DefaultResourceLoader 中
核心代码:
//DefaultResourceLoader.java @Override public Resource getResource(String location) { Assert.notNull(location, "Location must not be null"); //1. 通过 ProtocolResolver 协议解析器来记载资源 for (ProtocolResolver protocolResolver : getProtocolResolvers()) { Resource resource = protocolResolver.resolve(location, this); if (resource != null) { return resource; } } //2.如果location是以 / 开头则返回 ClassPathContextResource 类型的 资源 if (location.startsWith("http://www.likecs.com/")) { return getResourceByPath(location); }//3.如果是以 classpath: 开头,则返回 ClassPathResource 类型的资源 else if (location.startsWith(CLASSPATH_URL_PREFIX)) { return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader()); } else { try { //4.如果不是以上两种,则判断是否是 File URL ,如果是返回FileUrlResource 否则 返回UrlResource // 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. //5.最后则返回ClassPathContextResource return getResourceByPath(location); } } }