1.1 Spring IoC容器和bean介绍
本章介绍了Spring框架中控制反转(IoC)原理的实现。IoC也称为依赖注入(DI)。在这个过程中,对象仅通过构造函数参数、工厂方法的参数或在构造或从工厂方法返回对象实例后在对象实例上设置的属性来定义它们的依赖关系(即与它们一起工作的其他对象)。然后容器在创建bean时注入这些依赖项。这个过程基本上是bean本身的逆过程(因此得名,控制反转),通过使用类的直接构造或Service Locator模式等机制来控制其依赖项的实例化或位置。
org.springframework.beans和org.springframework.context包是Spring框架IoC容器的基础。BeanFactory接口提供了一种高级配置机制,能够管理任何类型的对象。ApplicationContext是BeanFactory的子接口。它补充道:
- 更容易与Spring的AOP特性集成
- 消息资源处理(用于国际化)
- 事件发布
- 应用程序层特定的上下文,例如用于web应用程序的WebApplicationContext。
简而言之,BeanFactory提供了配置框架和基本功能,而ApplicationContext添加了更多特定于企业的功能。ApplicationContext是BeanFactory的一个完整的超集,在本章中专门用于描述Spring的IoC容器。有关使用BeanFactory而不是ApplicationContext的更多信息,请参阅有关BeanFactory API的部分。
在Spring中,构成应用程序主干并由Spring IoC容器管理的对象称为bean。bean是由Spring IoC容器实例化、组装和管理的对象。否则,bean只是应用程序中的众多对象之一。bean以及它们之间的依赖关系反映在容器使用的配置元数据中。
1.2. 容器概述
org.springframework.context.ApplicationContext接口表示Spring IoC容器,并负责实例化、配置和组装bean。容器通过读取配置元数据来获得关于实例化、配置和组装哪些对象的指令。配置元数据以XML、Java注释或Java代码表示。它允许您表达组成应用程序的对象以及这些对象之间丰富的相互依赖关系。
Spring提供了ApplicationContext接口的几个实现。在独立应用程序中,通常会创建ClassPathXmlApplicationContext或FileSystemXmlApplicationContext的实例。虽然XML一直是定义配置元数据的传统格式,但您可以通过提供少量XML配置来声明支持这些额外的元数据格式,从而指示容器使用Java注释或代码作为元数据格式。
在大多数应用程序场景中,不需要显式spring.io/tools的用户代码来… IoC容器的一个或多个实例。例如,在web应用程序场景中,应用程序的web. XML文件中简单的8行(大约)样板web描述符XML通常就足够了(参见web应用程序的方便ApplicationContext实例化)。如果您使用用于Eclipse的Spring Tools(一个Eclipse驱动的开发环境),您可以轻松地创建这个样板配置,只需单击几下鼠标或击几下键盘。
下图显示了Spring如何工作的高级视图。您的应用程序类与配置元数据相结合,这样在创建并初始化ApplicationContext之后,您就有了一个完全配置的可执行系统或应用程序。
1.2.1. 配置元数据
如上图所示,Spring IoC容器使用一种形式的配置元数据。这个配置元数据表示作为应用程序开发人员,您如何告诉Spring容器实例化、配置和组装应用程序中的对象。
配置元数据通常以简单直观的XML格式提供,这也是本章大部分内容用来传达Spring IoC容器的关键概念和特性的格式。
基于xml的元数据并不是唯一允许的配置元数据形式。Spring IoC容器本身与实际编写配置元数据的格式完全分离。
现在,许多开发人员为他们的Spring应用程序选择基于java的配置。
有关在Spring容器中使用其他形式的元数据的信息,请参见:
- 基于注释的配置:使用基于注释的配置元数据定义bean。
- 基于Java的配置:使用Java而不是XML文件定义应用程序类外部的bean。要使用这些特性,请参阅@Configuration、@Bean、@Import和@DependsOn注释。
Spring配置由容器必须管理的至少一个(通常不止一个)bean定义组成。基于xml的配置元数据将这些bean配置为顶级元素中的元素。Java配置通常在@Configuration类中使用带@ bean注释的方法。
这些bean定义对应于构成应用程序的实际对象。通常,您将定义服务层对象、持久化层对象(如存储库或数据访问对象)、表示对象(如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="...">
<!-- 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>
- id属性是标识单个bean定义的字符串。
- class属性定义bean的类型,并使用完全限定的类名。
id属性的值可用于引用协作对象。本例中没有显示用于引用协作对象的XML。有关更多信息,请参阅依赖项。
1.2.2. 实例化容器
提供给ApplicationContext构造函数的一个或多个位置路径是资源字符串,允许容器从各种外部资源(如本地文件系统、Java CLASSPATH等)加载配置元数据。
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
在了解了Spring的IoC容器之后,您可能想了解更多关于Spring的资源抽象(如参考资料中所述)的知识,它提供了一种方便的机制,用于从URI语法中定义的位置读取InputStream。特别是,资源路径用于构造应用程序上下文,如应用程序上下文和资源路径中所述。
以下示例显示了服务层对象(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"/>
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions for services go here -->
</beans>
数据访问对象的dao .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">
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<bean id="itemDao" class="org.springframework.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类和JpaAccountDao和JpaItemDao类型(基于JPA对象-关系映射标准)的两个数据访问对象组成。属性name元素引用JavaBean属性的名称,ref元素引用另一个bean定义的名称。id和ref元素之间的链接表示协作对象之间的依赖关系。有关配置对象依赖项的详细信息,请参见依赖项。
编写基于xml的配置元数据
让bean定义跨越多个XML文件是很有用的。通常,每个单独的XML配置文件表示体系结构中的一个逻辑层或模块。
您可以使用应用程序上下文构造函数从所有这些XML片段加载bean定义。该构造函数接受多个Resource位置,如前一节所示。或者,使用元素的一次或多次出现来从另一个或多个文件加载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。所有位置路径都相对于进行导入的定义文件,因此services.xml必须与进行导入的文件位于相同的目录或类路径位置,而messageSource.xml和themeSource.xml必须位于导入文件位置下方的资源位置。可以看到,前面的斜杠被忽略了。然而,鉴于这些路径是相对的,最好完全不使用斜杠。根据Spring Schema,导入的文件的内容(包括顶级的元素)必须是有效的XML bean定义。
使用相对“..”来引用父目录中的文件是可能的,但不推荐使用。/”路径。这样做会在当前应用程序之外的文件上创建依赖项。特别地,对于classpath: url(例如,classpath:../services.xml),不建议使用此引用,因为运行时解析过程会选择“最近的”classpath根目录,然后查找它的父目录。类路径配置更改可能导致选择不同的、不正确的目录。
您总是可以使用完全限定的资源位置,而不是相对路径:例如,file:C:/config/services.xml或classpath:/config/services.xml。但是,请注意,您正在将应用程序的配置耦合到特定的绝对位置。一般来说,最好是为这种绝对位置保留一个间接的位置——例如,通过运行时根据JVM系统属性解析的“${…}”占位符。
名称空间本身提供导入指令特性。除了普通bean定义之外,Spring所提供的XML名称空间还提供了进一步的配置特性——例如,上下文和util名称空间。
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
}
}
}
1.2.3. 使用容器
ApplicationContext是高级工厂的接口,它能够维护不同bean及其依赖项的注册表。通过使用方法T getBean(字符串名称,类 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上混合和匹配这样的阅读器委托,从不同的配置源读取bean定义。
然后可以使用getBean检索bean的实例。ApplicationContext接口还有其他一些检索bean的方法,但是理想情况下,应用程序代码不应该使用它们。实际上,您的应用程序代码根本不应该调用getBean()方法,因此根本不依赖于Spring api。例如,Spring与web框架的集成为各种web框架组件(如控制器和jsf管理的bean)提供了依赖注入,允许您通过元数据(如自动装配注释)声明对特定bean的依赖。