1. 概述

本文深入剖析 Spring 框架中的核心组件 —— ApplicationContext。作为 Spring IoC 容器的代表,它是整个应用上下文的中枢。理解它,是掌握 Spring 的关键一步。

2. ApplicationContext 接口

Spring 框架的核心特性之一就是 IoC(控制反转)容器。这个容器负责管理应用中所有对象的生命周期和依赖关系,通过依赖注入(DI)实现控制反转。

BeanFactoryApplicationContext 这两个接口代表了 Spring 的 IoC 容器:

  • BeanFactory:是访问 Spring 容器的根接口,提供了管理 Bean 的基础功能。
  • ApplicationContextBeanFactory 的子接口,因此它天然继承了 BeanFactory 的所有能力。

更重要的是,ApplicationContext 提供了更丰富的、面向企业级应用的功能,这才是我们日常开发中首选它的原因:

  • 国际化(i18n)支持:通过 MessageSource 实现消息解析。
  • 事件发布与监听:基于 ApplicationEventApplicationListener
  • 资源访问:方便地加载文件、URL 等资源。
  • 应用层上下文:如 Web 应用中的 WebApplicationContext

简单来说,BeanFactory 是“能用”,而 ApplicationContext 是“好用且功能完备”。

3. 什么是 Spring Bean?

在深入 ApplicationContext 之前,必须明确 Spring Bean 的概念。

Spring Bean 是指由 Spring 容器实例化、装配和管理的任何对象。

⚠️ 一个常见的误区:是否要把应用中的所有对象都配置成 Bean?

答案是否定的。根据 Spring 官方的最佳实践:

  • 应该配置为 Bean 的对象
    • 服务层对象(Service)
    • 数据访问对象(DAO/Repository)
    • 表现层对象(Controller)
    • 基础设施对象(如 Hibernate SessionFactory, JMS Queue 等)
  • 不应配置为 Bean 的对象
    • 细粒度的领域模型对象(Domain Objects)。这些对象通常由 DAO 或业务逻辑在运行时创建和加载。

下面是一个典型的 Service 类,我们将把它作为 Spring Bean 使用:

public class AccountService {

  @Autowired
  private AccountRepository accountRepository;

  // getters and setters
}

4. 在容器中配置 Bean

ApplicationContext 的核心职责就是管理 Bean,因此我们需要向容器提供 Bean 的配置信息。Spring 支持多种配置方式,各有适用场景。

4.1. 基于 Java 的配置(推荐)

这是 Spring 3.0 之后最推荐的配置方式,类型安全、易于重构。

核心注解:

  • @Configuration:标记一个类为配置类。
  • @Bean:标记一个方法,其返回值将被注册为一个 Bean。

示例:创建一个配置类来定义 AccountServiceAccountRepository

@Configuration
public class AccountConfig {

  @Bean
  public AccountService accountService() {
    return new AccountService(accountRepository());
  }

  @Bean
  public AccountRepository accountRepository() {
    return new AccountRepository();
  }
}

4.2. 基于注解的配置

从 Spring 2.5 开始引入,是迈向“零 XML”的第一步。

关键点

  1. 需要在 XML 中启用注解扫描。
  2. 使用 @Component, @Service, @Repository, @Controller 等注解标记类。
  3. 使用 @Autowired, @Qualifier 等注解进行依赖注入。

示例:

  1. 创建 XML 配置文件 user-bean-config.xml 来启用注解扫描:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">
  
  <context:annotation-config/>
  <context:component-scan base-package="com.baeldung.applicationcontext"/>
</beans>

⚠️ <context:annotation-config/> 启用注解驱动的依赖注入。 <context:component-scan> 指定 Spring 扫描带注解类的包路径。

  1. 创建 UserService 类并用 @Component 标记:
@Component
public class UserService {
  // user service code
}
  1. 测试加载:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationcontext/user-bean-config.xml");
UserService userService = context.getBean(UserService.class);
assertNotNull(userService);

4.3. 基于 XML 的配置

这是 Spring 最传统的配置方式,虽然现在用得少了,但在维护老项目时仍会遇到。

所有 Bean 的定义和依赖关系都写在 XML 文件中。

示例:创建 account-bean-config.xml 文件:

<?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="accountService" class="com.baeldung.applicationcontext.AccountService">
    <constructor-arg name="accountRepository" ref="accountRepository" />
  </bean>
    
  <bean id="accountRepository" class="com.baeldung.applicationcontext.AccountRepository" />
</beans>

