此文是【Spring 容器详解】的支节点。
Spring框架提供了多种容器实现类,每种都有其特定的用途和实现方式。本文档将详细对比分析ClassPathXmlApplicationContext、AnnotationConfigApplicationContext、FileSystemXmlApplicationContext和WebApplicationContext这几个主要的容器实现类,包括它们的区别、使用场景和源码实现差异。
1. 容器实现类概览
1.1 继承关系图
BeanFactory
↑
ApplicationContext
↑
ConfigurableApplicationContext
↑
AbstractApplicationContext
├── AbstractRefreshableApplicationContext
│ ├── AbstractRefreshableConfigApplicationContext
│ │ ├── ClassPathXmlApplicationContext
│ │ └── FileSystemXmlApplicationContext
│ └── (其他基于XML的实现)
└── GenericApplicationContext
└── AnnotationConfigApplicationContext
(单独分支)
ApplicationContext
↑
WebApplicationContext
├── XmlWebApplicationContext
└── AnnotationConfigWebApplicationContext
1.1 主要区别总结
| 特性 | ClassPathXmlApplicationContext | AnnotationConfigApplicationContext | FileSystemXmlApplicationContext | WebApplicationContext |
|---|---|---|---|---|
| 配置方式 | XML文件 | 注解类 | XML文件 | XML或注解 |
| 资源位置 | 类路径 | 类路径 | 文件系统 | 类路径或文件系统 |
| 使用场景 | 传统XML配置 | 现代注解配置 | 外部配置文件 | Web应用 |
| 启动方式 | 指定XML文件路径 | 指定配置类 | 指定绝对路径 | Web容器启动 |
| 功能特性 | 基础功能 | 现代特性支持 | 基础功能 | Web特性支持 |
2. ClassPathXmlApplicationContext 详解
2.1 基本介绍
ClassPathXmlApplicationContext是基于类路径XML配置的Spring容器实现,是Spring最早提供的容器实现之一。
2.2 使用方式
// 1. 基本使用
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 2. 多个配置文件
ApplicationContext context = new ClassPathXmlApplicationContext(
"spring/applicationContext.xml",
"spring/spring-mvc.xml"
);
// 3. 指定父容器
ApplicationContext parentContext = new ClassPathXmlApplicationContext("parent.xml");
ApplicationContext childContext = new ClassPathXmlApplicationContext(
new String[]{"child.xml"}, parentContext
);
// 4. 延迟刷新
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"applicationContext.xml", false
);
// 手动刷新
context.refresh();
2.3 源码实现分析
public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
private Resource[] configResources;
// 构造函数1:指定配置文件位置
public ClassPathXmlApplicationContext(String... configLocations) {
this(configLocations, true, null);
}
// 构造函数2:指定配置文件位置和是否自动刷新
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
// 构造函数3:指定配置文件位置和父容器
public ClassPathXmlApplicationContext(String[] configLocations, ApplicationContext parent) {
this(configLocations, true, parent);
}
// 设置配置文件位置
public void setConfigLocations(String... locations) {
if (locations != null) {
Assert.noNullElements(locations, "Config locations must not be null");
this.configResources = new Resource[locations.length];
for (int i = 0; i < locations.length; i++) {
this.configResources[i] = getResource(locations[i]);
}
} else {
this.configResources = null;
}
}
// 获取配置文件资源
@Override
protected Resource[] getConfigResources() {
return this.configResources;
}
}
2.4 核心特性
- 类路径资源解析:自动将相对路径转换为类路径资源
- XML配置解析:使用XmlBeanDefinitionReader解析XML配置
- 继承支持:支持父容器的配置继承
- 延迟刷新:支持手动控制容器刷新时机
2.5 适用场景
- 传统的XML配置方式
- 需要从类路径加载配置文件
- 简单的Spring应用
- 学习和演示用途
3. AnnotationConfigApplicationContext 详解
3.1 基本介绍
AnnotationConfigApplicationContext是基于注解配置的Spring容器实现,是Spring 3.0引入的现代配置方式。
3.2 使用方式
// 1. 基本使用 - 指定配置类
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 2. 多个配置类
ApplicationContext context = new AnnotationConfigApplicationContext(
AppConfig.class,
DatabaseConfig.class,
SecurityConfig.class
);
// 3. 包扫描方式
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.scan("com.example.config", "com.example.service");
context.refresh();
// 4. 注册Bean定义
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(AppConfig.class);
context.registerBean("customBean", CustomBean.class);
context.refresh();
// 5. 环境配置
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.getEnvironment().setActiveProfiles("dev");
context.register(AppConfig.class);
context.refresh();
3.3 源码实现分析
public class AnnotationConfigApplicationContext extends GenericApplicationContext {
private final AnnotatedBeanDefinitionReader reader;
private final ClassPathBeanDefinitionScanner scanner;
// 默认构造函数
public AnnotationConfigApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
// 指定配置类的构造函数
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
this();
register(componentClasses);
refresh();
}
// 指定包路径的构造函数
public AnnotationConfigApplicationContext(String... basePackages) {
this();
scan(basePackages);
refresh();
}
// 注册配置类
public void register(Class<?>... componentClasses) {
Assert.notEmpty(componentClasses, "At least one component class must be specified");
this.reader.register(componentClasses);
}
// 扫描包
public void scan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
this.scanner.scan(basePackages);
}
}
3.4 核心特性
- 注解驱动配置:支持@Component、@Service、@Repository、@Controller等注解
- 配置类支持:支持@Configuration、@Bean等配置注解
- 包扫描:自动扫描指定包下的组件
- 条件配置:支持@Profile、@Conditional等条件注解
- Bean注册:支持动态注册Bean定义
4. FileSystemXmlApplicationContext 详解
4.1 基本介绍
FileSystemXmlApplicationContext是基于文件系统XML配置的Spring容器实现,允许从文件系统的绝对路径加载配置文件。
4.2 使用方式
// 1. 基本使用 - 绝对路径
ApplicationContext context = new FileSystemXmlApplicationContext("C:/config/applicationContext.xml");
// 2. 多个配置文件
ApplicationContext context = new FileSystemXmlApplicationContext(
"C:/config/spring/applicationContext.xml",
"C:/config/spring/spring-mvc.xml"
);
// 3. 相对路径(相对于当前工作目录)
ApplicationContext context = new FileSystemXmlApplicationContext("./config/applicationContext.xml");
// 4. 指定父容器
ApplicationContext parentContext = new FileSystemXmlApplicationContext("C:/config/parent.xml");
ApplicationContext childContext = new FileSystemXmlApplicationContext(
new String[]{"C:/config/child.xml"}, parentContext
);
// 5. 延迟刷新
FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext(
"C:/config/applicationContext.xml", false
);
context.refresh();
4.3 源码实现分析
public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {
// 构造函数1:指定配置文件路径
public FileSystemXmlApplicationContext(String... configLocations) {
this(configLocations, true, null);
}
// 构造函数2:指定配置文件路径和是否自动刷新
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
// 构造函数3:指定配置文件路径和父容器
public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) {
this(configLocations, true, parent);
}
// 重写资源加载方法,使用文件系统资源加载器
@Override
protected Resource getResourceByPath(String path) {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
}
4.4 核心特性
- 文件系统资源:支持从文件系统加载配置文件
- 绝对路径支持:支持完整的文件系统路径
- 相对路径支持:支持相对于当前工作目录的路径
- 资源加载器:使用FileSystemResource加载资源
5. WebApplicationContext 详解
5.1 基本介绍
WebApplicationContext是专门为Web应用设计的Spring容器,提供了Web相关的功能和特性。
5.2 实现类
// 1. XmlWebApplicationContext - 基于XML的Web容器
public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {
// XML配置支持
}
// 2. AnnotationConfigWebApplicationContext - 基于注解的Web容器
public class AnnotationConfigWebApplicationContext extends AnnotationConfigApplicationContext
implements ConfigurableWebApplicationContext {
// 注解配置支持
}
5.3 使用方式
// 1. 在web.xml中配置
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
// 2. 在Spring MVC中配置
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
// 3. Java配置方式
@Configuration
@EnableWebMvc
@ComponentScan("com.example.controller")
public class WebConfig implements WebMvcConfigurer {
// Web配置
}
5.4 源码实现分析
public interface WebApplicationContext extends ApplicationContext {
// Web应用相关的常量
String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
String SCOPE_REQUEST = "request";
String SCOPE_SESSION = "session";
String SCOPE_APPLICATION = "application";
// 获取Servlet上下文
ServletContext getServletContext();
}
public abstract class AbstractRefreshableWebApplicationContext extends AbstractRefreshableApplicationContext
implements ConfigurableWebApplicationContext {
// Web应用上下文属性
private String namespace;
private String[] configLocations;
private WebApplicationContext parent;
private ServletContext servletContext;
// 设置Servlet上下文
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
// 获取Servlet上下文
@Override
public ServletContext getServletContext() {
return this.servletContext;
}
// 创建环境
@Override
protected ConfigurableEnvironment createEnvironment() {
return new StandardServletEnvironment();
}
}
5.5 核心特性
- Web作用域:支持request、session、application作用域
- Servlet上下文:提供对ServletContext的访问
- Web环境:支持Web相关的环境配置
- 主题支持:支持主题和国际化
- 文件上传:支持文件上传功能
6. 源码实现差异分析
6.1 资源加载差异
// ClassPathXmlApplicationContext - 类路径资源加载
public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
@Override
protected Resource getResourceByPath(String path) {
// 使用ClassPathResource加载类路径资源
return new ClassPathResource(path);
}
}
// FileSystemXmlApplicationContext - 文件系统资源加载
public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {
@Override
protected Resource getResourceByPath(String path) {
// 使用FileSystemResource加载文件系统资源
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
}
// AnnotationConfigApplicationContext - 注解配置加载
public class AnnotationConfigApplicationContext extends GenericApplicationContext {
private final AnnotatedBeanDefinitionReader reader;
private final ClassPathBeanDefinitionScanner scanner;
// 使用注解读取器和包扫描器
public AnnotationConfigApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
}
6.2 Bean定义加载差异
// XML配置容器的Bean定义加载
public abstract class AbstractXmlApplicationContext extends AbstractRefreshableApplicationContext {
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException {
// 创建XML Bean定义读取器
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 配置读取器
beanDefinitionReader.setEnvironment(getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 加载Bean定义
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
}
// 注解配置容器的Bean定义加载
public class AnnotationConfigApplicationContext extends GenericApplicationContext {
public void register(Class<?>... componentClasses) {
Assert.notEmpty(componentClasses, "At least one component class must be specified");
this.reader.register(componentClasses);
}
public void scan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
this.scanner.scan(basePackages);
}
}
6.3 环境配置差异
// 普通应用容器的环境配置
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
@Override
protected ConfigurableEnvironment createEnvironment() {
return new StandardEnvironment();
}
}
// Web应用容器的环境配置
public abstract class AbstractRefreshableWebApplicationContext extends AbstractRefreshableApplicationContext
implements ConfigurableWebApplicationContext {
@Override
protected ConfigurableEnvironment createEnvironment() {
return new StandardServletEnvironment();
}
}
7. 性能对比分析
7.1 启动性能
| 容器类型 | 启动时间 | 内存占用 | 适用场景 |
|---|---|---|---|
| ClassPathXmlApplicationContext | 中等 | 中等 | 中小型应用 |
| AnnotationConfigApplicationContext | 快 | 低 | 现代应用 |
| FileSystemXmlApplicationContext | 中等 | 中等 | 外部配置应用 |
| WebApplicationContext | 慢 | 高 | Web应用 |
7.2 运行时性能
// 性能测试示例
public class ContainerPerformanceTest {
public static void main(String[] args) {
// 测试ClassPathXmlApplicationContext
long startTime = System.currentTimeMillis();
ApplicationContext xmlContext = new ClassPathXmlApplicationContext("applicationContext.xml");
long xmlTime = System.currentTimeMillis() - startTime;
// 测试AnnotationConfigApplicationContext
startTime = System.currentTimeMillis();
ApplicationContext annotationContext = new AnnotationConfigApplicationContext(AppConfig.class);
long annotationTime = System.currentTimeMillis() - startTime;
System.out.println("XML配置启动时间: " + xmlTime + "ms");
System.out.println("注解配置启动时间: " + annotationTime + "ms");
// 测试Bean获取性能
startTime = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
xmlContext.getBean("userService");
}
long xmlBeanTime = System.currentTimeMillis() - startTime;
startTime = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
annotationContext.getBean("userService");
}
long annotationBeanTime = System.currentTimeMillis() - startTime;
System.out.println("XML配置Bean获取时间: " + xmlBeanTime + "ms");
System.out.println("注解配置Bean获取时间: " + annotationBeanTime + "ms");
}
}
8. 最佳实践建议
8.1 选择建议
// 1. 新项目推荐使用注解配置
@Configuration
@EnableWebMvc
@ComponentScan("com.example")
public class AppConfig {
// 现代Spring应用配置
}
// 2. 遗留项目可以继续使用XML配置
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 3. 外部配置文件使用文件系统配置
ApplicationContext context = new FileSystemXmlApplicationContext("/etc/app/config.xml");
// 4. Web应用使用Web容器
// 在web.xml中配置ContextLoaderListener
8.2 混合配置
// 混合使用XML和注解配置
@Configuration
@ImportResource("classpath:legacy-config.xml")
@ComponentScan("com.example")
public class MixedConfig {
@Bean
public DataSource dataSource() {
// 新的数据源配置
return new HikariDataSource();
}
}
// 或者使用XML配置导入注解配置
<context:component-scan base-package="com.example"/>
<context:annotation-config/>
<import resource="classpath:annotation-config.xml"/>
8.3 配置管理
// 1. 环境相关配置
@Configuration
@Profile("dev")
public class DevConfig {
// 开发环境配置
}
@Configuration
@Profile("prod")
public class ProdConfig {
// 生产环境配置
}
// 2. 条件配置
@Configuration
@ConditionalOnClass(DataSource.class)
public class DatabaseConfig {
// 条件配置
}
// 3. 配置属性绑定
@Configuration
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private String name;
private int port;
// getter和setter方法
}