BeanDefinition是怎么来的?
发现流程图中涉及到了4个主要的接口,齐心协力之下,把配置信息变成了我们熟悉的Bean定义,来学习一下吧:
- ReourceLoader:用于加载资源(如XML配置文件)
- Resource: 表示加载的资源
- BeanDefinitionReader:读取资源中的bean定义
- BeanDefinitionRegistry:用于注册和管理bean定义
在上一篇文章中,我们学习了Resource接口,知道了可以通过实现类的构造方法来获取一个Resource
对象从而对资源进行访问。今天我们学习一下怎么通过ResourceLoader
接口来获取Ressource
对象。
问题:ResourceLoader 得到Resource对象和使用Resource构造函数来得到Resource对象有什么不同,ResourceLoader 的优势在哪儿?
资源加载器:ReourceLoader
ResourceLoader 是 Spring 框架中的一个接口,定义了一个加载资源的策略。它提供了一种统一的方式来访问各种不同类型的资源,例如文件系统中的文件、类路径资源、URL 资源等。
ResourceLoader 接口源码
public interface ResourceLoader {
String CLASSPATH_URL_PREFIX = "classpath:";
根据资源的路径获取资源对象
Resource getResource(String location);
返回用于加载类的ClassLoader
ClassLoader getClassLoader();
}
ResourceLoader 使用案例
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.DefaultResourceLoader;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class ResourceLoaderExample {
public static void main(String[] args) {
ResourceLoader resourceLoader = new DefaultResourceLoader();
// 加载类路径资源
Resource classPathResource = resourceLoader.getResource("classpath:config.properties");
printResourceContent(classPathResource);
// 加载文件系统资源
Resource fileSystemResource = resourceLoader.getResource("file:/path/to/file.txt");
printResourceContent(fileSystemResource);
// 加载 URL 资源
Resource urlResource = resourceLoader.getResource("http://example.com/resource.txt");
printResourceContent(urlResource);
}
private static void printResourceContent(Resource resource) {
try (InputStream inputStream = resource.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
我们发现,不管访问什么类型的资源,都可以通过getResource()
方法实现,该方法会通过入参的不同前缀(classpath:
、file:
、http:
)来选择使用正确的Resource实现类(),这就是统一接口
!
resourceLoader.getResource 策略模式
public class DefaultResourceLoader implements ResourceLoader {
@Override
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
// 处理前缀
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 {
// 尝试将位置解析为URL
URL url = new URL(location);
return new UrlResource(url);
} catch (MalformedURLException ex) {
// 不是URL, 那么就作为文件路径
return getResourceByPath(location);
}
}
}
如果getResource()入参没有“前缀”,默认使用文件系统资源
protected Resource getResourceByPath(String path) {
return new FileSystemResource(path);
}
@Override
@Nullable
public ClassLoader getClassLoader() {
return (this.classLoader != null ? this.classLoader : ClassUtils.getDefaultClassLoader());
}
}
看来DefaultResourceLoader只支持三种类型资源:文件系统资源、类路径资源、URl资源
问题:ResourceLoader 得到Resource对象和使用Resource构造函数来得到Resource对象有什么不同,ResourceLoader 的优势在哪儿?
答案:
Resource需要自己选择实现类,而通过ResourceLoader可以通过策略者模式,根据前缀选择合适的算法,适合加载多种资源类型的场景。如果没有前缀默认使用文件系统资源来获取Resource。
相比Resource自己选择实现类,ResourceLoader优势:
- 统一接口:提供一个统一的接口来加载不同类型的资源。
- 高度扩展性:可以通过自定义实现来支持更多类型的资源。
- 便捷性:内置了处理类路径资源、文件系统资源和 URL 资源的能力,适用于大多数常见的场景。
ResourceLoader 常用实现类
-
DefaultResourceLoader
- 默认实现类,能够处理各种资源位置前缀,如classpath:、file:、http:等。 FileSystemResourceLoader
- 扩展DefaultResourceLoader,用于加载文件系统中的资源。 ServletContextResourceLoader
- 用于在 Web 环境中加载资源,资源路径相对于 Web 应用的根目录。 WebApplicationContext
- 扩展自ApplicationContext,它也是一个ResourceLoader,能够加载Web应用环境中的资源。
ResourceLoader 为什么已经有DefaultResourceLoader,还会有一个FileSystemResourceLoader
不同点及使用场景:
-
DefaultResourceLoader
- 多用途:适用于需要加载多种类型资源的场景。通过前缀可以方便地指定资源类型。
- 自动解析:可以根据前缀自动选择合适的 Resource 实现类,简化了资源加载代码。
- 可扩展性:能够扩展以支持更多类型的资源。
-
FileSystemResourceLoader
- 专用性:适用于只需要加载文件系统资源的场景。它避免了不必要的类型检查和解析。
- 简化实现:因为只处理文件系统资源,代码更为简洁。
其实DefaultResourceLoader、FileSystemResourceLoader主要还是看我们怎么使用,如果是多种资源类型的加载,就使用DefaultResourceLoader,如果只是针对文件系统的资源加载,使用FileSystemResourceLoader更加简介。