1. IoC容器
1.1. 本章介绍了Spring的Inversion of Control(IoC)容器。
本章介绍了控制反转(IoC)原理的Spring框架实现。 IoC也称为依赖注入(DI)。在此过程中,对象仅通过构造函数参数,工厂方法的参数或在构造或从工厂方法返回后在对象实例上设置的属性来定义其依赖项(即,与它们一起使用的其他对象)。然后,容器在创建bean时注入那些依赖项。此过程从根本上讲是通过使用类的直接构造或诸如服务定位器模式之类的方法来控制其依赖项的实例化或位置的bean本身的逆过程(因此称为Control Inversion)。
org.springframework.beans和org.springframework.context包是Spring Framework的IoC容器的基础。 BeanFactory接口提供了一种能够管理任何类型对象的高级配置机制。 ApplicationContext是BeanFactory的一个子接口。 它增加了:
- 与Spring的AOP功能轻松集成
- 消息资源处理(用于国际化);
- 活动发布
- 应用层特定的上下文,例如Web应用程序中使用的WebApplicationContext。
简而言之,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代码表示。它使您能够表达组成应用程序的对象以及这些对象之间的丰富相互依赖关系。
Spring提供了ApplicationContext接口的几种实现。在独立应用程序中,通常创建ClassPathXmlApplicationContext或FileSystemXmlApplicationContext的实例。尽管XML是定义配置元数据的传统格式,但是您可以通过提供少量XML配置来声明性地启用对这些其他元数据格式的支持,从而指示容器将Java注解或代码用作元数据格式。
在大多数应用场景中,不需要显式用户代码即可实例化一个Spring IoC容器的一个或多个实例。例如,在Web应用程序场景中,应用程序的web.xml文件中简单的八行(约)样板Web描述符XML通常就足够了(请参阅Web应用程序的便捷ApplicationContext实例化)。如果使用Spring Tool Suite(基于Eclipse的开发环境),则只需单击几次鼠标或击键即可轻松创建此样板配置。
下图显示了Spring的工作原理的高级视图。您的应用程序类与配置元数据结合在一起,以便在创建和初始化ApplicationContext之后,您将拥有一个完全配置且可执行的系统或应用程序。
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定义对应于组成应用程序的实际对象。通常,您定义服务层对象,数据访问对象(DAO),表示对象(例如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
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>
1,id属性是一个标识单个bean定义的字符串。
2,class属性定义bean的类型并使用完全限定的classname。
id属性的值是指协作对象。 本示例中未显示用于引用协作对象的XML; 有关更多信息,请参阅依赖关系。
1.2.2. 实例化一个容器
提供给ApplicationContext构造函数的一个或多个位置路径是资源字符串,这些资源字符串使容器可以从各种外部资源(例如本地文件系统,Java CLASSPATH等)加载配置元数据。
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
了解了Spring的IoC容器之后,您可能想了解更多有关Spring的Resource抽象(如参考资料所述),它提供了一种方便的机制,可以从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
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和JpaItemDao类型的数据访问对象组成(基于JPA对象关系映射标准)。属性名称元素引用JavaBean属性的名称,而ref元素引用另一个bean定义的名称。 id和ref元素之间的这种联系表达了协作对象之间的依赖性。有关配置对象的依存关系的详细信息,请参阅依存关系。
编写基于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.xml,messageSource.xml和themeSource.xml。所有位置路径都相对于进行导入的定义文件,因此,services.xml必须与进行导入的文件位于同一目录或类路径位置,而messageSource.xml和themeSource.xml必须位于该位置下方的资源位置导入文件。如您所见,斜杠被忽略。但是,鉴于这些路径是相对的,最好不要使用任何斜线。根据Spring Schema,导入的文件的内容(包括顶级<beans />元素)必须是有效的XML bean定义。
可以但不建议使用相对的“ ../”路径引用父目录中的文件。这样做会创建对当前应用程序外部文件的依赖。特别是,不建议对classpath:URL(例如,classpath:../ services.xml)使用此引用,在URL中,运行时解析过程会选择“最近”的classpath根目录,然后查看其父目录。类路径配置的更改可能导致选择其他错误的目录。
您始终可以使用完全限定的资源位置来代替相对路径:例如,文件:C:/config/services.xml或类路径:/config/services.xml。但是请注意,您正在将应用程序的配置耦合到特定的绝对位置。通常最好为这样的绝对位置保留一个间接寻址,例如通过在运行时针对JVM系统属性解析的“ $ {…}”占位符。
命名空间本身提供了导入指令功能。 Spring所提供的一系列XML名称空间(例如,上下文和util名称空间)中提供了超出普通bean定义的其他配置功能。
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定义并按如下方式访问它们:
// 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定义)。 以下示例显示了Groovy配置:
ApplicationContext context = new GenericGroovyApplicationContext("services.groovy", "daos.groovy");
最灵活的变体是GenericApplicationContext与读取器委托结合使用,例如,与XML文件的XmlBeanDefinitionReader结合使用,如以下示例所示:
GenericApplicationContext context = new GenericApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml");
context.refresh();
您还可以将GroovyBeanDefinitionReader用于Groovy文件,如以下示例所示:
GenericApplicationContext context = new GenericApplicationContext();
new GroovyBeanDefinitionReader(context).loadBeanDefinitions("services.groovy", "daos.groovy");
context.refresh();
您可以在同一ApplicationContext上混合和匹配此类阅读器委托,从不同的配置源读取Bean定义。
然后,您可以使用getBean检索bean的实例。 ApplicationContext接口还有其他几种检索bean的方法,但是理想情况下,您的应用程序代码永远不要使用它们。 实际上,您的应用程序代码应该根本不调用getBean()方法,因此完全不依赖于Spring API。 例如,Spring与Web框架的集成为各种Web框架组件(例如控制器和JSF管理的Bean)提供了依赖项注入,使您可以通过元数据(例如自动装配注释)声明对特定Bean的依赖项。