4.2 容器概述

org.springframework.context.ApplicationContext 接口代表了 Spring 的 IoC 容器,负责实例化,配置和装配上述的 bean。容器获得指示来实例化某对象,配置并装 配,这都是通过读取配置元数据实现的。配置元数据在 XML 中,Java 注解或 Java 代码中来 表示。它允许你表达编写应用程序的对象,还有对象间丰富的相互依存的关系。

ApplicationContext 接口的一些实现使用 Spring 开箱的支持。在独立的应用程序 中 , 通 常 是 来 创 建 ClassPathXmlApplicationContext FileSystemXmlApplicationContext 的实例。XML 是定义配置元数据的传统格式,你 可以指示容器使用 Java 的注解或是代码作为元数据的格式,提供少量的 XML 配置声明来开 启对这些额外的元数据格式的支持。

在很多应用场景中,明确的用户代码不需要实例化一个或者多个 Spring IoC 容器的实例。 比如,在 Web 应用场景中,在应用程序的 web.xml 中简单的八(左右)行样板 J2EE 描述 符 XML 文件通常就够了(参考 4.14.4 节“对 Web 应用程序方便的应用上下文实例化”)。如 果你正使用 SpringSource 的工具套件,Eclipse 支持的开发环境或者是 Spring ROO 这样的样板 配置,就可以容易地被创建,点几下鼠标或按键就可以了。

下图是 Spring 如何工作的高级别视图。你的应用程序类联合配置元数据,所以在 ApplicationContext 被创建和实例化后,就得到了一个完全配置的可执行系统或程序。

image

Spring 的 IoC 容器

4.2.1 配置元数据

正如上图所示,Spring 的 IoC 容器处理配置元数据的一种形式;这个配置元数据代表了 你作为应用开发人员是如何告诉 Spring 容器在你的应用程序中来实例化,配置并装配对象 的。

配置元数据传统上是以直观的 XML 格式提供的,这是本章的大部分内容使用它来传达 Spring IoC 容器关键概念和功能。

注意

基于 XML 的元数据并不是唯一的配置元数据格式。这种配置元数据真正写入时,Spring 的 IoC 容器本身和这种格式完全脱钩。

关于 Spring 容器使用元数据格式的信息,可以参考:

基于注解的配置(4.9 节):Spring 2.5 引入了对基于注解元数据的支持。

基于 Java 的配置(4.12 节):从 Spring 3.0 开始,很多由 Spring JavaConfig 项目提供的特 性称为 Spring Framework 的核心。因此你可以在应用程序外部来定义 bean,使用 Java 代码而不是 XML 文件。要使用这些新的特性,请参考@Configuration,@Bean,

@Import 和@DependsOn 注解

Spring 配置典型的是最少由一个容器必须管理的 bean 定义构成。基于 XML 的配置元数据展示了这些 bean 的配置是用在顶级<beans/>元素中的<bean/>元素完成的。

这些 bean 的定义对应构成应用程序中真实的对象。比如你定义的服务层的对象,数据访问对象(Data Access Object,DAO),表示对象比如 Struts 的 Action 实例,基础设置对象 比如 Hibernate 的 SesstionFactories,JMS 的 Queues 等这些典型的例子。而不用在容 器中定义细粒度的领域模型对象,因为这通常是由 DAO 和业务逻辑负责创建并加载的领域 对象。但是你也可以使用 Spring 和 AspectJ 整合来配置创建在 IoC 容器控制之外的对象。参 考使用在 Spring 中使用 AspectJ 来对领域对象进行依赖注入(8.8.1 节)。

下面的示例展示了基本的基于 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-3.0.xsd">
    <bean id="..." class="...">
        <!-- 这个bean的合作者和配置在这里编写 -->
    </bean>
    <bean id="..." class="...">
        <!-- 这个bean的合作者和配置在这里编写 -->
    </bean>
    <!-- 更多的bean定义在这里编写 -->
</beans>

id 属性是一个字符串,用来标识定义的独立的 bean。class 属性定义了 bean 的类型, 需要使用类的完全限定名。id 属性的值指的就是协作对象。指写作对象的 XML 在这个示例 中没有展示;参考依赖(4.4 节)来获取更多信息。

4.2.2 实例化容器

实例化 Spring 的 IoC 容 器 是 很 简 单 的 。 定 位 路 径 或 所 有 路 径 提 供 给 ApplicationContext 的构造方法,实际上是表示资源的字符串,它就允许容器从各种外 部资源比如本地文件系统,Java 的 CLASSPATH 等来加载配置元数据

ApplicationContext context =
    new ClassPathXmlApplicationContext(new String[] {"services.xml",
    "daos.xml"});

