Spring IOC 源码解析(五) ResourceLoader

本文深入探讨了Spring框架中资源加载的机制,包括ResourceLoader接口、ResourceUtils工具类、ResourcePatternResolver接口及其实现PathMatchingResourcePatternResolver等核心组件。解析了如何通过不同的策略和实现从类路径、文件系统及Web应用根目录加载资源。

一.前言

用于加载资源的策略接口(例如,类路径或文件系统资源)。需要 ApplicationContext 来提供此功能,以及扩展的ResourcePatternResolver 支持。

DefaultResourceLoader 是一个独立的实现,可以在 ApplicationContext 外部使用,也可以被 ResourceEditor 使用。

使用特定上下文的资源加载策略,在ApplicationContext中运行时,可以从字符串中(路径)填充 Resource 和Resource 数组类型的 Bean 属性。

简单概括就是 Spring 加载外部资源的统一父接口,可以从不同渠道获得.

因为 ResourceLoader 的实现类太多(可以自己在 idea 中查看类结构),所以我会挑一些重要的,常用的来简单描述.其他的用到时我们可以在讨论.
在这里插入图片描述

二.ResourceLoader 结构分解

2.1 ResourceLoader 接口

该接口有一个唯一字段 String 类型,值为 classpath: ,他是从 ResourceUtils 抽象工具类里面应用的字段值 CLASSPATH_URL_PREFIX.

主要功能:

  1. 通过 String 类型的 location 获取 Resource.(子类就是实现通过各种 location 解析出资源的)
  2. 暴露此 ResourceLoader 使用的 classLoader

2.2 ResourceUtils

ResourceUtils 是一个抽象类,也没有具体的实现, 也可以说他自己就是的实现. 因为他的所有字段和方法都是 static 静态的.所以可以直接通过类来调用.

用于将资源位置解析为文件系统中的文件的实用方法。主要供框架内部使用。

ResourceLoader 的 getResource() 方法可以解析 Resource 对象的任何位置,从而允许通过其 getFile() 方法获取文件系统中的 java.io.File。

主要功能:
1.定义了一系列的协议静态字段
2.从指定 url 或 url 或 resourceLocation(String) 获取指定 File
3.将 URL 或 resourceLocation (String) 转为为实际的文件 URI
4.判断给定的 resourceLocation (String) 是否是 url
5.判断给定的 URL 是否指向文件系统中的资源

2.3 ResourcePatternResolver

用于将位置模式(例如,Ant 样式路径模式)解析为 Resource 对象的策略接口。

这是 ResourceLoader 接口的扩展。传入的R esourceLoader(例如,在上下文中运行时通过ResourceLoaderAware 传入的 ApplicationContext)可以检查它是否也实现了这个扩展接口。

子类 PathMatchingResourcePatternResolver 是一个独立的实现,可以在 ApplicationContext 外部使用,也可以由 ResourceArrayPropertyEditor 用于填充 Resource 数据 bean 属性。

