【附录】Spring框架多种容器实现类的区别?

38 阅读7分钟

此文是【Spring 容器详解】的支节点。

Spring框架提供了多种容器实现类,每种都有其特定的用途和实现方式。本文档将详细对比分析ClassPathXmlApplicationContextAnnotationConfigApplicationContextFileSystemXmlApplicationContextWebApplicationContext这几个主要的容器实现类,包括它们的区别、使用场景和源码实现差异。

1. 容器实现类概览

1.1 继承关系图

BeanFactory
↑
ApplicationContext
↑
ConfigurableApplicationContext
↑
AbstractApplicationContext
├── AbstractRefreshableApplicationContext
│   ├── AbstractRefreshableConfigApplicationContext
│   │   ├── ClassPathXmlApplicationContext
│   │   └── FileSystemXmlApplicationContext
│   └── (其他基于XML的实现)
└── GenericApplicationContext
    └── AnnotationConfigApplicationContext

(单独分支)
ApplicationContext
↑
WebApplicationContext
├── XmlWebApplicationContext
└── AnnotationConfigWebApplicationContext

1.1 主要区别总结

特性ClassPathXmlApplicationContextAnnotationConfigApplicationContextFileSystemXmlApplicationContextWebApplicationContext
配置方式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 核心特性

  1. 类路径资源解析:自动将相对路径转换为类路径资源
  2. XML配置解析:使用XmlBeanDefinitionReader解析XML配置
  3. 继承支持:支持父容器的配置继承
  4. 延迟刷新:支持手动控制容器刷新时机

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 核心特性

  1. 注解驱动配置:支持@Component、@Service、@Repository、@Controller等注解
  2. 配置类支持:支持@Configuration、@Bean等配置注解
  3. 包扫描:自动扫描指定包下的组件
  4. 条件配置:支持@Profile、@Conditional等条件注解
  5. 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 核心特性

  1. 文件系统资源:支持从文件系统加载配置文件
  2. 绝对路径支持:支持完整的文件系统路径
  3. 相对路径支持:支持相对于当前工作目录的路径
  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 核心特性

  1. Web作用域:支持request、session、application作用域
  2. Servlet上下文:提供对ServletContext的访问
  3. Web环境:支持Web相关的环境配置
  4. 主题支持:支持主题和国际化
  5. 文件上传:支持文件上传功能

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中等中等外部配置应用
WebApplicationContextWeb应用

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方法
}