文章目录

  • 前言
  • 一、统一资源:Resource
    • 1.整体结构
  • 二、ResourceLoader
    • 1.整体结构
    • 2.方法getResource
    • 3.ResourcePatternResolver
  • 三、spring如何定位资源?
    • 1.分析入口
    • 2.流程跟踪
  • 总结

前言

我们大体上知道spring可以通过读取xml配置文件,创建创建对象,然后放到ioc容器中,哪在代码层面是如何实现?这个过程用了什么的样的设计思想?本篇主要主要从整体结构上阅读spring的源码,看是如何进行资源定位?

一、统一资源:Resource

org.springframework.core.io.Resource 为 Spring 框架所有资源的抽象和访问接口,它继承 org.springframework.core.io.InputStreamSource接口。作为所有资源的统一抽象,Resource 定义了一些通用的方法,由子类 AbstractResource 提供统一的默认实现。

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;@NullableString getFilename();String getDescription();}
public interface InputStreamSource {InputStream getInputStream() throws IOException;
}

1.整体结构

Resource 根据资源的不同类型提供不同的具体实现,如下:

FileSystemResource :对 java.io.File 类型资源的封装,只要是跟 File 打交道的,基本上与 FileSystemResource 也可以打交道。支持文件和 URL 的形式。
ByteArrayResource :对字节数组提供的数据的封装。
UrlResource :对 java.net.URL类型资源的封装。内部委派 URL 进行具体的资源操作。
ClassPathResource :classpath 类型资源的实现。使用给定的 ClassLoader 或者给定的 Class 来加载资源。
其中AbstractResource提供了大部分的实现。如果我们想要实现自定义的 Resource 只需要继承AbstractResource抽象类,然后根据当前的具体资源特性覆盖相应的方法即可。

Spring 将资源的定义和资源的加载区分开,Resource 定义了统一的资源,资源的加载则由 ResourceLoader来统一定义。

二、ResourceLoader

ResourceLoader定义资源加载器,主要应用于根据给定的资源文件地址,返回对应的 Resource

public interface ResourceLoader {/** Pseudo URL prefix for loading from the class path: "classpath:". */String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;Resource getResource(String location);@NullableClassLoader getClassLoader();
}

#getResource(String location) 方法,根据所提供资源的路径 location 返回 Resource实例。
该方法支持以下模式的资源加载:
URL位置资源,如 "file:C:/test.dat"
ClassPath位置资源,如 "classpath:test.dat
相对路径资源,如 "WEB-INF/test.dat" ,此时返回的Resource 实例,根据实现不同而不同。
该方法的主要实现是在其子类 DefaultResourceLoader 中实现

1.整体结构

2.方法getResource

org.springframework.core.io.DefaultResourceLoaderResourceLoader 的默认实现。其getResource(String location)如下

// DefaultResourceLoader.java@Override
public Resource getResource(String location) {Assert.notNull(location, "Location must not be null");// 首先,通过 ProtocolResolver 来加载资源for (ProtocolResolver protocolResolver : this.protocolResolvers) {Resource resource = protocolResolver.resolve(location, this);if (resource != null) {return resource;}}// 其次,以 / 开头,返回 ClassPathContextResource 类型的资源if (location.startsWith("/")) {return getResourceByPath(location);// 再次,以 classpath: 开头,返回 ClassPathResource 类型的资源} else if (location.startsWith(CLASSPATH_URL_PREFIX)) {return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());// 然后,根据是否为文件 URL ,是则返回 FileUrlResource 类型的资源,否则返回 UrlResource 类型的资源} else {try {// Try to parse the location as a URL...URL url = new URL(location);return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));} catch (MalformedURLException ex) {// 最后,返回 ClassPathContextResource 类型的资源// No URL -> resolve as resource path.return getResourceByPath(location);}}
}

3.ResourcePatternResolver

另外需要关注的是:
ResourceLoaderResource getResource(String location) 方法,每次只能根据 location 返回一个 Resource 。其子类org.springframework.core.io.support.ResourcePatternResolver在它的基础上进行了扩展,支持根据指定的资源路径匹配模式每次返回多个 Resource 实例,其定义如下:

public interface ResourcePatternResolver extends ResourceLoader {String CLASSPATH_ALL_URL_PREFIX = "classpath*:";Resource[] getResources(String locationPattern) throws IOException;}

