7.3.2 设值注入
7.3.2 设值注入
设值注入是指IoC
容器通过成员变量的setter
方法来注入被依赖对象。这种注入方式简单、直观,因而在Spring
的依赖注入里大量使用。
下面示例将会对前面示例进行改写,使之更加规范。 Spring
推荐面向接口编程。不管是调用者,还是被依赖对象,都应该为之定义接口,程序应该面向它们的接口,而不是面向实现类编程,这样以便程序后期的升级、维护。
程序示例
项目结构
1 | E:\workspace_QingLiangJiJavaEEQiYeYingYongShiZhang5\setter |
Person.java
下面先定义一个Person
接口,该接口定义了一个Person
对象应遵守的规范。下面是Person
接口的代码。
1 | package org.crazyit.app.service; |
Axe.java
下面是Axe
接口的代码。
1 | package org.crazyit.app.service; |
Spring
推荐面向接口编程,这样可以更好地让规范和实现分离,从而提供更好的解耦。对于一个Java EE
应用,不管是DAO
组件,还是业务逻辑组件,都应该先定义一个接口,该接口定义了该组件应该实现的功能,但功能的实现则由其实现类提供。
Chinese.java
下面是Person
实现类的代码。
1 | package org.crazyit.app.service.impl; |
上面程序中的粗体字代码实现了Person
接口的useAxe()
方法,实现该方法时调用了axe
的chop()
方法,这就是典型的依赖关系。
回忆一下曾经编写的Java
应用,除了最简单的Hello world
之外,哪个应用不是A调用B、B调用C、C调用D…这种方式?那Spring
的作用呢? Spring
容器的最大作用就是以松耦合的方式来管理这种调用关系。在上面的Chinese
类中, Chinese
类并不知道它要调用的axe
实例在哪里,也不知道axe
实例是如何实现的,它只是需要调用Axe
对象的方法,这个Axe
实例将由Spring
容器负责注入。
SteelAxe.java
1 | package org.crazyit.app.service.impl; |
到现在为止,程序依然不知道Chinese
类和哪个Axe
实例耦合, Spring
当然也不知道! Spring
需要使用XML
配置文件来指定实例之间的依赖关系.Spring
采用了XML
配置文件,从Spring2.0
开始, Spring
推荐采用XML Schema
来定义配置文件的语义约束。当采用XML Schema
来定义配置文件的语义约束时,还可利用Spring
配置文件的扩展性进一步简化Spring
配置。Spring
为基于XML Schema
的XML
配置文件提供了一些新的标签,这些新标签使配置更简单,使用更方便。关于如何使用Spring
所提供的新标签,后面会有更进一步的介绍。
不仅如此,采用基于XML Schema
的XML
配置文件时, Spring
甚至允许程序员开发自定义的配置文件标签,让其他开发人员在Spring
配置文件中使用这些标签,但这些通常应由第三方供应商来完成。对普通软件开发人员以及普通系统架构师而言,则通常无须开发自定义的Spring
配置文件标签。所以本书也不打算介绍相关方面的内容。
提示:本书并不是一本完整的Spring
学习手册,所以本书只会介绍Spring
的核心机制,包括loC
、SpEL
、AOP
和资源访问等, Spring
和Hibernate
整合, Spring
的DAO
支持和事务管理,以及Spring
和Struts2
整合等内容,这些是Java ee
开发所需要的核心知识。而Spring
框架的其他方面,本书不会涉及.
下面是本应用所用的配置文件代码。
beans.xml
1 |
|
在配置文件中, Spring
配置Bean
实例通常会指定两个属性。
id
:指定该Bean
的唯一标识,Spring
根据id
属性值来管理Bean
,程序通过id
属性值来访问该Bean
实例。class
:指定该Bean
的实现类,此处不可再用接口,必须使用实现类,Spring
容器会使用XML
解析器读取该属性值,并利用反射来创建该实现类的实例。
可以看到Spring
管理Bean
的灵巧性。Bean
与Bean
之间的依赖关系放在配置文件里组织,而不是写在代码里。通过配置文件的指定, Spring
能精确地为每个Bean
的成员变量注入值。
Spring
会自动检测每个<bean>
定义里的<property>
元素定义, Spring
会在调用默认的构造器创建Bean
实例之后,立即调用对应的setter
法为Bean
的成员变量注入值。
下面是主程序的代码,该主程序只是简单地获取了Person
实例,并调用该实例的useAxeo
方法
1 | package lee; |
上面程序中的实现了创建Spring
容器,并通过Spring
容器来获取Bean
实例。从上面程序中可以看出, Spring
容器就是一个巨大的工厂,它可以”生产”出所有类型的Bean
实例。程序获取Bean
实例的方法是getBean()
。
一旦通过Spring
容器获得了Bean
实例之后,如何调用Bean
实例的方法就没什么特别之处了。执行上面程序,会看到如下执行结果:
1 | 石斧砍柴好慢 |
主程序调用Person
的useAxe()
方法时,该方法的方法体内需要使用Axe
实例,但程序没有任何地方将特定的Person
实例和Axe
实例耦合在一起。或者说,程序没有为Person
实例传入Axe
实例,Axe
实例由Spring
在运行期间注入。
下面是Axe
的另一个实现类:SteelAxe
SteelAxe.java
1 | package org.crazyit.app.service.impl; |
将修改后的SteelAxe
部署在Spring
容器中,只需在Spring
配置文件中增加如下一行:
1 | <!-- 配置steelAxe实例,其实现类是SteelAxe --> |
该行重新定义了一个Axe
实例,它的id是steelAxe
,实现类是SteelAxe
。然后修改chineseBean
的配置,将原来传入stoneAxe
的地方改为传入steelAxe
。也就是将
1 | <property name="axe" ref="stoneAxe" /> |
改成:
1 | <property name="axe" ref="steelAxe" /> |
此时再次执行程序,将得到如下结果:
从上面这种切换可以看出,因为chinese
实例与具体的Axe
实现类没有任何关系, chinese
实例仅仅与Axe
接口耦合,这就保证了chinese
实例与Axe
实例之间的松耦合—这也是Spring
强调面向接口编程的原因。
设值注入
Bean
与Bean
之间的依赖关系由Spring
管理,** Spring
采用setter
方法为目标Bean
注入所依赖的Bean
这种方式被称为设值注入**。
从上面示例程序中应该可以看出,依赖注入以配置文件管理Bean
实例之间的耦合,让Bean
实例之间的耦合从代码层次分离出来。依赖注入是一种优秀的解耦方式。
Spring IoC容器的三个基本要点
经过上面的介绍,不难发现使用Spring loC
容器的三个基本要点:
- 应用程序的各组件面向接口编程。面向接口编程可以将组件之间的耦合关系提升到接口层次从而有利于项目后期的扩展。
- 应用程序的各组件不再由程序主动创建,而是由
Spring
容器来负责产生并初始化。 Spring
采用配置文件
或注解
来管理Bean
的实现类、依赖关系,Spring
容器则根据配置文件或注解,利用反射来创建实例,并为之注入依赖关系