注意

在学习过 Spring 的 IoC 容器之后,你可能想更多了解 Spring 的 Resource 抽象,这在 第 5 章,资源中会描述,它提供了一个从定义 URI 语法的位置读取输入流的简便机制。特别 是,Resource 路径用来构建应用程序上下文,这会在 5.7 节“应用上下文和资源路径”中 来描述。

下面的示例展示了服务层代码(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
    http://www.springframework.org/schema/beans/spring -beans-3.0.xsd">
    <!-- services -->
    <bean id="petStore"
        class="org.springframework.samples.jpetstore.services
        .PetStoreServiceImpl">
        <property name="accountDao" ref="accountDao"/>
        <property name="itemDao" ref="itemDao"/>
        <!-- 这个bean的其它合作者和配置在这里编写-->
    </bean>
    <!-- 更多的service bean的定义在这里编写 -->
</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
    http://www.springframework.org/schema/beans/spring -beans-3.0.xsd">
    <bean id="accountDao"
        class="org.springframework.samples.jpetstore.dao.ibat is.SqlMapAccountDao">
        <!-- 这个bean的其它合作者和配置在这里编写 -->
    </bean>
    <bean id="itemDao"
        class="org.springframework.samples.jpetstore.dao.ibat is.SqlMapItemDao">
        <!-- 这个bean的其它合作者和配置在这里编写 -->
    </bean>
    <!-- 更多数据访问对象的定义在这里编写 -->
</beans>

在上面的示例中,服务层代码由类 PetStoreServiceImpl 和两个基于 iBatis(iBatis 现已更名为 MyBatis,译者注)对象/实体映射框架的数据访问对象 SqlMapAccountDao 和 SqlMapItemDao 构成。property 的 name 元素指的是 JavaBean 的属性名,而 ref 元 素指的是其它 bean 定义的名称。id 和 ref 元素之间的这种联系表示了两个协作对象的依赖 关系。要了解更多配置对象依赖的信息,可以参考依赖(4.4 节)。

4.2.2.1 处理基于 XML 的配置元数据

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

你可以使用 ApplicationContext 的构造方法从所有的 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.xml , messageSource.xml 和 themeSource.xml。所有的位置路径都是相对于该引用文件的, 所以 service.xml 必须在和引用文件 相同路 径中 或 者 是 类 路 径 下 , 而 messageSource.xml 和 themeSource.xml 必 须 是 在 位 于 引 用 文 件 下 一 级 的 resources 路径下。正如你看到的,前部的斜杠被忽略了,这是由于路径都是相对的,最 好就不用斜线。文件的内容会被引入,包括顶级的<beans/>元素,根据 Spring 的 Schema 或 DTD,它必须是有效 bean 定义的 XML 文件。

注意

在父目录中使用相对路径“../”来引用文件,这是可能的,但是不推荐这么做。这么做了会创建一个文件,它是当前应用程序之外的一个依赖。 特别是,这种引用对于“classpath:”的 URL(比如,“classpath:../service.xml”)是不推荐的,运行时的解析过 程会选择“最近的”类路径根目录并且会查看它的父目录。类路径配置的修改可能会导 致去选择一个不同的,不正确的目录。 你也可以使用资源位置的完全限定名来代替相对路径:比如,“file: C:/config /services.xml” 或“classpath:/config/services.xml”。这样的话,要注意你会耦合应用程序的配置到指定 的绝对路径。对于绝对路径,一般最好是保持一个间接的使用,比如通过占位符“${...}”, 这会基于运行时环境的 JVM 系统属性来解决。

4.2.3 使用容器

ApplicationContext 是能维护不同的 bean 和它们依赖注册的高级工厂接口。使用 T getBean(Stringname, Class<T> requiredType)方法你可以获取 bean 的实例。

ApplicationContext 允许你读取 bean 并且访问它们,如下所示:

// 创建并配置bean
ApplicationContext context =
    new ClassPathXmlApplicationContext(new String[] {"services.xml",
    "daos.xml"});
// 获取配置的实例
PetStoreServiceImpl service = context.getBean("petStore", PetStoreServiceImpl.class);
// 使用配置的实例
List userList service.getUsernameList();

使用 getBean()方法来获取 bean 的实例。ApplicationContext 接口有一些其它方法来获取 bean,但最好应用程序代码不使用它们。事实上,应用程序代码应该没有 getBean()方法的调用,那么就没有对 Spring API 的依赖了。比如,Spring 和 Web 框架整 合时,提供了对各种 Web 框架类库的依赖注入,不如控制器和 JSF 管理的 bean。