跳到主要内容

容器概述

ChatGPT-4o-mini 中英对照 Container Overview

org.springframework.context.ApplicationContext 接口代表 Spring IoC 容器,负责实例化、配置和组装 bean。容器通过读取配置元数据获得有关要实例化、配置和组装的组件的指令。配置元数据可以表示为带注解的组件类、具有工厂方法的配置类,或外部 XML 文件或 Groovy 脚本。无论哪种格式,您都可以组合您的应用程序以及这些组件之间丰富的相互依赖关系。

多个 ApplicationContext 接口的实现是 Spring 核心的一部分。在独立应用程序中,通常会创建一个 AnnotationConfigApplicationContextClassPathXmlApplicationContext 的实例。

在大多数应用场景中,不需要显式的用户代码来实例化一个或多个 Spring IoC 容器。例如,在一个普通的 Web 应用场景中,应用程序的 web.xml 文件中一个简单的样板 Web 描述符 XML 就足够了(参见 Web 应用程序的便捷 ApplicationContext 实例化)。在 Spring Boot 场景中,应用程序上下文会根据常见的设置约定为您隐式引导。

以下图示展示了 Spring 工作的高层次视图。您的应用程序类与配置元数据相结合,以便在 ApplicationContext 被创建和初始化后,您拥有一个完全配置且可执行的系统或应用程序。

container magic

图 1. Spring IoC 容器

配置元数据

如前面的图所示,Spring IoC 容器使用一种配置元数据。这种配置元数据表示您作为应用程序开发人员如何告诉 Spring 容器实例化、配置和组装您应用程序中的组件。

Spring IoC 容器本身与实际编写的配置元数据的格式完全解耦。如今,许多开发者为他们的 Spring 应用选择 基于 Java 的配置

Spring 配置至少包含一个,通常包含多个容器必须管理的 bean 定义。Java 配置通常在 @Configuration 类中使用 @Bean 注解的方法,每个方法对应一个 bean 定义。

这些 bean 定义对应于构成您应用程序的实际对象。通常,您会定义服务层对象、持久层对象,例如存储库或数据访问对象(DAOs)、表示层对象,例如 Web 控制器、基础设施对象,例如 JPA EntityManagerFactory、JMS 队列等。通常,容器中不会配置细粒度的领域对象,因为通常由存储库和业务逻辑来创建和加载领域对象。

XML 作为外部配置 DSL

基于 XML 的配置元数据将这些 bean 配置为顶层 <beans/> 元素内的 <bean/> 元素。以下示例显示了基于 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
https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="..." class="..."> // <1> // <2>
<!-- collaborators and configuration for this bean go here -->
</bean>

<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>

<!-- more bean definitions go here -->

</beans>
xml
  • id 属性是一个字符串,用于标识单个 bean 定义。

  • class 属性定义了 bean 的类型,并使用完全限定的类名。

id 属性的值可以用来引用协作对象。此示例中未显示引用协作对象的 XML。有关更多信息,请参见 Dependencies

要实例化一个容器,需要将 XML 资源文件的位置路径或路径提供给 ClassPathXmlApplicationContext 构造函数,以便容器从多种外部资源加载配置元数据,例如本地文件系统、Java CLASSPATH 等。

ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
java
备注

在了解了 Spring 的 IoC 容器后,您可能想要了解更多关于 Spring 的 Resource 抽象(如 Resources 中所述),它提供了一种方便的机制,用于从以 URI 语法定义的位置读取 InputStream。特别是,Resource 路径用于构建应用程序上下文,如 Application Contexts and Resource Paths 中所述。

