注:翻译自spring官方文档
3.1 IOC容器和beans
本章节介绍Spring框架中控制反转(IOC)原理的实现。IOC也成依赖注入(DI)。它是一个对象定义依赖关系的过程,也就是说,对象只通过构造函数参数、工厂方法的参数或对象实例构造或从工厂方法返回后在对象实例上设置的属性来定义它们所使用的其他对象。然后容器在创建bean时并注入他们的依赖对象。这个过程本质上与bean本身相反,因此称为控制反转(IoC), bean本身通过直接构造类或一种机制(如服务定位器模式)来控制依赖项的实例化或位置。
org.springframework.beans和org.springframework.context包是Spring框架中IOC容器的基础。BeanFactory接口提供了能够管理任何类型对象的高级配置机制,ApplicaitonContext是BeanFactory的子接口,使得它更易于与Spring框架的AOP特性集成;消息资源处理(用于国际化)、时间发布,以及应用程序层特定的上下文,如web应用程序中使用的WebApplicaitonContext。
总而言之,BeanFactory提供了配置框架和基本功能,而ApplicationContext新增更多企业特定的功能。ApplicationContext是BeanFactory的一个完整超集,在本章描述Spring的IOC容器时仅使用它,关于更多使用BeanFactory的信息在“3.16 BeanFactory”。
在Spring中,形成应用的主干并且有IOC容器管理的对象称为beans。一个bean是由IOC容器实例化、组装和管理的对象。否则,bean只是应用程序中的许多对象之一。Beans机器之间的依赖关系反应在容器使用的配置元数据中。
3.2容器
org.springframework.context.ApplicationContext接口表示Spring的IOC容器,并且负责实例化、配置和组装前面提到的bean。容器通过读取配置元数据获取关于要实例化、配置和组装对象的指令。配置元数据用XML、Java注解或者Java代码表示,它允许你表达组成应用程序的对象以及这些对象间丰富的依赖关系。
ApplicationContext接口的几个实现类随着Spring提供开箱即用。在独立应用程序中,通常创建ClassPathXmlApplicationContext或FileSystemXmlApplicationContext的实例。虽然XML一直是定义配置元数据的传统格式,但你可以通过提供少量XML配置来声明支持这些额外的元数据,从而指示容器使用Java注解或者代码作为元数据格式。
在大多数应用程序场景中,不需要显式用户代码来实例化Spring的IOC容器(一个或者多个实例)。例如,在web应用程序场景中,通常只需在应用程序的web. XML文件中包含8行左右的样板web描述符XML就足够了(参见3.15.4节,“web应用程序的方便应用程序上下文实化”)。如果你正在使用SpringTool Suite eclipse支持的开发环境,那么只需单击几下鼠标或击键就可以轻松创建这个样板配置。
下图是Spring工作原理的高级视图。你的应用程序类与配置元数据相结合,这样在创建并初始化ApplicationContext之后,你就拥有了一个完全配置的可执行系统或应用程序。
figure 3.1 Spring IOC容器
3.2.1 配置元数据
从图3.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定义,并且通常包含多个bean定义。基于xml的配置元数据显示这些bean被配置为顶级元素中的元素。Java配置通常在@Configuration类中使用@Bean注解的方法。
这些bean定义对应于组成应用程序的实际对象。通常定义服务层对象、数据访问对象(DAOs)、表示对象(如Struts Action实例)、基础设施对象(如Hibernate SessionFactories)、JMS队列等等。通常不会在容器中配置细粒度域对象,因为通常由dao和业务逻辑负责创建和加载域对象。不过,你可以使用Spring与AspectJ的集成来配置在IoC容器控制之外创建的对象。请参阅使用AspectJ使用Spring来依赖注入域对象。
下面的实例展示基于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.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;有关更多信息,请参见依赖项。
3.2.2 初始化一个容器
实例化Spring IoC容器很简单。提供给ApplicationContext构造函数的位置路径实际上是资源字符串,允许容器从各种外部资源(如本地文件系统)、Java类路径等加载配置元数据。
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"});
注意:在了解了Spring的IoC容器之后,你可能想了解更多关于Spring的资源抽象的信息,如第4章“资源”中所述,它提供了一种方便的机制,可以从URI语法中定义的位置读取InputStream。特别是,资源路径用于构造应用程序上下文,如第4.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.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>
下面展示数据库层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.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类以及JapItemDao类两个数据层的对象(数据库层类基于JPA对象/关系映射标准)。property name元素引用JavaBean属性的名称,ref元素引用另一个bean定义的名称。id和ref元素之间的这种链接表示协作对象之间的依赖关系。有关配置对象依赖项的详细信息,请参见依赖项。
组合基于xml的配置元数据
让bean定义跨多个XML文件是很有用的。通常,每个单独的XML配置文件表示体系结构中的逻辑层或模块。
你可以使用应用程序上下文构造函数从所有这些XML片段加载bean定义。这个构造函数接受多个资源位置,如前一节所示。或者,使用一个或多个出现的元素从另一个或多个文件加载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定义从三个文件加载:服务。servlces.xml, messageSource.xml和themeSource.xml。所有位置路径都相对于执行导入的定义文件,因此services.xml必须与执行导入的文件位于相同的目录或类路径位置,而messageSource.xml和themeSource.xml必须位于导入文件位置下方的resources位置。正如你所看到的,前面的斜杠被忽略,但是考虑到这些路径是相对的,所以最好不要使用斜杠。导入的文件的内容,包括顶层的元素,必须是根据Spring模式定义的有效XML bean定义。
注意:它是可能的,但不推荐,在父目录中引用文件使用一个相对"…/”路径。这样做会创建对当前应用程序外部文件的依赖。特别地,对于“classpath:”url(例如,“classpath:…/services.xml”),不推荐使用这个引用,因为运行时解析过程会选择“最近的”类路径根,然后查看它的父目录。类路径配置更改可能导致选择不同的、不正确的目录。你总是可以使用完全限定的资源位置,而不是相对路径:例如,“file:C:/config/services.xml”或“classpath:/config/services.xml”。但是,请注意,你正在将应用程序的配置耦合到特定的绝对位置。通常更可取的做法是为这些绝对位置保留一个间接的位置,例如,通过“${…}”占位符,这些占位符在运行时根据JVM系统属性解析。
导入指令是bean名称空间本身提供的特性。除了普通bean定义之外,Spring还提供了其他配置特性,比如“context”和“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
}
}
}
这种配置风格在很大程度上等同于XML bean定义,甚至支持Spring的XML配置名称空间。它还允许通过“importBeans”指令导入XML bean定义文件。
3.2.3使用容器
ApplicationContext是高级工厂的接口,该工厂能够维护不同bean及其依赖项的注册表。使用方法T getBean(String name, Class 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();
使用Groovy配置,引导看起来非常相似,只是一个不同的上下文实现类,它支持Groovy(但也理解XML bean定义):
ApplicationContext context = new GenericGroovyApplicationContext(“services.groovy”, “daos.groovy”);
最灵活的变体是GenericApplicationContext与reader委托相结合,例如使用XmlBeanDefinitionReader for XML文件:
GenericApplicationContext context = new GenericApplicationContext();
new XmlBeanDefinitionReader(ctx).loadBeanDefinitions("services.xml", "daos.xml");
context.refresh();
可以在相同的ApplicationContext上混合和匹配这样的reader委托,如果需要,还可以从不同的配置源读取bean定义。
然后可以使用getBean检索bean的实例。ApplicationContext接口还有一些用于检索bean的其他方法,但理想情况下,应用程序代码不应该使用它们。实际上,你的应用程序代码应该完全不调用getBean()方法,因此完全不依赖于Spring api。例如,Spring与web框架的集成为各种web框架组件(如控制器和jsf管理的bean)提供了依赖注入,允许你通过元数据(例如自动装配注释)声明对特定bean的依赖(比如:一个注入注解)。