容器概述

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

Spring提供了多个ApplicationContext接口的实现。在独立应用程序中,通常会创建一个ClassPathXmlApplicationContextFileSystemXmlApplicationContext的实例。虽然XML一直是定义配置元数据的传统格式,但您可以通过提供少量XML配置来声明性地启用对Java注解或代码的支持,从而指示容器使用Java注解或代码作为元数据格式。

在大多数应用程序场景中,通常不需要显式用户代码来实例化一个或多个Spring IoC容器的实例。例如,在Web应用程序场景中,在应用程序的web.xml文件中简单的八行左右的样板Web描述符XML通常就足够了(请参阅Web应用程序方便的ApplicationContext实例化)。如果您使用Spring Tools for Eclipse(基于Eclipse的开发环境),您可以通过几次鼠标点击或按键轻松创建这个样板配置。

下图显示了Spring工作的高层视图。您的应用程序类与配置元数据结合在一起,这样,在创建和初始化ApplicationContext之后,您就拥有一个完全配置和可执行的系统或应用程序。

容器魔法
图1. Spring IoC容器

配置元数据

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

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

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

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

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

这些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)
		<!-- 这个bean的协作者和配置在这里 -->
	</bean>

	<bean id="..." class="...">
		<!-- 这个bean的协作者和配置在这里 -->
	</bean>

	<!-- 更多bean定义在这里 -->

</beans>
1 id属性是一个字符串,用于标识单个bean定义。
2 class属性定义了bean的类型,并使用完全限定的类名。

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

实例化容器

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

  • Java

  • Kotlin

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

在了解了Spring的IoC容器之后,您可能想了解更多关于Spring的Resource抽象(如Resources中描述的)的信息,它提供了一个方便的机制,用于从URI语法中定义的位置读取InputStream。特别是,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">

	<!-- 服务 -->

	<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位置。正如您所见,前导斜杠会被忽略。但是,鉴于这些路径是相对的,最好根本不使用斜杠。被导入文件的内容,包括顶级<beans/>元素,必须根据Spring模式是有效的XML bean定义。

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

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

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

Groovy Bean Definition 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
		}
	}
}

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

使用容器

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

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

  • Java

  • Kotlin

// 创建和配置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配置:

  • Java

  • Kotlin

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

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

  • Java

  • Kotlin

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()

您还可以使用GroovyBeanDefinitionReader读取Groovy文件,如下例所示:

  • Java

  • Kotlin

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上混合使用这些读取器委托,从不同的配置源中读取bean定义。

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