以下示例展示了服务层对象 (services.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
https://www.springframework.org/schema/beans/spring-beans.xsd">

<!-- services -->

<bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
<property name="accountDao" ref="accountDao"/>
<property name="itemDao" ref="itemDao"/>
<!-- additional collaborators and configuration for this bean go here -->
</bean>

<!-- more bean definitions for services go here -->

</beans>
xml

以下示例展示了数据访问对象 daos.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
https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="accountDao"
class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao">
<!-- additional collaborators and configuration for this bean go here -->
</bean>

<bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao">
<!-- additional collaborators and configuration for this bean go here -->
</bean>

<!-- more bean definitions for data access objects go here -->

</beans>
xml

在前面的示例中,服务层由 PetStoreServiceImpl 类和两个数据访问对象 JpaAccountDaoJpaItemDao 组成(基于 JPA 对象关系映射标准)。property name 元素指的是 JavaBean 属性的名称,而 ref 元素指的是另一个 bean 定义的名称。idref 元素之间的这种链接表达了协作对象之间的依赖关系。有关配置对象依赖关系的详细信息,请参见 Dependencies

组合基于 XML 的配置元数据

将 bean 定义跨多个 XML 文件是很有用的。通常,每个单独的 XML 配置文件代表您架构中的一个逻辑层或模块。

您可以使用 ClassPathXmlApplicationContext 构造函数从 XML 片段加载 bean 定义。该构造函数接受多个 Resource 位置,如 前一部分 所示。或者,使用一个或多个 <import/> 元素从另一个文件或多个文件加载 bean 定义。以下示例演示了如何做到这一点:

<beans>
<import resource="services.xml"/>
<import resource="resources/messageSource.xml"/>
<import resource="/resources/themeSource.xml"/>

<bean id="bean1" class="..."/>
<bean id="bean2" class="..."/>
</beans>
xml

在前面的示例中,外部 bean 定义是从三个文件加载的:services.xmlmessageSource.xmlthemeSource.xml。所有位置路径都是相对于执行导入的定义文件,因此 services.xml 必须与执行导入的文件位于同一目录或类路径位置,而 messageSource.xmlthemeSource.xml 必须位于导入文件位置下的 resources 位置。如您所见,前导斜杠会被忽略。然而,考虑到这些路径是相对的,最好根本不使用斜杠。被导入文件的内容,包括顶层的 <beans/> 元素,必须根据 Spring Schema 是有效的 XML bean 定义。

备注

可以使用相对 "../" 路径引用父目录中的文件,但不推荐这样做。这样会导致对当前应用程序之外的文件产生依赖。特别是,对于 classpath: URLs(例如,classpath:../services.xml),不推荐使用这种引用,因为运行时解析过程会选择“最近”的类路径根目录,然后查看其父目录。类路径配置的更改可能会导致选择不同的、不正确的目录。

您始终可以使用完全限定的资源位置,而不是相对路径:例如,file:C:/config/services.xmlclasspath:/config/services.xml。但是,请注意,您将应用程序的配置与特定的绝对位置耦合在一起。通常,更好的做法是为这些绝对位置保留一个间接引用 — 例如,通过在运行时解析的 "${…​}" 占位符来实现,该占位符会根据 JVM 系统属性进行解析。

命名空间本身提供了导入指令功能。除了普通的 bean 定义之外,Spring 提供了一些 XML 命名空间,以便进行进一步的配置功能,例如 contextutil 命名空间。

Groovy Bean 定义 DSL

作为外部化配置元数据的进一步示例,Bean 定义也可以用 Spring 的 Groovy Bean Definition DSL 表达,这在 Grails 框架中是众所周知的。通常,这种配置存放在一个 ".groovy" 文件中,其结构如下例所示:

beans {
dataSource(BasicDataSource) {
driverClassName = "org.hsqldb.jdbcDriver"
url = "jdbc:hsqldb:mem:grailsDB"
username = "sa"
password = ""
settings = [mynew:"setting"]
}
sessionFactory(SessionFactory) {
dataSource = dataSource
}
myService(MyService) {
nestedBean = { AnotherBean bean ->
dataSource = dataSource
}
}
}
groovy

这种配置风格在很大程度上等同于 XML bean 定义,甚至支持 Spring 的 XML 配置命名空间。它还允许通过 importBeans 指令导入 XML bean 定义文件。

使用容器

ApplicationContext 是一个高级工厂的接口,能够维护不同 bean 及其依赖关系的注册表。通过使用方法 T getBean(String name, Class<T> requiredType),您可以检索您的 bean 实例。

ApplicationContext 让你读取 bean 定义并访问它们,以下示例展示了这一点:

// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

// retrieve configured instance
PetStoreService service = context.getBean("petStore", PetStoreService.class);

// use configured instance
List<String> userList = service.getUsernameList();
java

使用 Groovy 配置,启动过程看起来非常相似。它有一个不同的上下文实现类,该类支持 Groovy(但也理解 XML bean 定义)。以下示例展示了 Groovy 配置:

ApplicationContext context = new GenericGroovyApplicationContext("services.groovy", "daos.groovy");
java

最灵活的变体是 GenericApplicationContext 与读取器委托的组合 — 例如,使用 XmlBeanDefinitionReader 处理 XML 文件,如下例所示:

GenericApplicationContext context = new GenericApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml");
context.refresh();
java

您还可以使用 GroovyBeanDefinitionReader 来处理 Groovy 文件,如以下示例所示:

GenericApplicationContext context = new GenericApplicationContext();
new GroovyBeanDefinitionReader(context).loadBeanDefinitions("services.groovy", "daos.groovy");
context.refresh();
java

您可以在同一个 ApplicationContext 上混合和匹配这样的读取器委托,从不同的配置源读取 bean 定义。

您可以使用 getBean 来检索您的 bean 实例。ApplicationContext 接口还有其他一些方法用于检索 bean,但理想情况下,您的应用程序代码不应该使用它们。实际上,您的应用程序代码不应该调用 getBean() 方法,因此完全不依赖于 Spring API。例如,Spring 与 web 框架的集成为各种 web 框架组件(如控制器和 JSF 管理的 bean)提供了依赖注入,让您可以通过元数据(例如自动装配注解)声明对特定 bean 的依赖。