package org.springframework.core.io;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils;
/**
* {@link ResourceLoader}接口的默认实现。
*
* <p>由{@link ResourceEditor}使用,并作为{@link org.springframework.context.support.AbstractApplicationContext}的基类。
* 也可以单独使用。
*
* <p>如果位置值是URL,将返回一个{@link UrlResource},
* 如果它是非URL路径或“classpath:”伪URL,则将返回{@link ClassPathResource}。
*
* @author Juergen Hoeller
* @since 10.03.2004
* @see FileSystemResourceLoader
* @see org.springframework.context.support.ClassPathXmlApplicationContext
*/
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);
/**
* 创建一个新的DefaultResourceLoader。
* <p>ClassLoader访问将在实际资源访问时使用线程上下文类加载器进行(自5.3)。
* 要获得更多控制,请将特定的ClassLoader传递给{@link#DefaultResourceLoader(ClassLoader)}。
* @see java.lang.Thread#getContextClassLoader()
*/
public DefaultResourceLoader() {
}
/**
* 创建一个新的DefaultResourceLoader。
* @param classLoader 用于加载类路径资源的ClassLoader, or {@code null}
* 或{@code null}时使用在实际访问资源时的线程上下文类加载器。
*/
public DefaultResourceLoader(@Nullable ClassLoader classLoader) {
this.classLoader = classLoader;
}
/**
* 指定用于加载类路径资源的ClassLoader,
* 或{@code null}时使用在实际访问资源时的线程上下文类加载器。
* <p>默认情况下,ClassLoader访问将在实际资源访问时使用线程上下文类加载器(自5.3)。
*/
public void setClassLoader(@Nullable ClassLoader classLoader) {
this.classLoader = classLoader;
}
/**
* 返回ClassLoader用以加载类路径资源。
* <p>将此资源加载器创建的所有ClassPathResource对象传递给ClassPathResource的构造函数。
* @see ClassPathResource
*/
@Override
@Nullable
public ClassLoader getClassLoader() {
return (this.classLoader != null ? this.classLoader : ClassUtils.getDefaultClassLoader());
}
/**
* 使用此资源加载器注册给定的解析器,以便处理其他协议。
* <p>任何此类解析器都将在此加载器的标准解析规则之前被调用。因此,它也可能覆盖任何默认规则。
* @since 4.3
* @see #getProtocolResolvers()
*/
public void addProtocolResolver(ProtocolResolver resolver) {
Assert.notNull(resolver, "ProtocolResolver must not be null");
this.protocolResolvers.add(resolver);
}
/**
* 返回当前注册的协议解析器的集合,允许自检和修改。
* @since 4.3
* @see #addProtocolResolver(ProtocolResolver)
*/
public Collection<ProtocolResolver> getProtocolResolvers() {
return this.protocolResolvers;
}
/**
* Obtain a cache for the given value type, keyed by {@link Resource}.
* 获取以{@link Resource}为键的给定值类型的缓存。
* @param valueType 值类型,例如,ASM {@code MetadataReader}
* @return 缓存{@link Map},在{@code ResourceLoader}级别共享。
* @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<>());
}
/**
* 清除此资源加载器中的所有资源缓存。
* @since 5.0
* @see #getResourceCache
*/
public void clearResourceCaches() {
this.resourceCaches.clear();
}
/**
* 返回指定资源位置的{@code Resource}句柄。
*/
@Override
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
//协议解析器查找到Resource,直接返回
for (ProtocolResolver protocolResolver : getProtocolResolvers()) {
Resource resource = protocolResolver.resolve(location, this);
if (resource != null) {
return resource;
}
}
if (location.startsWith("/")) {
return getResourceByPath(location);
}
else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
try {
// Try to parse the location as a URL...
URL url = ResourceUtils.toURL(location);
return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
}
catch (MalformedURLException ex) {
// No URL -> resolve as resource path.
return getResourceByPath(location);
}
}
}
/**
* 返回给定路径上资源的Resource句柄。
* <p>默认实现支持类路径位置。 这应该适用于独立实现,但可以被覆盖,
* 例如用于针对Servlet容器的实现。
* @param path Resource的路径
* @return 相应的Resource句柄
* @see ClassPathResource
* @see org.springframework.context.support.FileSystemXmlApplicationContext#getResourceByPath
* @see org.springframework.web.context.support.XmlWebApplicationContext#getResourceByPath
*/
protected Resource getResourceByPath(String path) {
return new ClassPathContextResource(path, getClassLoader());
}
/**
* ClassPathResource 通过实现ContextResource接口显式地表示上下文相关路径。
*/
protected static class ClassPathContextResource extends ClassPathResource implements ContextResource {
public ClassPathContextResource(String path, @Nullable ClassLoader classLoader) {
super(path, classLoader);
}
@Override
public String getPathWithinContext() {
return getPath();
}
@Override
public Resource createRelative(String relativePath) {
String pathToUse = StringUtils.applyRelativePath(getPath(), relativePath);
return new ClassPathContextResource(pathToUse, getClassLoader());
}
}
}
【spring-core】DefaultResourceLoader
于 2025-01-10 11:10:43 首次发布