1.2 容器概述

1.2 容器概述 #

Version 5.2.2.RELEASE


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

Spring提供了ApplicationContext接口的几种实现。在独立的应用中,通常创建的是ClassPathXmlApplicationContextFileSystemXmlApplicationContext的实例。虽然XML一直是传统的定义配置元数据的格式,但您仍然可以使用Java注解或代码作为元数据格式,只需要通过提供少量XML配置来显式地开启对这些其他元数据格式的支持。

在大多数应用程序场景中,实例化Spring IoC容器的一个或多个实例不需要显式的用户代码。例如,在web应用程序场景中,应用程序的样板web描述XML web.xml 中简单的八行(大约八行)通常就足够了(请参见[Web应用简单的ApplicationContext实例化])。如果您使用Spring Tool Suite(Eclipse驱动的开发环境),只需单击几下鼠标或按键,就可以轻松创建此样板配置。

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

图1. Spring IoC容器

1.2.1 配置元数据 #

如上图所示,Spring IoC容器使用一种形式的配置元数据。此配置元数据表示作为应用程序开发人员,您如何告知Spring容器实例化、配置和组装您应用程序中的对象。

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

基于XML的元数据不是唯一允许的配置元数据形式。Spring IoC容器本身与实际写入此配置元数据的格式完全解耦。现在,许多开发人员为他们的Spring应用选择基于Java的配置

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

  • 基于注解的配置:Spring 2.5引入了对基于注解的配置元数据的支持。
  • 基于Java的配置:从Spring 3.0开始,Spring JavaConfig项目提供的许多特性成为了核心Spring框架的一部分。因此,您可以使用Java而不是XML文件来定义应用程序类外的bean。要使用这些新功能,请参阅@Configuration@Bean@Import@DependsOn注解。

Spring配置至少包括一个但通常不止一个必须由容器管理的bean定义。基于XML的配置元数据将这些bean配置为顶级<beans/>元素中的<bean/>元素。Java配置通常在@Configuration类中使用@Bean注解标注的方法来定义bean。

这些bean定义对应着构成应用程序的实际对象。通常,您定义服务层对象、数据访问对象(DAO)、表示对象(如Struts中的Action实例)、基础结构对象(如Hibernate SessionFactorys)、JMS Queues等。通常,不在容器中配置细粒度域对象,因为创建和加载域对象通常是DAO和业务逻辑的责任。但是,您可以使用Spring与AspectJ的集成来配置在IoC容器控制之外创建的对象。请参阅在Spring中使用AspectJ注入依赖域对象

以下示例显示了基于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
<!-- 此处是当前bean的协作者和配置 --> </bean> <bean id="..." class="..."> <!-- 此处是当前bean的协作者和配置 --> </bean> <!-- 此处是其他的bean定义 --> </beans>
1
id属性是标识单个bean定义的字符串。
2
class属性定义bean的类型并且要使用全限定的类名。

id属性的值引用协作对象。本例中的XML未展示引用协作对象。有关更多信息,请参阅依赖

1.2.2 初始化容器 #

传给ApplicationContext构造函数的位置路径(可以有多个)是资源字符串,容器将从各种外部资源(如本地文件系统、Java CLASSPATH等)加载配置元数据。

ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
val context = ClassPathXmlApplicationContext("services.xml", "daos.xml")
在了解Spring的IoC容器之后,您可能想进一步了解Spring的Resource抽象(如[资源]中所述),它提供了一种方便的机制,通过URI语法定义的位置读取输入流。具体而言,Resource路径用于构建应用上下文,如[应用上下文与资源路径]中所述。

以下示例展示了服务层对象(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"/>
        <!-- 此处是当前bean的其他协作者和配置 -->
    </bean>

    <!-- 此处是其他服务的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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="accountDao"
        class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao">
        <!-- 此处是当前bean的其他协作者和配置 -->
    </bean>

    <bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao">
        <!-- 此处是当前bean的其他协作者和配置 -->
    </bean>

    <!-- 此处是其他数据访问对象的bean定义 -->

</beans>

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

组合基于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目录下。如您所见,前面的斜杠被忽略了。然而,考虑到这些路径是相对的,最好不要使用斜杠。根据Spring Schema,要导入文件的内容包括顶级<beans/>元素,而且必须是有效的XML bean定义。

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

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

命名空间本身提供导入指令功能。Spring提供的一系列XML命名空间中提供了普通bean定义之外的更多配置特性 — 例如,contextutil命名空间。

Groovy Bean 定义 DSL #

作为外部化配置元数据的另一个示例,bean定义也可以在Spring的Groovy Bean定义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
        }
    }
}

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

1.2.3 使用容器 #

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

ApplicationContext允许您读取bean定义并访问它们,如下例所示:

// 创建并配置bean
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

// 获取配置好的实例
PetStoreService service = context.getBean("petStore", PetStoreService.class);

// 使用配置好的实例
List<String> userList = service.getUsernameList();
import org.springframework.beans.factory.getBean

// 创建并配置bean
val context = ClassPathXmlApplicationContext("services.xml", "daos.xml")

// 获取配置好的实例
val service = context.getBean<PetStoreService>("petStore")

// 使用配置好的实例
var userList = service.getUsernameList()

对于Groovy配置,引导看起来非常相似。它有一个不同的上下文实现类,是识别Groovy的(但也能识别XML bean定义)。以下示例展示了Groovy配置:

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

最灵活的变种是GenericApplicationContext与读取器(reader)的组合 — 例如,对于XML文件使用XmlBeanDefinitionReader,如下例所示:

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

当然,对于Groovy文件,您还可以使用GroovyBeanDefinitionReader,如下例所示:

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

您可以在同一ApplicationContext上混合使用这些读取器(reader),以从不同的配置源读取bean定义。

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