Spring 源码第一篇开整!配置文件是怎么加载的?

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!

resources 目录下创建配置文件:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns=“http://www.springframework.org/schema/beans”

xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”

xsi:schemaLocation=“http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd”>

然后去加载这个配置文件:

public static void main(String[] args) {

XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource(“beans.xml”));

User user = factory.getBean(User.class);

System.out.println("user = " + user);

}

这里为了展示数据的读取过程,我就先用这个已经过期的 XmlBeanFactory 来加载,这并不影响我们阅读源码。

上面这个是一个非常简单的 Spring 入门案例,相信很多小伙伴在第一次接触 Spring 的时候,写出来的可能都是这个 Demo。

在上面这段代码执行过程中,首先要做的事情就是先把 XML 配置文件加载到内存中,再去解析它,再去。。。。。

一步一步来吧,先来看 XML 文件如何被加入到内存中去。

3.文件读取


文件读取在 Spring 中很常见,也算是一个比较基本的功能,而且 Spring 提供的文件加载方式,不仅仅在 Spring 框架中可以使用,我们在项目中有其他文件加载需求也可以使用。

首先,Spring 中使用 Resource 接口来封装底层资源,Resource 接口本身实现自 InputStreamSource 接口:

我们来看下这两个接口的定义:

public interface InputStreamSource {

InputStream getInputStream() throws IOException;

}

public interface Resource extends InputStreamSource {

boolean exists();

default boolean isReadable() {

return exists();

}

default boolean isOpen() {

return false;

}

default boolean isFile() {

return false;

}

URL getURL() throws IOException;

URI getURI() throws IOException;

File getFile() throws IOException;

default ReadableByteChannel readableChannel() throws IOException {

return Channels.newChannel(getInputStream());

}

long contentLength() throws IOException;

long lastModified() throws IOException;

Resource createRelative(String relativePath) throws IOException;

@Nullable

String getFilename();

String getDescription();

}

代码倒不难,我来稍微解释下:

  1. InputStreamSource 类只提供了一个 getInputStream 方法,该方法返回一个 InputStream,也就是说,InputStreamSource 会将传入的 File 等资源,封装成一个 InputStream 再重新返回。

  2. Resource 接口实现了 InputStreamSource 接口,并且封装了 Spring 内部可能会用到的底层资源,如 File、URL 以及 classpath 等。

  3. exists 方法用来判断资源是否存在。

  4. isReadable 方法用来判断资源是否可读。

  5. isOpen 方法用来判断资源是否打开。

  6. isFile 方法用来判断资源是否是一个文件。

  7. getURL/getURI/getFile/readableChannel 分别表示获取资源对应的 URL/URI/File 以及将资源转为 ReadableByteChannel 通道。

  8. contentLength 表示获取资源的大小。

  9. lastModified 表示获取资源的最后修改时间。

  10. createRelative 表示根据当前资源创建一个相对资源。

  11. getFilename 表示获取文件名。

  12. getDescription 表示在资源出错时,详细打印出出错的文件。

当我们加载不同资源时,对应了 Resource 的不同实现类,来看下 Resource 的继承关系:

可以看到,针对不同类型的数据源,都有各自的实现,我们这里来重点看下 ClassPathResource 的实现方式。

ClassPathResource 源码比较长,我这里挑一些关键部分来和大家分享:

public class ClassPathResource extends AbstractFileResolvingResource {

private final String path;

@Nullable

private ClassLoader classLoader;

@Nullable

private Class<?> clazz;

public ClassPathResource(String path) {

this(path, (ClassLoader) null);

}

public ClassPathResource(String path, @Nullable ClassLoader classLoader) {

Assert.notNull(path, “Path must not be null”);

String pathToUse = StringUtils.cleanPath(path);

if (pathToUse.startsWith(“/”)) {

pathToUse = pathToUse.substring(1);

}

this.path = pathToUse;

this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());

}

public ClassPathResource(String path, @Nullable Class<?> clazz) {

Assert.notNull(path, “Path must not be null”);

this.path = StringUtils.cleanPath(path);

this.clazz = clazz;

}

public final String getPath() {

return this.path;

}

@Nullable

public final ClassLoader getClassLoader() {

return (this.clazz != null ? this.clazz.getClassLoader() : this.classLoader);

}

@Override

public boolean exists() {

return (resolveURL() != null);

}

@Nullable

protected URL resolveURL() {

if (this.clazz != null) {

return this.clazz.getResource(this.path);

}

else if (this.classLoader != null) {

return this.classLoader.getResource(this.path);

}

else {

return ClassLoader.getSystemResource(this.path);

}

}

@Override

public InputStream getInputStream() throws IOException {

InputStream is;

if (this.clazz != null) {

is = this.clazz.getResourceAsStream(this.path);

}

else if (this.classLoader != null) {

is = this.classLoader.getResourceAsStream(this.path);

}

else {

is = ClassLoader.getSystemResourceAsStream(this.path);

}

if (is == null) {

throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");

}

return is;

}

@Override

public URL getURL() throws IOException {

URL url = resolveURL();

if (url == null) {

throw new FileNotFoundException(getDescription() + " cannot be resolved to URL because it does not exist");

}

return url;

}

@Override

public Resource createRelative(String relativePath) {

String pathToUse = StringUtils.applyRelativePath(this.path, relativePath);

return (this.clazz != null ? new ClassPathResource(pathToUse, this.clazz) :

new ClassPathResource(pathToUse, this.classLoader));

}

@Override

@Nullable

public String getFilename() {

return StringUtils.getFilename(this.path);

}

@Override

public String getDescription() {

StringBuilder builder = new StringBuilder(“class path resource [”);

String pathToUse = this.path;

if (this.clazz != null && !pathToUse.startsWith(“/”)) {

builder.append(ClassUtils.classPackageAsResourcePath(this.clazz));

builder.append(‘/’);

}

最后

如果觉得本文对你有帮助的话,不妨给我点个赞,关注一下吧!

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!
path;

if (this.clazz != null && !pathToUse.startsWith(“/”)) {

builder.append(ClassUtils.classPackageAsResourcePath(this.clazz));

builder.append(‘/’);

}

最后

如果觉得本文对你有帮助的话,不妨给我点个赞,关注一下吧!

[外链图片转存中…(img-RajYr5OR-1714662180636)]

[外链图片转存中…(img-Nv4ahENf-1714662180636)]

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!

  • 9
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值