BeanDefinition是怎么来的?
发现流程图中涉及到了4个主要的接口,齐心协力之下,把配置信息变成了我们熟悉的Bean定义,来学习一下吧:
- ReourceLoader:用于加载资源(如XML配置文件)
- Resource: 表示加载的资源
- BeanDefinitionReader:读取资源中的bean定义
- BeanDefinitionRegistry:用于注册和管理bean定义
什么是资源(Resource)?
在计算机科学和软件工程中,资源
通常指的是程序或系统需要访问和使用的外部数据或文件。这些资源可以以多种形式存在,包括但不限于文件系统中的文件、数据库中的数据、网络上的远程文件或服务、类路径中的文件以及内存中的数据。这些资源可以包含配置文件、图像、文档、脚本、音频文件、视频文件等。
-
1. 类路径资源 (Classpath Resource)
- 描述:这些资源位于应用程序的类路径中,通常用于加载配置文件、静态资源等。例如,applicationContext.xml位于类路径中,可以通过classpath:applicationContext.xml进行访问。 2.文件系统资源 (File System Resource)
- 描述:这些资源位于文件系统中,可以通过绝对路径或相对路径进行访问。例如,/path/to/file.txt位于文件系统中,可以通过file:/path/to/file.txt进行访问。 3. URL资源 (URL Resource)
- 描述:这些资源通过URL进行访问,通常用于加载网络上的远程资源。例如,http://example.com/resource.txt位于网络上,可以通过http://example.com/resource.txt进行访问。 4. 字节数组资源 (Byte Array Resource)
- 描述:这些资源使用字节数组表示,可以直接在内存中操作。例如,使用new ByteArrayResource(byteArray)创建一个字节数组资源。 5. 输入流资源 (Input Stream Resource)
- 描述:这些资源使用输入流表示,可以直接从输入流中读取数据。例如,使用new InputStreamResource(inputStream)创建一个输入流资源。 6. ServletContext资源 (ServletContext Resource)
- 描述:这些资源位于Web应用的ServletContext中,通常用于加载Web应用中的配置文件或静态资源。例如,/WEB-INF/web.xml位于ServletContext中。
Resource实现类
资源类型 | 实现类 | 示例路径 |
---|---|---|
1. 类路径资源 | ClassPathResource | classpath:applicationContext.xml |
2. 文件系统资源 | FileSystemResource | file:/path/to/file.txt |
3. Url资源 | UrlResource | http://example.com/resource.txt |
4. 字节数组资源 | ByteArrayResource | new ByteArrayResource(byteArray) |
5. 输入流资源 | InputStreamResource | new InputStreamResource(inputStream) |
6. ServletContext资源 | ServletContextResource | /WEB-INF/web.xml |
统一接口:Resource
Resource
接口为这些不同类型的资源提供了统一的操作方法。
"统一接口"
说的就是:无论资源类型如何,开发者都可以使用相同的方法来检查资源的存在性、读取资源内容等。
public interface Resource extends InputStreamSource {
资源是否存在
boolean exists();
资源是否可读
boolean isReadable();
资源是否已打开
boolean isOpen();
1. FileSystemResource:返回实例:file:/path/to/file.txt(文件系统资源)
2. UrlResource:返回实例:http://example.com/resource.txt(URL资源)
3. ByteArrayResource:返回实例:抛出FileNotFoundException异常
URL getURL() throws IOException; 获取资源的URL
URI getURI() throws IOException; 获取资源的URI
获取资源对应的文件对象
File getFile() throws IOException;
获取资源的内容长度
long contentLength() throws IOException;
获取资源的最后修改时间
long lastModified() throws IOException;
根据相对路径创建相对于此资源的资源
Resource createRelative(String relativePath) throws IOException;
获取资源的文件名
String getFilename();
描述资源的内容或位置
虽然方法名中包含了“描述”,但它并不会实际读取资源的内容或从资源中提取字符串。
描述内容是指资源的类型和位置信息,而不是资源内部的数据内容。
String getDescription();
}
方法说明:isOpen
资源是否已打开:打开资源意味着打开一个输入流来读取其内容。
观察:
类 | getInputStream()返回结果 | isOpen()返回结果 |
---|---|---|
FileSystemResource | 每次返回一个新对象 | false |
ClassPathResource | 每次返回一个新对象 | false |
ByteArrayResource | 每次返回一个新对象 | false |
InputStreamResource | 每次返回封装的对象 | true |
… | … | … |
问题:
- FileSystemResource的getInputStream()方法、InputStreamResource的getInputStream()方法有什么不同吗?
- 为什么调用getInputStream()方法之后,有些isOpen返回false,有些返回true?
解答:
先看看FileSystemResource 的源码:
public class FileSystemResource extends AbstractResource {
private final File file;
public FileSystemResource(File file) {
this.file = file;
}
每次都是返回一个新的FileInputStream,且下面的isOpen永远返回false
@Override
public InputStream getInputStream() throws IOException {
return new FileInputStream(this.file);
}
isOpen永远返回true!
@Override
public boolean isOpen() {
return false;
}
// 其他方法...
}
FileSystemResource:代表一个文件系统中的资源。每次调用getInputStream方法时,它都会创建一个新的FileInputStream实例。因此,资源的打开状态不依赖于FileSystemResource实例,而是依赖于每次调用时创建的新的输入流。所以isOpen方法必须返回false,因为资源的打开状态与FileSystemResource的实例无关。
再看看InputStreamResource 的源码:
public class InputStreamResource extends AbstractResource {
private final InputStream inputStream;
private boolean read = false;
public InputStreamResource(InputStream inputStream) {
this.inputStream = inputStream;
}
这里是依赖本身的,且会在getInputStream时候将read 设置为true
@Override
public InputStream getInputStream() {
this.read = true;
return this.inputStream;
}
默认为flase,调用getInputStream变为true
@Override
public boolean isOpen() {
return this.read;
}
// 其他方法...
}
InputStreamResource:直接包装一个现有的InputStream。每次调用getInputStream方法时,它都是返回包装的同一个InputStream,依赖于自己,并且在第一次调用getInputStream之后,read改变为true,因此isOpen会返回true。
方法说明:getURI | getURL
- getURI() 会返回一个用于标识资源的 URI;
- getURL() 通常会返回一个可以用来定位资源的 URL,表示一个可访问的地址;
- 只要getURL方法能返回一个URL,那就是可以访问的URL,否则,方法会抛出异常。类似于getFile、getInputStream都是返回一个可访问的对象;
观察:
对象 | getURL返回结果 | getURI返回结果 | 说明 |
---|---|---|---|
FileSystemResource | file:/path/to/file.txt | file:/path/to/file.txt | 文件系统资源URL |
UrlResource | http://example.com/resource.txt | http://example.com/resource.txt | URL资源 |
ClassPathResource | file:/path/to/your/project/target/classes/application.properties | file:/path/to/your/project/target/classes/application.properties | 类路径资源 |
ServletContextResource | file:/path/to/your/servlet/context/resource | 抛出异常 | ServletContext资源 |
ByteArrayResource | 抛出异常 | 抛出异常 | 字节数组资源 |
InputStreamResource | 抛出异常 | 抛出异常 | 输入流资源 |
URL是什么
- 统一资源定位符
- 侧重点:指向一个 可以访问 的资源,所以包含了访问资源的必要信息(如http、https、ftp等),以及资源的位置(例如域名和路径)
- URL示例:http://example.com/resource
URI 是什么?
- 统一资源标识符
- 侧重点:用于标识一个资源。URI 可以是一个 URL 或者 URN
使用案例
项目结构:
src/
└── main/
└── resources/
└── config.properties
config.properties 文件内容:
app.name=MyApp
app.version=1.0.0
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.util.Properties;
public class ClassPathResourceExample {
public static void main(String[] args) {
// 创建 ClassPathResource 对象,指向类路径中的 config.properties 文件
Resource resource = new ClassPathResource("config.properties");
// 调用所有 Resource 接口的方法
try {
System.out.println("资源是否存在: " + resource.exists());
System.out.println("资源是否可读: " + resource.isReadable());
System.out.println("资源是否打开: " + resource.isOpen());
URL url = resource.getURL();
System.out.println("资源 URL: " + url);
URI uri = resource.getURI();
System.out.println("资源 URI: " + uri);
File file = resource.getFile();
System.out.println("资源文件信息: " + file.getAbsolutePath());
long contentLength = resource.contentLength();
System.out.println("资源内容长度: " + contentLength);
long lastModified = resource.lastModified();
System.out.println("资源最后修改时间: " + lastModified);
//createRelative(): 创建一个相对于当前资源路径的新资源对象
Resource relativeResource = resource.createRelative("anotherConfig.properties");
System.out.println("创建一个相对于当前资源路径的新资源对象: " + relativeResource.getDescription());
String filename = resource.getFilename();
System.out.println("资源文件名: " + filename);
// getDescription()
String description = resource.getDescription();
System.out.println("资源描述信息: " + description);
// 读取文件内容
try (InputStream inputStream = resource.getInputStream()) {
Properties properties = new Properties();
properties.load(inputStream);
// 输出读取到的配置内容
System.out.println("App Name: " + properties.getProperty("app.name"));
System.out.println("App Version: " + properties.getProperty("app.version"));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
getInputStream、getFile、getURL选择
-
getInputStream()
对文件的内容进行操作。提供一种通用的方式读取各种类型的资源,而不关心资源的实际存储位置。
-
getFile()
对文件进行操作,比如:文件复制、移动、删除等。 只有当资源实际存储在文件系统中时才能使用。如果资源不是文件系统中的文件,例如位于 JAR 包中的文件或网络资源,调用该方法会抛出异常。
-
getURL()
适用于需要将资源作为 URL 处理的场景,例如通过网络访问资源、传递资源的 URL 等。可以用于网络通信、资源定位等。
构造函数参数:绝对路径
- 文件的完整路径,从文件系统的根目录开始。例如:
- Windows:
C:\Users\username\Documents\file.txt
- Linux:
/home/username/Documents/file.txt
- Windows:
绝对路径:以 / 开头的路径被视为绝对路径,直接指向文件系统中的位置。例如,/data/file.txt
会被解析为根目录下的 data/file.txt
(在 Unix/Linux 系统中)。
构造函数参数:相对路径
相对路径:没有以 / 开头的路径被视为相对于当前工作目录的路径。例如,data/file.txt
会被解析为当前工作目录下的data/file.txt
。
什么叫 “当前工作目录” ?
工作目录:是指 Java 虚拟机(JVM)启动时的目录。这通常是运行 Java 命令的目录!例如:
包所在目录 | 命令运行目录 | 运行命令 | 工作目录 |
---|---|---|---|
/home/project | /home/project | java -jar myapp.jar | /home/project |
/home/project | /home | java -jar /home/project/myapp.jar | /home/project |
“工作目录” = “包所在目录”
构造函数参数:没有“前缀”
在没有前缀的情况下,路径处理的方式取决于具体使用的 Resource 实现类。
- FileSystemResource:用于文件系统中的文件,当传递相对路径时,它会解释为相对于当前工作目录的路径。
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
public class FileSystemResourceExample {
public static void main(String[] args) {
Resource resource = new FileSystemResource("data/file.txt");
printResourceDetails(resource);
}
private static void printResourceDetails(Resource resource) {
try {
System.out.println("File exists: " + resource.exists());
System.out.println("URL: " + resource.getURL());
System.out.println("File: " + resource.getFile());
} catch (IOException e) {
e.printStackTrace();
}
}
}
-
ClassPathResource:用于类路径中的资源,当传递相对路径时,它会解释为相对于类路径的路径。
如果你传递
com/example/config.properties
,ClassPathResource 会在类路径下的com/example目录
中寻找这个文件。
src
└── main
└── resources
├── config.properties
└── com
└── example
└── config.properties
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
public class ClassPathResourceExample {
public static void main(String[] args) {
Resource resource = new ClassPathResource("com/example/config.properties");
printResourceDetails(resource);
}
private static void printResourceDetails(Resource resource) {
try {
System.out.println("File exists: " + resource.exists());
System.out.println("URL: " + resource.getURL());
// Note: getFile() might not work for resources in JAR files
} catch (IOException e) {
e.printStackTrace();
}
}
}
- UrlResource
当我们说“相对于当前 URL”的时候,这通常是指一个 URL 基于另一个 URL 的路径来解析。在 Web 开发中,相对路径是基于当前页面的 URL 来解析的,但在 Java 中使用 UrlResource 时,必须明确指定完整的 URL 或相对路径。
import org.springframework.core.io.UrlResource;
import org.springframework.core.io.Resource;
import java.io.IOException;
import java.net.MalformedURLException;
public class UrlResourceExample {
public static void main(String[] args) {
try {
// 创建一个绝对 URL 的资源
Resource resource = new UrlResource("http://example.com/folder/resource.html");
printResourceDetails(resource);
// 如果你尝试使用相对路径,通常需要与一个基 URL 结合使用
// 例如,构建一个新的 URL
URL baseUrl = new URL("http://example.com/");
URL relativeUrl = new URL(baseUrl, "otherpage.html");
Resource relativeResource = new UrlResource(relativeUrl);
printResourceDetails(relativeResource);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
private static void printResourceDetails(Resource resource) {
try {
System.out.println("Resource exists: " + resource.exists());
System.out.println("Resource URL: " + resource.getURL());
System.out.println("Resource Content Length: " + resource.contentLength());
} catch (IOException e) {
e.printStackTrace();
}
}
}
详细说明
-
绝对 URL:
Resource resource = new UrlResource(“http://example.com/resource.txt”);
这个构造函数直接使用绝对 URL,UrlResource 会尝试访问这个 URL 指向的资源。 -
相对 URL:
在Web开发中,相对路径是基于当前页面的URL来解析的。例如,如果你在浏览 http://example.com/folder/resource.html,并且页面上有一个相对链接 …/otherpage.html,这个链接会被解析为 http://example.com/otherpage.html。