4. 依赖注入
依赖注入可视为控制反转的自然结果。我们将通过一个新示例进行说明。考虑以下三层Web应用程序:
![]() |
假设对 DAO 层的访问由之前讨论过的 [IArticlesDao] 接口控制,且整个应用程序是一个用于在网上购买商品的 Web 应用程序。 商品由 [Dao] 层管理。假设对业务层的访问由以下接口控制:
Public Interface IArticlesDomain
' 购买一篮商品
Sub acheter(ByVal panier As Panier)
' 获取商品列表
Function getAllArticles() As IList
' 获取特定商品
Function getArticleById(ByVal idArticle As Integer) As Article
' 用于记录错误
ReadOnly Property erreurs() As ArrayList
End Interface
我们不会过多探讨各个方法的含义。只需注意,用于获取所有在售商品列表的方法 [getAllArticles] 需要访问相关数据。为了获取这些数据,它必须调用接口 [IArticlesDao]。 [IArticlesDomain] 接口的实现类骨架可能如下所示:
Imports istia.st.articles.dao
...
Namespace istia.st.articles.domain
Public Class AchatsArticles
Implements IArticlesDomain
'私有字段
Private _articlesDao As IArticlesDao
Private _erreurs As ArrayList
' 构造函数
Public Sub New(ByVal articlesDao As IArticlesDao)
_articlesDao = articlesDao
End Sub
...
End Class
End Namespace
如前所述,业务层接口中的某些方法需要向 [Dao] 层请求数据。 因此,我们对接口 [IArticlesDomain] 的实现类 [AchatsArticles] 需要一个指向接口 [IArticlesDao] 实现类的引用。 上文中的私有字段 [_articlesDao] 即为该引用。该引用是在构造 [AchatsArticles] 对象时提供的。
假设已编写了类 [AchatsArticles],且希望使用类型为 [Nunit] 的测试用例对其进行测试。让我们构建该测试用例,使其使用 Spring 配置文件:
...
Imports Spring.Objects.Factory.Xml
Imports System.IO
...
<TestFixture()> _
Public Class NunitSpringTestArticlesDomain
' 待测试对象
Private articlesDomain As IArticlesDomain
<SetUp()> _
Public Sub init()
' 获取 Spring 对象生成器的实例
Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config-domain.xml", FileMode.Open))
' 请求实例化articles dao对象
articlesDomain = CType(factory.GetObject("articlesdomain"), IArticlesDao)
End Sub
...
End Class
Spring 的配置文件 [spring-config-domain.xml] 内容是什么?可能如下所示:
<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">
<objects>
<description>Gestion d'une table d'articles</description>
<!-- 接口的实现类 IArticlesDao -->
<object id="articlesdao" type="istia.st.articles.dao.ArticlesDaoPlainODBC, articlesdao">
<constructor-arg index="0">
<value>odbc-firebird-articles</value>
</constructor-arg>
<constructor-arg index="1">
<value>SYSDBA</value>
</constructor-arg>
<constructor-arg index="2">
<value>masterkey</value>
</constructor-arg>
</object>
<!-- 接口的实现类 IArticlesDomain -->
<object id="articlesdomain" type="istia.st.articles.domain.AchatsArticles, articlesdomain">
<constructor-arg index="0">
<ref object="articlesdao" />
</constructor-arg>
</object>
</objects>
该文件是此前用于实例化 [Dao] 层中 [IArticlesDao] 类型单例的文件,我们在此基础上添加了用于实例化业务层中 [IArticlesDomain] 类型单例的代码。该单例将如何构建?
- 外部代码向Spring请求配置文件中名为“articlesdomain”的单例的引用。这正是我们测试类中[init]方法的情况:
<SetUp()> _
Public Sub init()
' 获取 Spring 对象生成器的实例
Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config-domain.xml", FileMode.Open))
' 请求实例化 articles dao 对象
articlesDomain = CType(factory.GetObject("articlesdomain"), IArticlesDao)
End Sub
- Spring 在其配置文件中找到了该单例的定义。它发现,要实例化该单例,需要另一个名为“articlesdao”的单例:
<object name="articlesdomain" class="istia.st.articles.domain.AchatsArticles, articlesdomain">
<constructor-arg index="0">
<ref object="articlesdao" />
</constructor-arg>
</object>
- Spring 随后将实例化单例“articlesdao”。我们之前已经解释过它是如何实现的。
- 完成此操作后,它便可实例化单例“articlesdomain”,并将该实例的引用返回给请求它的代码。
该机制可概括如下:
![]() |
可见,Spring 已处理了单例“articlesdomain”对单例“articlesdao”的依赖关系。为了以这种方式使用 Spring,我们需要构建一个带有构造函数的类,该构造函数将依赖的单例作为参数:
Imports istia.st.articles.dao
...
Namespace istia.st.articles.domain
Public Class AchatsArticles
Implements IArticlesDomain
'私有字段
Private _articlesDao As IArticlesDao
' 构造函数
Public Sub New(ByVal articlesDao As IArticlesDao)
_articlesDao = articlesDao
End Sub
...
End Class
End Namespace
“依赖注入”这一术语同时涵盖:
- 根据依赖关系构建待实例化类的特定方式
- Spring在实例化这些类时管理这些依赖关系的方式

