最新Spring 源码第一篇开整!配置文件是怎么加载的?(1),java内存模型面试回答

惊喜

最后还准备了一套上面资料对应的面试题(有答案哦)和面试时的高频面试算法题(如果面试准备时间不够,那么集中把这些算法题做完即可,命中率高达85%+)

image.png

image.png

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

}

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

  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(‘/’);

}

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

pathToUse = pathToUse.substring(1);

}

builder.append(pathToUse);

builder.append(‘]’);

return builder.toString();

}

}

  1. 首先,ClassPathResource 的构造方法有四个,一个已经过期的方法我这里没有列出来。另外三个,我们一般调用一个参数的即可,也就是传入文件路径即可,它内部会调用另外一个重载的方法,给 classloader 赋上值(因为在后面要通过 classloader 去读取文件)。

  2. 在 ClassPathResource 初始化的过程中,会先调用 StringUtils.cleanPath 方法对传入的路径进行清理,所谓的路径清理,就是处理路径中的相对地址、Windows 系统下的 \\ 变为 / 等。

  3. getPath 方法用来返回文件路径,这是一个相对路径,不包含 classpath。

  4. resolveURL 方法表示返回资源的 URL,返回的时候优先用 Class.getResource 加载,然后才会用 ClassLoader.getResource 加载,关于 Class.getResource 和 ClassLoader.getResource 的区别,又能写一篇文章出来,我这里就大概说下,Class.getResource 最终还是会调用 ClassLoader.getResource,只不过 Class.getResource 会先对路径进行处理。

  5. getInputStream 读取资源,并返回 InputStream 对象。

  6. createRelative 方法是根据当前的资源,再创建一个相对资源。

这是 ClassPathResource,另外一个大家可能会接触到的 FileSystemResource ,小伙伴们可以自行查看其源码,比 ClassPathResource 简单。

如果不是使用 Spring,我们仅仅想自己加载 resources 目录下的资源,也可以采用这种方式:

ClassPathResource resource = new ClassPathResource(“beans.xml”);

InputStream inputStream = resource.getInputStream();

拿到 IO 流之后自行解析即可。

在 Spring 框架,构造出 Resource 对象之后,接下来还会把 Resource 对象转为 EncodedResource,这里会对资源进行编码处理,编码主要体现在 getReader 方法上,在获取 Reader 对象时,如果有编码,则给出编码格式:

public Reader getReader() throws IOException {

if (this.charset != null) {

return new InputStreamReader(this.resource.getInputStream(), this.charset);

}

else if (this.encoding != null) {

return new InputStreamReader(this.resource.getInputStream(), this.encoding);

}

else {

return new InputStreamReader(this.resource.getInputStream());

}

}

所有这一切搞定之后,接下来就是通过 XmlBeanDefinitionReader 去加载 Resource 了。

4.小结

最后

对于很多Java工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。

整理的这些资料希望对Java开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

再分享一波我的Java面试真题+视频学习详解+技能进阶书籍

美团二面惜败,我的凉经复盘(附学习笔记+面试整理+进阶书籍)

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

整理的这些资料希望对Java开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。**

再分享一波我的Java面试真题+视频学习详解+技能进阶书籍

[外链图片转存中…(img-kXd1GsHp-1715648764038)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值