介绍
-
ClassPathXmlApplicationContext
是学习Spring入门依赖接触的第一个容器类。 -
ClassPathXmlApplicationContext
是 Spring 框架中用于加载 XML 配置文件来创建应用上下文的类 。它在 Spring 的 IoC(控制反转)容器体系中扮演重要角色,属于ApplicationContext
类型的常用容器
用法示例
+ 在对应的 XML 配置文件中,定义相关 Bean :<?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">
<bean id="user" class="com.cloud.domain.User"></bean>
</beans>
- 使用时,通常会传入 XML 配置文件路径来创建实例,进而获取配置文件中定义的 Bean 。例如:
public class ClassPathApplicationContextTest {
public static void main(String[] args) {
// 创建 ClassPathXmlApplicationContext 实例,加载指定的 XML 配置文件
ClassPathXmlApplicationContext xmlAppContext = new ClassPathXmlApplicationContext("spring-bean.xml");
// 从容器中获取名为 user 的 Bean 实例
User service = (User)xmlAppContext.getBean("user");
service.test();
}
}
构造函数
构造函数:有多个重载构造函数ClassPathXmlApplicationContext
是 Spring 框架中常用的应用上下文实现类,用于从类路径加载 XML 配置文件并创建应用上下文。以下是其常见构造函数及其功能介绍:
1. 单路径构造函数
public ClassPathXmlApplicationContext(String configLocation) throws BeansException
-
功能:从指定的类路径加载单个 XML 配置文件,并创建应用上下文。
-
参数:
configLocation
:XML 配置文件的路径(如applicationContext.xml
)。
-
特点:
- 配置文件路径支持类路径前缀(如
classpath:
),但默认即从类路径加载。 - 加载后自动刷新容器(调用
refresh()
),初始化所有单例 Bean。
- 配置文件路径支持类路径前缀(如
2. 多路径构造函数
public ClassPathXmlApplicationContext(String... configLocations) throws BeansException
-
功能:从多个类路径加载 XML 配置文件,并合并为一个应用上下文。
-
参数:
configLocations
:多个 XML 配置文件的路径数组(如{"beans1.xml", "beans2.xml"}
)。
-
特点:
- 多个配置文件中的 Bean 定义会合并,相同 ID 的 Bean 后加载的会覆盖先加载的。
- 同样会自动刷新容器。
3. 带父上下文的单路径构造函数
public ClassPathXmlApplicationContext(String configLocation, ApplicationContext parent) throws BeansException
-
功能:创建具有父上下文的应用上下文,继承父上下文中的 Bean 定义。
-
参数:
configLocation
:XML 配置文件路径。parent
:父上下文(如new AnnotationConfigApplicationContext(ParentConfig.class)
)。
-
特点:
- 子上下文可访问父上下文中的 Bean,但父上下文无法访问子上下文中的 Bean。
- 父子上下文的环境配置(如属性源、激活的 profile)会自动合并。
4. 带刷新控制的多路径构造函数
public ClassPathXmlApplicationContext(
String[] configLocations,
boolean refresh,
@Nullable ApplicationContext parent
) throws BeansException
- 功能:最完整的构造函数,支持自定义配置文件、刷新行为和父上下文。
- 参数:
configLocations
:XML 配置文件路径数组。refresh
:是否自动刷新容器(true
表示立即刷新,false
则需手动调用refresh()
)。parent
:父上下文(可为null
)。
- 场景:
- 当需要手动控制容器初始化时机时(如先添加额外配置再刷新),可设置
refresh = false
。
- 当需要手动控制容器初始化时机时(如先添加额外配置再刷新),可设置
示例对比
构造函数 | 配置文件数量 | 父上下文 | 自动刷新 |
---|---|---|---|
ClassPathXmlApplicationContext(String) | 单个 | 无 | ✅ |
ClassPathXmlApplicationContext(String[]) | 多个 | 无 | ✅ |
ClassPathXmlApplicationContext(String, ApplicationContext) | 单个 | 有 | ✅ |
ClassPathXmlApplicationContext(String[], boolean, ApplicationContext) | 多个 | 可选 | 可选 |
注意事项
- 路径格式:
- 推荐使用
classpath:
前缀(如"classpath:config/applicationContext.xml"
),避免歧义。 - 支持通配符(如
"classpath*:config/*.xml"
),可匹配多个文件。
- 推荐使用
- 刷新限制:
- 容器刷新后不能重复刷新,需创建新的上下文实例。
- 父子上下文关系:
- 父子上下文共享同一个环境(
Environment
),但 Bean 定义相互隔离。
- 父子上下文共享同一个环境(
通过选择合适的构造函数,开发者可以灵活控制应用上下文的加载方式、配置文件组合以及父子上下文关系,满足不同场景下的依赖注入需求。
启动源码流程
UML类图
源码解析
源码执行流程
一、容器初始化流程(构造器调用)
1. 客户端调用入口
Client->>ClassPathXmlApplicationContext: ClassPathXmlApplicationContext("config.xml")
- 动作:客户端通过构造器传入配置文件路径(如
"config.xml"
),触发容器初始化。 - 目标:创建
ClassPathXmlApplicationContext
实例,并完成父类初始化和配置路径设置。
2. 父类构造器逐层调用
ClassPathXmlApplicationContext->>AbstractXmlApplicationContext: super(parent)
AbstractXmlApplicationContext->>AbstractRefreshableConfigApplicationContext: super(parent)
AbstractRefreshableConfigApplicationContext->>AbstractRefreshableApplicationContext: super(parent)
AbstractRefreshableApplicationContext->>AbstractApplicationContext: super(parent)
- 层级关系
ClassPathXmlApplicationContext
→AbstractXmlApplicationContext
→AbstractRefreshableConfigApplicationContext
→AbstractRefreshableApplicationContext
→AbstractApplicationContext
。
- 核心逻辑
- 在
AbstractApplicationContext
中完成资源解析器和环境配置的初始化。
- 在
3. 资源解析器创建
AbstractApplicationContext->>PathMatchingResourcePatternResolver: new PathMatchingResourcePatternResolver(this)
- 作用:创建
PathMatchingResourcePatternResolver
用于解析类路径下的资源(如classpath:config.xml
)。 - 返回值:解析器实例返回给
AbstractApplicationContext
,用于后续配置文件加载。
4. 环境配置合并(若有父容器)
alt parent != null
AbstractApplicationContext->>parent: getEnvironment()
...(遍历父环境属性源、合并配置文件等)
end
- 条件:若存在父容器(
parent != null
),当前容器会合并父容器的环境配置:- 属性源(
PropertySources
):将父环境中不存在的属性源按顺序添加到当前环境末尾(保证父配置优先级低于当前配置)。 - 配置文件(Profiles):合并父容器的激活配置文件(
Active Profiles
)和默认配置文件(Default Profiles
)。
- 属性源(
二、setConfigLocations方法核心流程
1. 方法入口与参数校验
ClassPathXmlApplicationContext->>AbstractRefreshableConfigApplicationContext: setConfigLocations(configLocations)
AbstractRefreshableConfigApplicationContext->>AbstractRefreshableConfigApplicationContext: locations != null
AbstractRefreshableConfigApplicationContext->>AbstractRefreshableConfigApplicationContext: Assert.noNullElements(locations)
- 参数校验:
- 检查
configLocations
是否为null
。 - 调用
Assert.noNullElements
确保数组中无null
元素(避免空指针异常)。
- 检查
2. 初始化配置路径数组
AbstractRefreshableConfigApplicationContext->>AbstractRefreshableConfigApplicationContext: this.configLocations = new String[locations.length]
- 动作:根据传入的路径数组长度创建
configLocations
数组,用于存储解析后的路径。
3. 遍历每个配置路径并解析
loop 遍历每个location
AbstractRefreshableConfigApplicationContext->>AbstractRefreshableConfigApplicationContext: resolvePath(locations[i])
- 核心逻辑:对每个原始路径字符串
locations[i]
执行以下处理:
三、路径解析流程(resolvePath 方法)
1. 获取环境对象(ConfigurableEnvironment)
AbstractRefreshableConfigApplicationContext->>AbstractApplicationContext: getEnvironment()
- 逻辑:
- 首次调用时,
environment
为null
,触发createEnvironment()
创建StandardEnvironment
。 StandardEnvironment
初始化时会添加系统属性(System.getProperties()
)和环境变量(System.getenv()
)到属性源。
- 首次调用时,
2. 解析占位符(resolveRequiredPlaceholders)
AbstractRefreshableConfigApplicationContext->>ConfigurableEnvironment: resolveRequiredPlaceholders(path)
- 调用链:
ConfigurableEnvironment
→PropertySourcesPropertyResolver
→PropertyPlaceholderHelper
。
3. 占位符解析核心逻辑(`PropertyPlaceholderHelper`)
PropertyPlaceholderHelper->>PropertyPlaceholderHelper: parseStringValue(path, resolver, null)
- 步骤分解:
- 查找占位符起始位置:通过
indexOf("${")
定位占位符(如${app.config.path}
)。 - 查找占位符结束位置:调用
findPlaceholderEndIndex
确定}
的位置。 - 提取占位符文本:如
app.config.path
。 - 递归解析嵌套占位符:若占位符中包含其他占位符(如
${env.${type}.config}
),递归调用parseStringValue
。 - 解析属性值:
- 调用
resolver.resolvePlaceholder(placeholder)
从属性源中获取值。 - 支持默认值(如
${key:defaultValue}
):若解析失败,使用冒号后的默认值。
- 调用
- 替换占位符:将解析后的值替换到原始字符串中,循环处理直至无占位符。
- 查找占位符起始位置:通过
4. 处理特殊情况
- 循环引用:通过
visitedPlaceholders
集合检测循环引用(如A=${B}, B=${A}
),抛出IllegalArgumentException
。 - 未解析占位符:若
ignoreUnresolvablePlaceholders
为false
(默认),解析失败时抛出异常;否则保留原始字符串。
四、路径处理收尾
AbstractRefreshableConfigApplicationContext->>AbstractRefreshableConfigApplicationContext: resolvedPath.trim()
AbstractRefreshableConfigApplicationContext->>this.configLocations: [i] = resolvedPath
- 动作:
- 去除解析后路径的前后空格(如
" config.xml "
→"config.xml"
)。 - 将处理后的路径存入
configLocations
数组。
- 去除解析后路径的前后空格(如
五、流程总结
关键类与职责
类名 | 职责描述 |
---|---|
ClassPathXmlApplicationContext | 入口类,负责初始化容器并设置配置路径。 |
AbstractRefreshableConfigApplicationContext | 存储配置路径并提供 setConfigLocations 方法。 |
ConfigurableEnvironment | 管理环境属性(如系统属性、环境变量),提供占位符解析能力。 |
PropertyPlaceholderHelper | 核心占位符解析器,处理嵌套占位符、默认值和循环引用。 |
数据流向
原始路径(客户端输入) → resolvePath → 解析占位符 → 去除空格 → 存入 configLocations 数组 → 后续用于加载配置文件
异常处理
- 参数为空:
Assert.noNullElements
抛出IllegalArgumentException
。 - 占位符解析失败:
PropertyPlaceholderHelper
抛出IllegalArgumentException
。 - 循环引用:通过
visitedPlaceholders
检测并抛出异常。
六、与 Spring 容器启动的关联
- 后续流程:构造器调用完成后,
ClassPathXmlApplicationContext
会调用refresh()
启动容器,此时configLocations
中已存储解析后的路径,用于加载 XML 配置文件(如通过PathMatchingResourcePatternResolver
加载资源)。 - 核心作用:
setConfigLocations
是容器配置路径的“预处理”阶段,确保配置路径中的动态占位符(如从环境变量获取路径)在容器启动前完成解析。
通过以上流程,Spring 容器实现了配置路径的动态解析和校验,为后续 Bean 定义的加载和容器初始化奠定了基础。
启动源码入口
public ClassPathXmlApplicationContext(String... configLocations) throws BeansException {
this(configLocations, true, null);
}
时序图
流程图
Client
└── 调用ClassPathXmlApplicationContext("config.xml")
└── 执行ClassPathXmlApplicationContext(["config.xml", true, null])构造器
├── **调用父类构造器链**
│ ├── ClassPathXmlApplicationContext->>AbstractXmlApplicationContext: super(parent)
│ │ ├── AbstractXmlApplicationContext->>AbstractRefreshableConfigApplicationContext: super(parent)
│ │ │ ├── AbstractRefreshableConfigApplicationContext->>AbstractRefreshableApplicationContext: super(parent)
│ │ │ │ └── AbstractRefreshableApplicationContext->>AbstractApplicationContext: super(parent)
│ │ │ │ ├── **AbstractApplicationContext初始化**
│ │ │ │ │ ├── 调用this()无参构造器
│ │ │ │ │ │ ├── 获取资源解析器:getReourcePatternResolver()
│ │ │ │ │ │ │ └── 创建PathMatchingResourcePatternResolver实例
│ │ │ │ │ │ └── 设置父容器:setParent(parent)
│ │ │ │ │ │ └── **若父容器存在(parent != null)**
│ │ │ │ │ │ ├── 合并环境配置(Environment)
│ │ │ │ │ │ │ ├── 从父容器获取环境:parent.getEnvironment()
│ │ │ │ │ │ │ ├── 当前容器获取/创建环境:getEnvironment()
│ │ │ │ │ │ │ ├── 合并父环境的PropertySources(遍历添加不存在的属性源)
│ │ │ │ │ │ │ ├── 合并激活配置文件(Active Profiles)
│ │ │ │ │ │ │ └── 合并默认配置文件(Default Profiles)
│ │ │ │ │ │ └── 父类构造器逐层返回
│ │ │ │ └── 返回至AbstractRefreshableConfigApplicationContext
│ │ └── 返回至AbstractXmlApplicationContext
│ └── 返回至ClassPathXmlApplicationContext构造器
├── **设置配置路径**
│ └── ClassPathXmlApplicationContext->>AbstractRefreshableConfigApplicationContext: setConfigLocations(configLocations)
│ ├── 校验参数:locations != null 且无null元素
│ ├── 初始化configLocations数组
│ └── **遍历每个配置路径**
│ └── 对每个location执行:
│ ├── 调用resolvePath(locations[i])解析路径
│ │ ├── 获取环境对象:getEnvironment()
│ │ │ ├── **若环境未初始化(environment == null)**
│ │ │ │ ├── 创建StandardEnvironment实例
│ │ │ │ │ ├── 调用AbstractEnvironment构造器初始化属性源和解析器
│ │ │ │ │ │ ├── 添加系统属性(systemProperties)和环境变量(systemEnvironment)到属性源
│ │ │ │ │ └── 返回环境对象
│ │ │ └── **若环境已初始化**:直接返回
│ │ ├── 调用环境解析占位符:resolveRequiredPlaceholders(path)
│ │ │ ├── PropertySourcesPropertyResolver创建PropertyPlaceholderHelper
│ │ │ ├── 递归解析占位符(支持嵌套和默认值)
│ │ │ ├── 处理循环引用和解析失败异常
│ │ │ └── 返回解析后的字符串
│ ├── 去除路径前后空格:resolvedPath.trim()
│ └── 存入configLocations数组
└── **启动容器刷新**
结尾
- refresh核心源码在下一章进行解析,因为此方法属于公共方法,AnnotationConfigApplicationContext内部也是用此类的进行容器刷新,以及后面的Springmvc同样也是,所以说refresh源码重中之重。