org.springframework.core.io.support.PathMatchingResourcePatternResolver ,为 ResourcePatternResolver 最常用的子类,它除了支持 ResourceLoaderResourcePatternResolver 新增的 classpath*: 前缀外,还支持 Ant 风格的路径匹配模式(类似于 “**/*.xml”)。

/*** 内置的 ResourceLoader 资源定位器*/
private final ResourceLoader resourceLoader;
/*** Ant 路径匹配器*/
private PathMatcher pathMatcher = new AntPathMatcher();public PathMatchingResourcePatternResolver() {this.resourceLoader = new DefaultResourceLoader();
}public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {Assert.notNull(resourceLoader, "ResourceLoader must not be null");this.resourceLoader = resourceLoader;
}public PathMatchingResourcePatternResolver(@Nullable ClassLoader classLoader) {this.resourceLoader = new DefaultResourceLoader(classLoader);
}
@Overridepublic Resource getResource(String location) {return getResourceLoader().getResource(location);}

ResourceLoader 在实例化的时候,可以指定一个 ResourceLoader,并在getResource方法中使用了该ResourceLoader,如果不提供,则默认使用DefaultResourceLoader
PathMatchingResourcePatternResolver最大的不同是提供了getResources方法的实现。

@Override
public Resource[] getResources(String locationPattern) throws IOException {Assert.notNull(locationPattern, "Location pattern must not be null");// 以 "classpath*:" 开头if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {// 路径包含通配符// a class path resource (multiple resources for same name possible)if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {// a class path resource patternreturn findPathMatchingResources(locationPattern);// 路径不包含通配符} else {// all class path resources with the given namereturn findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));}// 不以 "classpath*:" 开头} else {// Generally only look for a pattern after a prefix here, // 通常只在这里的前缀后面查找模式// and on Tomcat only after the "*/" separator for its "war:" protocol. 而在 Tomcat 上只有在 “*/ ”分隔符之后才为其 “war:” 协议int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 :locationPattern.indexOf(':') + 1);// 路径包含通配符if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {// a file patternreturn findPathMatchingResources(locationPattern);// 路径不包含通配符} else {// a single resource with the given namereturn new Resource[] {getResourceLoader().getResource(locationPattern)};}}
}

这里不展开对getResources方法的分析。我们重点关注,spring是如何使用ResourceResourceLoader

三、spring如何定位资源?

1.分析入口

创建一个测试主函数

public static void main(String[] args) {ApplicationContext context =new ClassPathXmlApplicationContext("classpath*:application.xml");UserService userService = context.getBean(UserService.class);System.out.println(userService);// 这句将输出: hello worldSystem.out.println(userService.getName());}

2.流程跟踪


这里先暂且忽略其他流程,一致找到加载xml文件的地方

上面的流程可通过端点跟踪或者是ClassPathXmlApplicationContext类的继承结构找到对应的实现。

这里重要的一个地方是beanDefinitionReader.setResourceLoader(this);,也就是说AbstractXmlApplicationContext及其子类也属于ResourceLoader。继续跟踪
这里到AbstractBeanDefinitionReader#loadBeanDefinitions方法后,就是我们的目的地了,下面来分析该方法

public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {// 获取加载器 也就是上面的beanDefinitionReader.setResourceLoader(this);ResourceLoader resourceLoader = getResourceLoader();if (resourceLoader == null) {throw new BeanDefinitionStoreException("Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");}if (resourceLoader instanceof ResourcePatternResolver) {// Resource pattern matching available.try {Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);int count = loadBeanDefinitions(resources);if (actualResources != null) {Collections.addAll(actualResources, resources);}if (logger.isTraceEnabled()) {logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");}return count;}catch (IOException ex) {throw new BeanDefinitionStoreException("Could not resolve bean definition resource pattern [" + location + "]", ex);}}else {// Can only load single resources by absolute URL.Resource resource = resourceLoader.getResource(location);int count = loadBeanDefinitions(resource);if (actualResources != null) {actualResources.add(resource);}if (logger.isTraceEnabled()) {logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");}return count;}}

在分析上面方法之前,先来看一张类关系图

从上面结构图中可以看出,我们创建的ClassPathXmlApplicationContext也实现了ResourceLoader,并且判断resourceLoader instanceof ResourcePatternResolver为true.
ClassPathXmlApplicationContextgetResource最终又委托给了ResourcePatternResolver进行实现

ResourcePatternResolver再根据不同的location匹配返回不同的Resource对象

总结

  1. 不同的资源路径(location),对应不同的Resource对象
  2. 根据location,由ResourceLoader进行加载,返回不同的Resouce对象
  3. ResourcePatternResolverResourceLoader进行了增强,可以处理多个location
  4. ClassPathXmlApplicationContext也实现了ResourceLoader,但并不直接获取Resource,而是委托给了ResourcePatternResolver进行处理

到这里,还有一点没有弄清楚,ClassPathXmlApplicationContext为什么要实现ResourceLoader呢?

Spring如何加载xml文件相关推荐

  1. Spring如何加载XSD文件(org.xml.sax.SAXParseException: Failed to read schema document错误的解决方法)...

    本文原文连接: http://blog.csdn.net/bluishglc/article/details/7596118 ,转载请注明出处! 有时候你会发现过去一直启动正常的系统,某天启动时会报出 ...

  2. org.xml.sax.SAXParseException: Failed to read schema document错误的完美解决方法 以及 Spring如何加载XSD文件

    有时候你会发现过去一直启动正常的系统,某天启动时会报出形如下面的错误: org.xml.sax.SAXParseException: schema_reference.4: Failed to rea ...

  3. Spring加载xml文件错误异常:Offending resource: class path resource [spring-context.xml];

    很少写单纯的java工程,在运行的时候,加载xml文件的时候报错,在此记录错误信息,备自己以后查阅,错误信息如下: Java HotSpot(TM) 64-Bit Server VM warning: ...

  4. Spring中加载xml配置文件的六种方式

    Spring中加载xml配置文件的六种方式 博客分类: Spring&EJB XMLSpringWebBeanBlog  因为目前正在从事一个项目,项目中一个需求就是所有的功能都是插件的形式装 ...

  5. Spring如何加载XSD文件

    http://blog.csdn.net/bluishglc/article/details/7596118 本文原文连接: http://blog.csdn.net/bluishglc/articl ...

  6. spring boot 加载静态文件

    spring boot 加载静态文件 @Slf4j @Configuration public class WebMvcConfig extends WebMvcConfigurationSuppor ...

  7. 解决dom4j加载xml文件性能慢的问题

    解决dom4j加载xml文件性能慢的问题 参考文章: (1)解决dom4j加载xml文件性能慢的问题 (2)https://www.cnblogs.com/wulm/p/9863104.html 备忘 ...

  8. android xml 加载错误提示,加载uixml文件失败 打开wps时显示“加载XML文件失败1

    打开wps时显示"加载XML文件失败." 打开wps时显示"加载XML文件失败1 关闭所有打开的Word文档: 开始 → 运行 → 粘贴上面复制的命令 → 确定. 在打开 ...

  9. [转载]spring+mybatis加载属性文件设置数据源失败原因及解决方案 - 泡在网上的日子

    spring+mybatis加载属性文件设置数据源失败原因及解决方案 - 泡在网上的日子 http://www.jcodecraeer.com/a/chengxusheji/java/2013/062 ...

  10. IDEA 加载xml文件失败,解决方案

    问题 在IDEA中,第在src/main路径下添加了xml,使用如下代码加载xml文件时: String path = JsoupDemo.class.getClassLoader().getReso ...

最新文章

  1. 酷狗音乐QQ显示(VC源代码)
  2. dhcp 续约review报文_Linux的私房菜 DHCP
  3. OSI七层模型的作用
  4. 大数据之_亿级分布式日志管理ELK_工作笔记001_ELK认识介绍
  5. 华为 “VRRP” 多备份组
  6. python数据分析与应用-Python数据分析与应用 PDF 内部全资料版
  7. 排队论模型(八):Matlab 生成随机数、排队模型的计算机模拟
  8. 太极软件qn的代码_多版本QQ内置qn、qx模块
  9. 大英博物馆天猫开店,本王的宝贝都要被你们玩坏啦!
  10. 计算机网络面试常见题
  11. 【Java语言】交换两个数的数值
  12. 【机器学习】深入浅出经典贝叶斯统计
  13. 智能家居有必要HomeKit吗?
  14. linux win10自带浏览器,win10系统下如何安装opera浏览器
  15. 解码元宇宙,深度剖析元宇宙空间+数字人+数字孪生
  16. google开源服务器apprtc的搭建
  17. 随机实现“泰坦”与“宙斯”之间的模拟交战。说明:本题以希腊神话中宙斯和泰坦间的交战为背景
  18. 站群管理软件-通过SEO拓展业务并吸引潜在客户
  19. 智能时代,电话销售如何打造高效的智能化解决方案?
  20. JAVA窗体程序调用图片、音频、字体资源

热门文章

  1. 计算机操作系统应用,.计算机操作系统应用技巧 (转
  2. postman工具 如何传递当前时间戳和MD5加密
  3. Java后端面试题总结一
  4. 线程池Executor框架解读
  5. JS 里的数据类型及几个操作
  6. Linux--进程组 作业 会话 守护(精灵)进程
  7. c++中static关键字的用法总结
  8. android Fragment 动画 设置位置
  9. POJ 3415 Common Substrings (后缀数组,长度不小于k的公共子串的个数)
  10. asp.net电子商务开发实战 视频 第三讲(门类列表)