本章介绍Spring的控制反转(IoC)容器。
1.1。Spring IoC容器和Bean简介
本章介绍了控制反转(IoC)原理的Spring Framework实现。IoC也称为依赖注入(DI)。这是一个过程,通过这个过程,对象只能通过构造函数参数,工厂方法的参数或在构造或从工厂方法返回后在对象实例上设置的属性来定义它们的依赖关系(即,它们使用的其他对象)。 。然后容器在创建bean时注入这些依赖项。此过程基本上是bean本身的逆(因此名称,控制反转),通过使用类的直接构造或诸如服务定位器模式的机制来控制其依赖关系的实例化或位置。
在org.springframework.beans
和org.springframework.context
包是Spring框架的IoC容器的基础。该 BeanFactory接口提供了一种能够管理任何类型对象的高级配置机制。 ApplicationContext是一个子界面BeanFactory
。它补充说:
- 更容易与Spring的AOP功能集成
- 消息资源处理(用于国际化)
- 活动出版
- 特定
WebApplicationContext
于应用程序层的上下文,例如在Web应用程序中使用的上下文。
简而言之,它BeanFactory
提供了配置框架和基本功能,并ApplicationContext
添加了更多特定于企业的功能。它ApplicationContext
是完整的超集,BeanFactory
在本章中仅用于Spring的IoC容器的描述。有关使用BeanFactory
而不是ApplicationContext,
看到 的BeanFactory更多信息。
在Spring中,构成应用程序主干并由Spring IoC容器管理的对象称为bean。bean是一个由Spring IoC容器实例化,组装和管理的对象。否则,bean只是应用程序中许多对象之一。Bean及其之间的依赖关系反映在容器使用的配置元数据中。
1.2。集装箱概览
该org.springframework.context.ApplicationContext
接口代表Spring IoC容器,负责实例化,配置和组装bean。容器通过读取配置元数据获取有关要实例化,配置和组装的对象的指令。配置元数据以XML,Java注释或Java代码表示。它允许您表达组成应用程序的对象以及这些对象之间丰富的相互依赖性。
ApplicationContext
Spring提供了几种接口实现。在独立应用程序中,通常会创建一个ClassPathXmlApplicationContext或的实例FileSystemXmlApplicationContext。虽然XML是定义配置元数据的传统格式,但您可以通过提供少量XML配置来声明容器使用Java注释或代码作为元数据格式,以声明方式启用对这些其他元数据格式的支持。
在大多数应用程序方案中,不需要显式用户代码来实例化Spring IoC容器的一个或多个实例。例如,在Web应用程序场景中,应用程序文件中的简单八行(左右)样板Web描述符XML web.xml
通常就足够了。如果您使用 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 Framework的一部分。因此,您可以使用Java而不是XML文件在应用程序类外部定义bean。可以使用使用这些新功能@Configuration, @Bean, @Import,和@DependsOn注释。
Spring配置包含容器必须管理的至少一个且通常不止一个bean定义。基于XML的配置元数据将这些bean配置为<bean/>
顶级元素内的<beans/>
元素。Java配置通常@Bean
在@Configuration
类中使用注释方法。
这些bean定义对应于构成应用程序的实际对象。通常,您定义服务层对象,数据访问对象(DAO),表示对象(如Struts Action
实例),基础结构对象(如Hibernate SessionFactories
,JMS Queues
等)。通常,不会在容器中配置细粒度域对象,因为DAO和业务逻辑通常负责创建和加载域对象。但是,您可以使用Spring与AspectJ的集成来配置在IoC容器控制之外创建的对象。
The following example shows the basic structure of XML-based configuration metadata:
| 该 |
| 该 |
id
属性的值指的是协作对象。在此示例中未显示用于引用协作对象的XML。
1.2.2。实例化容器
提供给ApplicationContext
构造函数的位置路径是资源字符串,它允许容器从各种外部资源(如本地文件系统,Java等)加载配置元数据CLASSPATH
。
| 在了解了Spring的IoC容器之后,您可能想要了解有关Spring的 |
以下示例显示了服务层对象(services.xml)
配置文件:
以下示例显示了数据访问对象daos.xml
文件:
在前面的示例中,服务层由的PetStoreServiceImpl
类和类型的两个数据访问对象JpaAccountDao
和JpaItemDao
(基于JPA对象关系映射标准)。该property name
元素是指JavaBean属性的名称,以及ref
元素指的是另一个bean定义的名称。元素id
和ref
元素之间的这种联系表达了协作对象之间的依赖关系。有关配置对象的依赖关系的详细信息 。
编写基于XML的配置元数据
让bean定义跨越多个XML文件会很有用。通常,每个单独的XML配置文件都代表架构中的逻辑层或模块。
您可以使用应用程序上下文构造函数从所有这些XML片段中加载bean定义。此构造函数采用多个资源位置,如前一节所示。或者,使用一个或多个<import/>元素从另一个或多个文件加载bean定义。以下示例显示了如何执行此操作:
在前面的例子中,外部豆定义是从三个文件加载: services.xml
,messageSource.xml
,和themeSource.xml
。所有位置路径都与执行导入的定义文件相关,因此services.xml
必须与执行导入的文件位于相同的目录或类路径位置, messageSource.xml
而且themeSource.xml
必须位于resources
导入文件位置下方的位置。如您所见,忽略前导斜杠。但是,鉴于这些路径是相对的,最好不要使用斜杠。<beans/>
根据Spring Schema,正在导入的文件的内容(包括顶级元素)必须是有效的XML bean定义。
| 可以(但不建议)使用相对“../”路径引用父目录中的文件。这样做会对当前应用程序之外的文件创建依赖关系。特别是,不建议对 您始终可以使用完全限定的资源位置而不是相对路径:例如, |
命名空间本身提供了import指令功能。Spring提供的一系列XML命名空间中提供了除普通bean定义之外的其他配置功能 - 例如,context
和util
名称空间。
Groovy Bean定义DSL
作为外化配置元数据的另一个示例,bean定义也可以在Spring的Groovy Bean定义DSL中表示,如Grails框架中所知。通常,此类配置位于“.groovy”文件中,其结构如下例所示:
此配置样式在很大程度上等同于XML bean定义,甚至支持Spring的XML配置命名空间。它还允许通过importBeans
指令导入XML bean定义文件。
1.2.3。使用容器
它ApplicationContext
是高级工厂的接口,能够维护不同bean及其依赖项的注册表。通过使用该方法T getBean(String name, Class<T> requiredType)
,您可以检索Bean的实例。
将ApplicationContext
让你读bean定义和访问它们,如下例所示:
使用Groovy配置,bootstrapping看起来非常相似。它有一个不同的上下文实现类,它是Groovy-aware(但也理解XML bean定义)。以下示例显示了Groovy配置:
最灵活的变体GenericApplicationContext
与读者委托相结合 - 例如,XmlBeanDefinitionReader
对于XML文件,如以下示例所示:
您还可以使用GroovyBeanDefinitionReader
for Groovy文件,如以下示例所示:
您可以ApplicationContext
在不同的配置源中读取和匹配此类读取器委托,读取bean定义。
然后,您可以使用它getBean
来检索Bean的实例。该ApplicationContext
接口还有一些其他方法可以检索bean,但理想情况下,您的应用程序代码绝不应该使用它们。实际上,您的应用程序代码根本不应该调用该 getBean()
方法,因此根本不依赖于Spring API。例如,Spring与Web框架的集成为各种Web框架组件(如控制器和JSF托管bean)提供依赖注入,允许您通过元数据(例如自动装配注释)声明对特定bean的依赖性。
依赖
典型的企业应用程序不包含单个对象(或Spring用法中的bean)。即使是最简单的应用程序也有一些对象可以协同工作,以呈现最终用户所看到的连贯应用程序。下一节将介绍如何定义多个独立的bean定义,以及对象协作实现目标的完全实现的应用程序。
1.4.1。依赖注入
依赖注入(DI)是一个过程,通过这个过程,对象只能通过构造函数参数,工厂方法的参数或在构造对象实例后在对象实例上设置的属性来定义它们的依赖关系(即,它们使用的其他对象)。从工厂方法返回。然后容器在创建bean时注入这些依赖项。这个过程基本上是bean本身的反向(因此名称,控制反转),它通过使用类的直接构造或服务定位器模式来控制其依赖项的实例化或位置。
使用DI原则的代码更清晰,当对象提供其依赖项时,解耦更有效。该对象不查找其依赖项,也不知道依赖项的位置或类。因此,您的类变得更容易测试,特别是当依赖关系在接口或抽象基类上时,这允许在单元测试中使用存根或模拟实现。
DI存在两个主要变体:基于构造函数的依赖注入和基于Setter的依赖注入。
基于构造函数的依赖注入
基于构造函数的DI由容器调用具有多个参数的构造函数来完成,每个参数表示一个依赖项。调用static
具有特定参数的工厂方法来构造bean几乎是等效的,本讨论同样处理构造函数和static
工厂方法的参数。以下示例显示了一个只能通过构造函数注入进行依赖注入的类:
请注意,这个类没有什么特别之处。它是一个POJO,它不依赖于容器特定的接口,基类或注释。
构造函数参数解析
通过使用参数的类型进行构造函数参数解析匹配。如果bean定义的构造函数参数中不存在潜在的歧义,那么在bean定义中定义构造函数参数的顺序是在实例化bean时将这些参数提供给适当的构造函数的顺序。考虑以下课程:
假设ThingTwo
并且ThingThree
类与继承无关,则不存在潜在的歧义。因此,以下配置工作正常,您不需要在<constructor-arg/>
元素中显式指定构造函数参数索引或类型。
当引用另一个bean时,类型是已知的,并且可以发生匹配(与前面的示例一样)。当使用简单类型时,例如 <value>true</value>
,Spring无法确定值的类型,因此无法在没有帮助的情况下按类型进行匹配。考虑以下课程:
构造函数参数类型匹配
在前面的场景中,如果使用type
属性显式指定构造函数参数的类型,则容器可以使用与简单类型的类型匹配。如下例所示:
构造函数参数索引
您可以使用该index
属性显式指定构造函数参数的索引,如以下示例所示:
除了解决多个简单值的歧义之外,指定索引还可以解决构造函数具有相同类型的两个参数的歧义。
| 该指数从0开始。 |
构造函数参数名称
您还可以使用构造函数参数名称进行值消歧,如以下示例所示:
请记住,为了使这项工作开箱即用,必须在启用调试标志的情况下编译代码,以便Spring可以从构造函数中查找参数名称。如果您不能或不想使用debug标志编译代码,则可以使用 @ConstructorProperties JDK批注显式命名构造函数参数。然后,示例类必须如下所示: