在这一部分,我们来关注一个使用Spring的DI功能的简单的Spring应用程序。在一个应用程序中使用Spring的DI功能,需要遵循以下步骤。
(1)确定应用程序对象及其依赖关系。
(2)根据步骤1中确定的应用程序对象创建POJO类。
(3)创建描述应用程序对象及其依赖项的配置元数据。
(4)创建一个Spring loc容器的实例并将配置元数据传递给它。
(5)从Spring loc容器实例中访问应用程序对象。
现在让我们来看看上述步骤在Mybank应用程序中是如何体现的。
1 确定应用程序对象及其依赖关系前面讨论过,mybank应用程序展示了创建一笔定期存款的表单(见图1-3)。图1-4的时序图显示了当用户提交表单时出现的应用程序对象(以及它们之间的交互)。
图1-4 MyBank的应用程序对象及其依赖项
在图1-4所示的时序图中, FixedDepositcontroller代表当这个表单提交时接受请求的WebController ,而FixedDepositDetails对象包含定期存款明细, FixedDepositController调用FixedDepositservice (服务层对象)的createFixedDeposit方法。然后, FixedDepositService调用FixedDepositDao对象(数据访问对象)来把定期存款明细保存到应用程序的数据存储区。因此,我们可以从图中理解FixedDepositService是
FixedDepositController对象的依赖项,而FixedDepositDao是FixedDepositService对象的依赖项。
2根据确定的应用程序对象创建POJO类
一旦确定了应用程序对象,下一步就是根据这些应用程序对象创建POJO类。ch01-bankapp-xml项目中包含了对应于FixedDepositController、FixedDepositService和FixedDepositDao这些应用程序对象的POJC类。ch01-bankapp-xml项目是一个使用Spring DI功能的简化版Mybank应用程序。你可以将ch01-bankapp-xml项目导入IDE中。接下来我们来看这个项目中包含的文件。
在1.3节中,我们讨论了把依赖项作为构造函数参数或作为setter方法参数传递给应用程序对象。代码清单1-12展示了一个FixedDepositService的实例( FixedDepositController的依赖项)是如何作为一个setter方法的参数传递给FixedDepositController类的。
代码清单1-12 FixedDepositController类
在代码清单1-12中, FixedDepositService这个依赖项是通过setFixedDepositService方法被传递给FixedDepositController的。我们马上就能看到setFixedDepositService的setter方法被Spring调用。
现在让我们看看如何在配置元数据中指定应用程序对象及其依赖关系。
3创建配置元数据我们在1.3节中了解到, Spring容器读取指定了应用程序对象及其依赖项的配置元数据,将应用程序对象实例化并注入它们的依赖项。在本节中,我们先介绍配置元数据中包含的其他信息,然后深入研究如何用XML方式指定配置元数据。
配置元数据指定应用程序所需的企业服务(如事务管理、安全性和远程访问)的信息,例如,如果想让Spring来管理事务,你需要在配置元数据中配置对Spring的PlatformTransactionManager接口的一个实现。PlatformTransactionManager实现负责管理事务(更多关于Spring的事务管理功能详见第8章)。
如果应用程序和消息中间件(如ActiveMQ )、数据库(如MySQL)、电子邮件服务器等进行交互,那么这些简化了与外部系统交互的Spring的特定对象也是在配置元数据中定义的。例如,如果应用程序需要向ActiveMQ发送或接收JMS消息,你可以在配置元数据中配置Spring的JmsTemplate类来简化和ActiveMQ的交互。在代码清单1-10中可以看到,如果使用JmsTemplate向JMS提供者发送消息,你不需要处理低级别的JMS API (更多关于Spring对与JMS提供者交互的支持详见第10章).
你可以通过XML文件或者通过POJO类中的注解将配置元数据提供给Spring容器。从Spring 3.0版本开始,你也可以通过在Java类上添加Spring的@Confiquration注解来将配置元数据提供给Spring容器。在本节中,我们将介绍如何通过XML方式指定配置元数据。在第6章和第7章中,我们将分别介绍如何通过POJO类中的注解和通过对Java类的@Configuration注解来配置元数据。
通过创建一个包含应用程序对象及其依赖项信息的应用程序上下文XML文件,可以按照XML格式将配置元数据提供给应用程序。代码清单1-13呈现了一个应用程序上下文XML文件的大体样式。下面的XML展示了MyBank应用程序的应用程序上下文XML文件由FixedDepositController, FixedDepositService以及
FixedDepositDao (见图1-4以了解这些对象如何相互作用)等对象组成。
代码清单1-13 ApplicationContext.xml-MyBank的应用程序上下文XML文件
以下是关于应用程序上下文XML文件的要点。
(1)在spring-beans.xsd schema (也被称为Sprina的bean schema )中定义的<beans>元素是应用程序上下文的XML文件的根元素。spring-beans.xsd schema包含在Spring 5.0.1发布版的spring-beans-5.0.1.reLEASE.jar JAR包中。
(2)每个<bean>元素配置一个由Spring容器管理的应用程序对象。在Spring框架的术语中,一个<bean>元素代表一个bean定义。Sprinq容器创建的基于bean定义的对象称为一个bean, id特性指定bean的唯一名称, class特性指定bean的完全限定类名。还可以使用<bean>元毒的name特件来指定bean的别名。在MyBank应用程序中, FixedDepositController, FixedDepositService和FixedDepositDao为应用程序对象,因此我们有3个<bean>元素-每个应用程序对象对应一个<bean>元素。由于Spring容器管理着由<bean>元素配置的应用程序对象, Spring容器也就需要承担创建并注入它们的依赖关系的责任。不需要直接创建由<bean>元素定义的应用程序对象实例,而是应该从Spring容器中获取它们。在本节后面的部分,我们将介绍如何获取由Spring容器管理的应用程序对象。
(3)没有和MyBank应用程序中的FixedDepositDetails域对象相对应的<bean>元素。这是因为域对象通常不是由Spring容器管理的,它们由应用程序所使用的ORM框架(如Hibernate )创建,或者通过使用new运算符以编程方式创建它们。
(4) <property>元素指定由<bean>元素配置的bean的依赖项(或者配置属性).<property>元素对应于bean类中的JavaBean风格的setter方法,该方法由Spring容器调用以设置bean的依赖关系(或配置属性) 。
现在让我们来介绍一下如何通过setter方法注入依赖项。
4通过setter方法注入依赖项为了理解如何通过在bean类中定义的setter方法注入依赖,我们再来观察一下MyBank应用程序中的FixedDepositController类。
代码清单1-14 FixedDepositController类
代码清单1-14表明FixedDepositController类中声明了一个类型为FixedDepositService,名称为
fixedDepositService的实例变量。这个fixedDepositService变量由setFixedDepositService方法设定-一种针对fixedDepositService变量的JavaBean风格的setter方法。这是一个基于setter方法的Dl示例,其中的setter方法满足依赖项.
图1-5描述了在applicationContext.xml文件中对FixedDepositController类的bean定义(见代码清单1-13)。
前文的bean定义表明, FixedDepositcontroller bean通过sproperty>元素定义了它对于
FixedDepositService bean的依赖。Sprinq容器在bean创建时会调用bean类中JavaBean风格的setter方法,该方法与sproperty>元素的name特性对应。 <property>元素的引用特性标识可以分辨需要创建具体哪个Spring bean的实例。引用特性的值必须与配置元数据中的<bean>元素的id特性值(或由name特性指定的名称之一)匹配。
在图1-5中, <property>元毒的name特性的值为fxedDepositService ,这意味着sproperty>元素对应于FixedDepositController类(见代码清单1-14 )中setFixedDepositService的setter方法。由于<property>元素的re特性的值是service ,因此<property>元素是指id特性的值为service的<bean>元素。现在, id特性的值为service的<bean>元素是FixedDepositService bean (见代码清单1-13 ) . Spring容器创建了一个FixedDepositService类(一个依赖项)的实例,并将FixedDepositService实例作为调用FixedDepositController (一个依赖对象)的setFixedDepositService方法(一个用于fixedDepositService变量的JavaBean风格的setter方法)的参数。
图1-5使用<property>元素定义依赖项
在FixedDepositController应用程序对象的上下文中,图1-6总结了<property>元素的name和re特性的用途。
图1-6 <property>元素的name特性对应于一个满足bean依赖关系的JavaBean风格的setter方法, re特性指的是另一个bean
图1-6显示了name特性的fixedDepositService值对应于FixedDepositController类的setFixedDepositservice方法,ref性的service值对应于id值为service的bean.
图1-7总结了Spring容器如何根据MyBank应用程序的applicationContext.xml文件(见代码清单1-13)提供的配置元数据创建bean并注入它们的依赖项。该图显示了Spring loc容器创建FixedDepositController、FixedDepositService和FixedDepositDao bean并注入其依赖项的步骤顺序。在尝试创建bean之前, Spring容器读取并验证由applicationContext.xml文件提供的配置元数据。由Sprina容器创建bean的顺序取决于它们在applicationContext.xml文件中的定义顺序,Spring容器确保在调用setter方法之前完全配署了一个bean的依赖关系。例如, FixedDepositController bean依赖于FixedDepositService bean ,因此, Spring容器会在调用FixedDepositController bean的setFixedDepositService方法之前配署好FixedDepositService bean。
图1-7 Spring loc容器创建bean并注入其依赖关系的顺序
到目前为止,我们已经看到的这些bean定义指示Spring容器调用bean类的无参数构造函数来创建bean实例,并使用基于setter的D来注入依赖关系。在第2章中,我们将介绍指示Spring容器通过类中定义的工厂方法创建bean实例的bean定义。另外,我们将介绍如何通过构造函教参数注入依赖关系(也称为基于构造函数的D1)。
现在来看看如何创建Spring容器的实例,并将配置元数据传递给它。
5 创建Spring容器的一个实例Spring的ApplicationContext对象表示Spring容器的一个实例。Spring提供了一些ApplicationContext接口的内置实现,如ClassPathXmlApplicationContext, FileSystemXmlApplicationContext.
XmlWebApplicationcontex等.ApplicationContext实现的选择取决于如何定义配置元数据(使用XML、注解或Java代码)以及应用程序类型(独立或Web应用程序)。例如, ClassPathXmlApplicationContext和
FileSvstemXmlApplicationContext类适用干以XML格式提供配置元数据的独立应用程序
XmlWebApplicationContext适用于以XML格式提供配置元数据的Web应用程序
AnnotationConfigWebApplicationContext适用于通过Java代码以编程方式提供配置元数据的Web应用程序,等等。
由于MyBank应用程序是一个独立的应用程序,因此可以使用ClassPathXmlApplicationContext或FilesystemXmlApplicationContext类来创建一个Spring容器的实例。应该注意到
ClassPathXmlApplicationContext类从指定的类路径位置加载应用程序上下文XML文件,
FileSystemXmlApplicationContext类从文件系统上的指定位置加载应用程序上下文XML文件。
MyBank应用程序中的BankApp类展示了使用ClassPathXmlApplicationContext类创建一个Spring容器的实例(见代码清单1-15)。
代码清单1-15 BanKApp类
代码清单1-15展示了BankApp中负责引导Spring容器的main方法,其中应用程序上下文XML文件的类路径位置传递给了ClassPathXmlApplicationContext类中的构造函数。创建ClassPathXmlApplicationcontext实例的结果是在应用程序上下文XML文件中创建的那些bean都是单个范围并被设置为预实例化的。在第2章中,我们将讨论bean的范围,以及使用Spring容器预实例化或者延迟实例化bean的含义。现在,你可以假设在MyBank应用程序的applicationContext.xml文件中定义的bean是singleton范围的,并设置为预实例化。这意味着在创建ClassPathXmlApplicationContext的实例时,在applicationcontext.xml文件中定义的bean也会被创建。
现在我们已经看到如何创建一个Spring容器的实例,下面来看如何从Sprinq容器中检索bean实例。
6 从Spring容器访问bean通过<bean>元素定义的应用程序对象由Spring容器创建和管理。可以通过调用ApplicationcContext接口的getBean方法来访问这些应用程序对象的实例。
代码清单1-16展示了BankApp类的main方法,它从Spring容器中检索FixedDepositController bean实例并调用其方法。
代码清单1-16 BankApp类
首先调用ApplicationContext的getBean方法,从Sprinq容器中检索FixedDepositController bean的一个实例,然后调用FixedDepositController bean的submit和get方法。要从Spring容器检索其实例的bean的名称是传递给getBean方法的参数。传递给getBean方法的bean的名称必须是要检索的bean的id或name特性的值。如果没有指定名称的bean注册到Spring容器中, getBean方法将抛出异常。
在代码清单1-16中,要配置FixedDepositController实例,我们没有以编程方式创建FixedDepositService的实例并将其设置在FixedDepositController实例上,也没有创建一个FixedDepositDao的实例并将其设置在FixedDepositService实例上。这是因为创建依赖项并将它们注入依赖对象中的任务是由Spring容器处理的。
如果进入ch01-bankapp-xml项目并执行BankApp类的main方法,将在控制台上看到以下输出内容。
由上面的输出可知, Spring容器将所有在MyBank应用程序的applicationContext.xml文件中定义的bean都创建了一个实例。此外, Spring容器使用基于setter的DI将FixedDepositService的实例注入FixedDepositController实例中,并将FixedDepositDao的实例注入FixedDepositService实例中。
本文节选自《Spring学习指南 第4版》
Spring 框架是以简化J2EE 应用程序开发为特定目标而创建的,是当前最流行的Java 开发框架。本书主要介绍Beans 的配置、依赖注入、定制bean、基于Java 的容器、AOP、Spring Data、Spring MVC 等内容。本书基于非常适合构建JavaWeb 微服务框架的Spring 5 编写,涵盖Java 的函数式编程、RxJava 2 的反应式编程、用Spring WebFlux、Spring Data 和Spring Security 开发反应式RESTful Web 服务等内容。 本书适合Web 开发者和想使用Spring 的初学者参考,也可供对Web 开发和Spring 感兴趣的读者参考。
,