可以与任何类型的位置模式一起使用(例如“/WEB-INF/*-context.xml”):输入模式必须与策略实现相匹配。此接口仅指定转换方法而不是特定的模式格式。

该接口有一个唯一的属性和一个唯一的方法:
属性:String CLASSPATH_ALL_URL_PREFIX = "classpath*:"

方法:Resource[] getResources(String locationPattern)
类路径中所有匹配资源的伪 URL 前缀:“classpath *:”这与 ResourceLoader 的类路径URL前缀的不同之处在于它检索给定名称的所有匹配资源

2.4 PathMatchingResourcePatternResolver

ResourcePatternResolver 实现,它能够将指定的资源位置路径解析为一个或多个匹配的资源。源路径可以是一个简单的路径,该路径具有到目标资源的一对一映射,或者可以包含特殊的“classpath*:”前缀 和/或 内部Ant 样式正则表达式(使用Spring的 AntPathMatcher 实用程序进行匹配)。后者都是有效的通配符。

在简单的情况下,如果指定的位置路径不以“classpath *:”前缀开头,并且不包含PathMatcher模式,则此解析器将通过底层ResourceLoader上的getResource()调用返回单个资源。

通过“classpath *:”前缀,可以检索具有相同名称的多个类路径资源。
例如,“classpath *:META-INF / beans.xml”将在类路径中找到所有“beans.xml”文件,无论是在“classes”目录中还是在JAR文件中。

概括:传入的 location (String) 可以是 Ant 样式的路径或 classpath * 前缀的路径,解析后返回所有匹配到的 Resource .

Ant 样式示例:

 /WEB-INF/*-context.xml
 com/mycompany/**/applicationContext.xml
 file:C:/some/path/*-context.xml
 classpath:com/mycompany/**/applicationContext.xml

主要功能(部分):

  1. 搜索URLClassLoader 所有 URL 以获取 jar 文件引用,并以指向 jar 文件内容根目录的形式将它们添加到给定的资源集。(概括:将所有 jar 文件的根目录 URL 存入指定的 Set 集合中)
  2. 从系统属性 java.class.path 中获取所有 jar 文件引用,添加到指定 Set 集合中
  3. 将 ClassLoader 返回的 URL 转化为 resource
  4. 确定给定 location 的根目录
  5. 通过 ClassLoader 查找具体给定路径的所有类位置资源。

概括:该类的主要功能是解析 location(String) 或 系统参数 等等,获取多资源的功能.

2.5 ServletContextResourcePatternResolver

PathMatchingResourcePatternResolver 的 ServletContext 子类,能够通过ServletContext.getResourcePaths(java.lang.String)在Web应用程序根目录下找到匹配的资源。返回超类的文件系统,检查其他资源。

主要功能:

  1. 重写 PathMatchingResourcePatternResolverdoFindPathMatchingFileResources 方法,它检查ServletContextResource 并使用 ServletContext.getResourcePaths 查找 Web 应用程序根目录下的匹配资源。
  2. 递归检索与给定模式匹配的 ServletContextResources ,将它们添加到给定的结果集。

2.6 DefaultResourceLoader

ResourceLoader 接口的默认实现。由 ResourceEditor 使用,并作为 AbstractApplicationContext 的基类。也可以单独使用。

如果位置值是URL,则返回UrlResource;如果是非URL路径或“classpath:”伪URL,则返回ClassPathResource。

主要功能:

  1. 使用此资源加载器注册给定的解析程序,允许处理其他协议。
  2. 清除此资源加载器中的所有资源缓存。
  3. 返回 ClassLoader 以加载类路径资源。
  4. 返回当前注册的协议解析器的集合,允许默认和修改。
  5. 返回指定资源位置的资源.
  6. 指定 ClassLoader 以使用或加载类路径资源,以便在实际资源访问时使用线程上下文类加载器。

2.7 AbstractApplicationContext

ApplicationContext 接口的抽象实现。不要求用于配置的存储类型;只需实现常见的上下文功能使用 Template Method 设计模式,需要具体的子类来实现抽象方法

AbstractApplicationContext 的父类就是 DefaultResourceLoader, AbstractApplicationContext 是所有上下文的基类,也就保证的所有的 ApplicationContext 都是具备了资源加载的功能.

AbstractApplicationContext 作为所有上下文的基类,实现了很多 ApplicationContext 的公用的功能,因为功能太多,我们会在后面讲解源码的遇到的时候在一点一点讲解.

这里我们主要知道他的两个特性:

  1. AbstractApplicationContext 的基类是 DefaultResourceLoader,即 AbstractApplicationContext 具备资源加载能力,
  2. AbstractApplicationContext 是所有 ApplicationContext 实现的基类, 即有 ApplicationContext 都具备资源加载能力.

Spring IOC 源码解析(六) XmlBeanFactory

<think>好的,我现在需要帮助用户理解Spring IOC容器的源码实现和工作原理。用户之前已经提供了一些相关的引用内容,我需要结合这些内容来组织一个清晰且详细的回答。 首先,我应该先回顾用户提供的引用,找出关键点。引用1提到BeanFactory和ApplicationContext是两个核心接口,需要分析它们的源码差异。引用2提到了IoC容器的基本概念、关键接口、bean的生命周期和循环依赖的解决方法。引用3提到了事件发布器和监听器的初始化,这部分涉及观察者模式。引用4是关于prepareRefresh方法的预处理步骤,包括初始化属性和早期事件处理。引用5提到了Bean工厂后置处理器和Bean实例化的完成。 接下来,我需要确定回答的结构。通常,源码分析可以从容器启动流程、Bean生命周期管理、核心接口差异、设计模式应用等方面展开。用户的问题比较广泛,所以需要覆盖主要部分,同时保持结构清晰。 第一步,容器启动流程。根据引用4中的prepareRefresh方法,这是刷新前的预处理步骤,包括设置启动时间、状态,初始化属性,验证环境变量,保存早期事件。然后,引用5中的invokeBeanFactoryPostProcessors和finishBeanFactoryInitialization是后续步骤,处理Bean工厂后置处理器和实例化Bean。需要详细说明这些步骤的顺序和作用。 第二步,Bean的生命周期管理。这里需要结合BeanFactory和ApplicationContext的区别,可能涉及Bean的创建、初始化、依赖注入、销毁等阶段。引用2中提到bean的生命周期是一个重点,可能需要详细描述每个阶段涉及的接口和方法,比如BeanPostProcessor。 第三步,关键接口分析。BeanFactory作为基础接口,而ApplicationContext扩展了更多功能,如事件发布、国际化等。引用1和引用2都提到了这两个接口,需要比较它们的源码和功能差异,比如ApplicationContext如何继承BeanFactory并添加额外特性。 第四部分,设计模式的应用。例如,观察者模式在事件处理中的应用,如引用3中的事件发布器和监听器的初始化。可能还有工厂模式在Bean创建中的使用,单例模式在Bean作用域中的应用等。 第部分,解决循环依赖。引用2提到循环依赖是高级特性,需要解释Spring如何通过三级缓存解决这个问题,可能涉及源码中的DefaultSingletonBeanRegistry类中的getSingleton方法及相关处理。 在组织内容时,要确保每个部分都有对应的源码引用和解释,例如在容器启动流程中,可以引用prepareRefresh的代码片段,说明其作用。同时,结合用户提供的引用,正确标注引用标识,如[^4]、[^5]等。 另外,用户可能需要示例代码或流程图来辅助理解,但根据问题描述,用户要求的是文本回答,所以需要用清晰的步骤和子标题来划分内容。比如使用“1. 容器启动流程”、“2. Bean生命周期管理”等。 最后,生成相关问题,可能需要围绕源码分析、设计模式、具体实现细节等方面提问,例如询问BeanFactory和ApplicationContext的区别,或者如何扩展BeanPostProcessor接口。 需要确保回答符合用户的要求:结构清晰,分步骤解释,引用正确,并且使用中文。同时,避免使用Markdown格式,但根据用户提供的示例,可能需要使用代码块来展示源码片段,如引用4中的prepareRefresh方法代码。 现在需要整合这些思路,形成一个连贯的回答,涵盖用户提到的所有关键点,并正确引用提供的参考文献。</think>### Spring IOC 源码分析及详细解读 #### 一、容器启动流程 Spring IOC 容器的启动流程围绕 `ApplicationContext` 的 `refresh()` 方法展开,核心步骤如下: 1. **预处理阶段 (`prepareRefresh()`)** - 记录启动时间,设置容器状态为激活。 - 初始化环境变量和配置参数(如 `initPropertySources()`)。 - 验证必要属性是否合法(如 `validateRequiredProperties()`)。 - 准备早期事件存储队列 `earlyApplicationEvents` [^4]。 2. **加载Bean定义 (`obtainFreshBeanFactory()`)** - 创建 `BeanFactory`(默认实现为 `DefaultListableBeanFactory`)。 - 解析 XML 或注解配置,将 Bean 定义注册到 `BeanDefinitionMap` 中。 3. **处理后置处理器 (`invokeBeanFactoryPostProcessors()`)** - 调用 `BeanFactoryPostProcessor` 实现类(如 `ConfigurationClassPostProcessor`),完成配置类解析、`@Bean` 方法解析等[^5]。 4. **初始化事件和监听器** - 通过 `initApplicationEventMulticaster()` 初始化事件广播器。 - 通过 `registerListeners()` 注册监听器,支持观察者模式[^3]。 5. **实例化单例Bean (`finishBeanFactoryInitialization()`)** - 遍历 `BeanDefinitionMap`,对非懒加载的单例 Bean 进行实例化、依赖注入和初始化[^5]。 --- #### 二、Bean生命周期管理 Bean 的生命周期由以下接口控制: ```java // 示例代码:Bean生命周期关键接口 public interface BeanPostProcessor { Object postProcessBeforeInitialization(Object bean, String beanName); Object postProcessAfterInitialization(Object bean, String beanName); } ``` 1. **实例化**:通过反射调用构造函数。 2. **属性注入**:通过 `populateBean()` 方法注入依赖(如 `@Autowired`)。 3. **初始化**: - 调用 `InitializingBean.afterPropertiesSet()`。 - 执行自定义初始化方法(如 `@PostConstruct`)。 4. **销毁**:容器关闭时调用 `DisposableBean.destroy()` 或自定义销毁方法。 --- #### 三、核心接口对比:BeanFactory vs ApplicationContext | 特性 | BeanFactory | ApplicationContext | |---------------------|---------------------------|----------------------------------------| | Bean实例化 | 延迟加载 | 支持预加载(通过 `refresh()` 触发) | | 国际化支持 | 无 | 有(`MessageSource`) | | 事件发布 | 无 | 有(`ApplicationEventPublisher`) | | AOP集成 | 需手动配置 | 自动集成 | | 资源访问 | 基础支持 | 扩展支持(如 `ResourceLoader`) | ApplicationContext 通过组合模式扩展了 BeanFactory 的功能[^1]。 --- #### 四、设计模式应用 1. **工厂模式**:`BeanFactory` 是工厂接口,`DefaultListableBeanFactory` 是具体实现。 2. **观察者模式**:事件广播器 (`ApplicationEventMulticaster`) 和监听器 (`ApplicationListener`) 实现解耦[^3]。 3. **模板方法模式**:`AbstractApplicationContext.refresh()` 定义了容器刷新的步骤骨架。 --- #### 、循环依赖解决 Spring 通过**三级缓存**解决循环依赖: 1. **一级缓存** (`singletonObjects`):存放完全初始化的 Bean。 2. **二级缓存** (`earlySingletonObjects`):存放未完成属性注入的早期 Bean 引用。 3. **三级缓存** (`singletonFactories`):存放 Bean 工厂对象,用于生成早期代理对象。 源码关键方法: ```java // DefaultSingletonBeanRegistry 类 protected Object getSingleton(String beanName, boolean allowEarlyReference) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; } ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值