容器概述

cn.taketoday.context.ApplicationContext 接口代表了 IoC 容器, 负责实例化、配置和组装 bean。容器通过读取配置元数据来获取实例化、配置和组装对象的指令。 配置元数据可以用 XML、Java 注解或 Java 代码表示。它允许您表达组成应用程序的对象以及这些对象之间的丰富相互依赖关系。

框架提供了几个 ApplicationContext 接口的实现,在单体应用中,当你要使用 XML 配置来描述和配置 Bean 时 你可以使用 ClassPathXmlApplicationContext 或者 FileSystemXmlApplicationContext。 当然你也可以使用少量 XML 配置开启 Java 注解或代码作为元数据格式。

Configuration Metadata

IoC 容器消耗一种形式的配置元数据。这些配置元数据代表您作为应用程序开发者告诉 Infra 容器如何实例化、配置和组装应用程序中的对象。

传统上,配置元数据通常以简单直观的 XML 格式提供,这也是本章大部分内容用来传达 IoC 容器的关键概念和特性的方式。

XML 格式的元数据配置并不是唯一允许的配置元数据形式。IoC 容器本身与配置元数据的实际编写格式完全解耦。 如今,许多开发者选择使用 Java-based configuration 来配置他们的 Infra 应用程序。

有关在 Infra 容器中使用其他形式的元数据的信息,请参阅:

基础设施配置至少包括一个,通常是多个 bean 定义,容器必须管理这些定义。基于 XML 的配置元数据将这些 bean 配置为顶级 <beans/> 元素内的 <bean/> 元素。Java 配置通常使用 @Configuration 类中的 @Bean 注释方法。

这些 bean 定义对应于组成应用程序的实际对象。通常,您定义服务层对象、持久化层对象(如存储库或数据访问对象(DAO))、 表示层对象(如 Web 控制器)、基础设施对象(如 JPA EntityManagerFactory、JMS 队列等)。通常,不会在容器中配置细

以下示例显示了基于 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>
1 id 属性是一个字符串,用于标识单个 bean 定义。
2 class 属性定义了 bean 的类型,并使用完全限定的类名。

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

实例化一个容器

ApplicationContext 构造函数中提供的位置路径或路径是资源字符串,允许容器从各种外部资源加载配置元数据,例如本地文件系统、Java CLASSPATH 等等。

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

在了解了 Infra IoC 容器之后,您可能想进一步了解 Infra Resource 抽象( 如 资源 所述),它提供了一种方便的机制,用于从 URI 语法中定义的位置读取 InputStream。 具体而言,Resource 路径用于构建应用程序上下文,如 ApplicationContext 和资源路径 中所述。

下面的示例显示了服务层对象(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="cn.taketoday.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>

下面的示例显示了数据访问对象 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="cn.taketoday.samples.jpetstore.dao.jpa.JpaAccountDao">
    <!-- additional collaborators and configuration for this bean go here -->
  </bean>

  <bean id="itemDao" class="cn.taketoday.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>

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

组合基于 XML 的配置元数据

可以将Bean定义跨越多个XML文件是非常有用的。通常,每个独立的XML配置文件代表着架构中的一个逻辑层或模块。

您可以使用应用程序上下文构造函数从所有这些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>

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

可以使用相对 "../" 路径引用父目录中的文件,但不推荐这样做。这样做会创建对当前应用程序之外的文件的依赖关系。 特别是,不建议在 classpath: URL(例如,classpath:../services.xml)中使用此引用,其中运行时解析过程选择 “nearest” 类路径根,然后查找其父目录。类路径配置更改可能导致选择不同的、不正确的目录。

您始终可以使用完全限定的资源位置而不是相对路径:例如,file:C:/config/services.xmlclasspath:/config/services.xml。但是,请注意,您正在将应用程序的配置与特定的绝对位置耦合在一起。 通常最好为此类绝对位置保持间接性 - 例如,通过在运行时针对 JVM 系统属性解析的 "${…​}" 占位符。

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

使用容器

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();
ApplicationContext context = new GenericGroovyApplicationContext("services.groovy", "daos.groovy");

最灵活的变体是 GenericApplicationContext 与读取代理结合使用,例如,使用 XmlBeanDefinitionReader 读取 XML 文件,如下面的示例所示:

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

您可以在同一个 ApplicationContext 中混合使用这样的读取代理,从不同的配置源中读取 Bean 定义。

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