5. ApplicationContext 的类型

Spring 提供了多种 ApplicationContext 实现,以适应不同环境。

5.1. AnnotationConfigApplicationContext

适用于纯 Java 配置的非 Web 应用(如独立应用、单元测试)。

它可以接收 @Configuration@Component 等注解类作为输入。

ApplicationContext context = new AnnotationConfigApplicationContext(AccountConfig.class);
AccountService accountService = context.getBean(AccountService.class);

5.2. AnnotationConfigWebApplicationContext

这是 AnnotationConfigApplicationContextWeb 版本

当你在 web.xml 中配置 ContextLoaderListener 或 Spring MVC 的 DispatcherServlet 时,可以使用它。

从 Spring 3.0 开始,也可以通过实现 WebApplicationInitializer 接口进行编程式配置

public class MyWebApplicationInitializer implements WebApplicationInitializer {

  public void onStartup(ServletContext container) throws ServletException {
    AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
    context.register(AccountConfig.class);
    context.setServletContext(container);

    // servlet configuration
  }
}

5.3. XmlWebApplicationContext

如果你的 Web 应用使用 XML 配置,则使用此类。

配置方式与 AnnotationConfigWebApplicationContext 类似,同样支持 web.xmlWebApplicationInitializer

public class MyXmlWebApplicationInitializer implements WebApplicationInitializer {

  public void onStartup(ServletContext container) throws ServletException {
    XmlWebApplicationContext context = new XmlWebApplicationContext();
    context.setConfigLocation("/WEB-INF/spring/applicationContext.xml");
    context.setServletContext(container);

    // Servlet configuration
  }
}

5.4. FileSystemXmlApplicationContext

用于从文件系统路径或 URL 加载 XML 配置文件。

常见于需要手动指定配置文件路径的独立应用或测试脚本。

String path = "C:/myProject/src/main/resources/applicationcontext/account-bean-config.xml";

ApplicationContext context = new FileSystemXmlApplicationContext(path);
AccountService accountService = context.getBean("accountService", AccountService.class);

5.5. ClassPathXmlApplicationContext

用于从类路径(classpath) 加载 XML 配置文件。

这是测试中最常用的加载方式,也适用于嵌入在 JAR 包中的应用上下文。

ApplicationContext context = new ClassPathXmlApplicationContext("applicationcontext/account-bean-config.xml");
AccountService accountService = context.getBean("accountService", AccountService.class);

6. ApplicationContext 的附加功能

除了管理 Bean,ApplicationContext 还提供了强大的附加功能。

6.1. 消息解析(国际化)

ApplicationContext 通过继承 MessageSource 接口来支持消息解析和国际化。

主要实现类:

  • ResourceBundleMessageSource最常用,基于 JDK 的 ResourceBundle
  • StaticMessageSource:适合测试,可编程添加消息。
  • ReloadableResourceBundleMessageSource:支持从任意资源位置加载,并可热重载属性文件。

使用步骤

  1. 在类路径下创建 messages.properties 文件:
account.name=TestAccount
  1. 在配置类中定义 MessageSource Bean:
@Bean
public MessageSource messageSource() {
  ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
  messageSource.setBasename("config/messages"); // 注意路径
  return messageSource;
}
  1. AccountService 中注入 MessageSource
@Autowired
private MessageSource messageSource;
  1. 获取消息:
messageSource.getMessage("account.name", null, Locale.ENGLISH);

6.2. 事件处理

ApplicationContext 提供了内置的事件发布/订阅机制。

  • 核心组件ApplicationEvent(事件)和 ApplicationListener(监听器)。
  • 内置事件:如 ContextStartedEvent, ContextStoppedEvent, ContextClosedEvent
  • 自定义事件:可以轻松创建自己的业务事件。

这为解耦组件、实现观察者模式提供了非常方便的手段。

7. 总结

本文系统性地介绍了 Spring ApplicationContext

  • 它是 BeanFactory 的增强版,提供了企业级功能。
  • 掌握了三种 Bean 配置方式:Java、注解、XML。
  • 了解了五种主要的 ApplicationContext 实现及其适用场景。
  • 学习了其核心附加功能:国际化和事件处理。

ApplicationContext 是 Spring 应用的“心脏”,理解透彻后,再看 Spring Boot 的自动配置,就会豁然开朗。真正的“踩坑”少,开发效率高。

本文完整代码示例可参考 GitHub 仓库:https://github.com/baeldung/tutorials


原始标题:The Spring ApplicationContext