7. 解决方案
7.1. Visual Studio 项目
![]() | ![]() |
注释:
- Spring 所需的文件已放置在 [bin] 文件夹中:Spring.Core.dll、log4net.dll、Spring.Core.xml
- Spring的配置文件[spring-config-3tier-*.xml]也已放置在文件夹[bin]中
- 在根目录 [istia] 下,包含应用程序的各个类
该项目已配置为在 [bin] 文件夹中生成 DLL 和 [spring3tier.dll]:

7.2. [istia.st.spring3tier.dao] 包
IDao 接口:
Namespace istia.st.spring3tier.dao
Public Interface IDao
' 在图层中执行某项操作 [dao]
Function doSomethingInDaoLayer(ByVal a As Integer, ByVal b As Integer) As Integer
End Interface
End Namespace
一种初始实现:
Namespace istia.st.spring3tier.dao
Public Class DaoImpl1
Implements istia.st.spring3tier.dao.IDao
' 在图层中执行某项操作 [dao]
Public Function doSomethingInDaoLayer(ByVal a As Integer, ByVal b As Integer) As Integer Implements IDao.doSomethingInDaoLayer
Return a + b
End Function
End Class
End Namespace
- 该类没有私有字段
- 方法 [doSomethingInDaoLayer] 按要求返回其参数的总和。
第二个实现类:
Namespace istia.st.spring3tier.dao
Public Class DaoImpl2
Implements istia.st.spring3tier.dao.IDao
' 在图层中执行某项操作 [dao]
Public Function doSomethingInDaoLayer(ByVal a As Integer, ByVal b As Integer) As Integer Implements IDao.doSomethingInDaoLayer
Return a - b
End Function
End Class
End Namespace
- 该类没有私有字段
- 方法 [doSomethingInDaoLayer] 按要求返回其参数的差值。
7.3. 包 [istia.st.spring3tier.domain]
接口 IDomain:
Namespace istia.st.spring3tier.domain
Public Interface IDomain
' 在 [domain] 层中执行某项操作
Function doSomethingInDomainLayer(ByVal a As Integer, ByVal b As Integer) As Integer
End Interface
End Namespace
IDomainImpl1 的首个实现类:
Imports istia.st.spring3tier.dao
Namespace istia.st.spring3tier.domain
Public Class DomainImpl1
Implements istia.st.spring3tier.domain.IDomain
' 私有字段
Private _dao As IDao
' 关联属性
Public WriteOnly Property dao() As IDao
Set(ByVal Value As IDao)
_dao = Value
End Set
End Property
' 默认构造函数
Public Sub New()
End Sub
' 在 [domain] 层中执行某项操作
Public Function doSomethingInDomainLayer(ByVal a As Integer, ByVal b As Integer) As Integer Implements IDomain.doSomethingInDomainLayer
a += 1
b += 1
Return _dao.doSomethingInDaoLayer(a, b)
End Function
End Class
End Namespace
- 该类有一个私有字段,该字段引用了类型为 [IDao] 的单例,该单例提供对 [Dao] 层的访问。该字段将在对象构造时由 Spring(依赖注入)进行初始化。
- 方法 [doSomethingInDomainLayer] 会递增其参数,然后将这些参数传递给单例 [dao] 的方法 [doSomethingInDaoLayer]
第二个实现类 IDomainImpl2:
Imports istia.st.spring3tier.dao
Namespace istia.st.spring3tier.domain
Public Class DomainImpl2
Implements istia.st.spring3tier.domain.IDomain
' 私有字段
Private _dao As IDao
' 关联属性
Public WriteOnly Property dao() As IDao
Set(ByVal Value As IDao)
_dao = Value
End Set
End Property
' 默认构造函数
Public Sub New()
End Sub
' 在 [domain] 层中执行某项操作
Public Function doSomethingInDomainLayer(ByVal a As Integer, ByVal b As Integer) As Integer Implements IDomain.doSomethingInDomainLayer
a -= 1
b -= 1
Return _dao.doSomethingInDaoLayer(a, b)
End Function
End Class
End Namespace
- 该类有一个私有字段,该字段引用了类型为 [IDao] 的单例,该单例提供对 [Dao] 层的访问。该字段将在对象构造时由 Spring(依赖注入)进行初始化。
- 方法 [doSomethingInDomainLayer] 会递减其参数,然后将它们传递给单例 [dao] 的方法 [doSomethingInDaoLayer]
7.4. [istia.st.spring3tier.control] 包
接口 IControl:
Namespace istia.st.spring3tier.control
Public Interface IControl
' 在 [control] 层中执行某项操作
Function doSomethingInControlLayer(ByVal a As Integer, ByVal b As Integer) As Integer
End Interface
End Namespace
ControlImpl1 的首个实现类:
Imports istia.st.spring3tier.domain
Namespace istia.st.spring3tier.control
Public Class ControlImpl1
Implements istia.st.spring3tier.control.IControl
' 私有字段
Private _domain As IDomain
' 关联属性
Public WriteOnly Property domain() As IDomain
Set(ByVal Value As IDomain)
_domain = Value
End Set
End Property
' 默认构造函数
Public Sub New()
End Sub
' 在 [control] 层中执行某项操作
Public Function doSomethingInControlLayer(ByVal a As Integer, ByVal b As Integer) As Integer Implements IControl.doSomethingInControlLayer
a += 1
b += 1
Return _domain.doSomethingInDomainLayer(a, b)
End Function
End Class
End Namespace
- 该类有一个私有字段,该字段引用了类型为 [IDomain] 的单例,该单例提供对 [Domain] 层的访问。该字段将在对象构造时由 Spring(依赖注入)进行初始化。
- 方法 [doSomethingInControlLayer] 会递增其参数,然后将这些参数传递给单例 [domain] 中的方法 [doSomethingInDomainLayer]
第二个实现类 IControlImpl2:
Imports istia.st.spring3tier.domain
Namespace istia.st.spring3tier.control
Public Class ControlImpl2
Implements istia.st.spring3tier.control.IControl
' 私有字段
Private _domain As IDomain
' 关联属性
Public WriteOnly Property domain() As IDomain
Set(ByVal Value As IDomain)
_domain = Value
End Set
End Property
' 默认构造函数
Public Sub New()
End Sub
' 在 [control] 层中执行某项操作
Public Function doSomethingInControlLayer(ByVal a As Integer, ByVal b As Integer) As Integer Implements IControl.doSomethingInControlLayer
a -= 1
b -= 1
Return _domain.doSomethingInDomainLayer(a, b)
End Function
End Class
End Namespace
- 该类有一个私有字段,该字段引用了类型为 [IDomain] 的单例,该单例提供对 [Domain] 层的访问。该字段将在对象构造时由 Spring(依赖注入)进行初始化。
- 方法 [doSomethingInControlLayer] 会递减其参数,然后将这些参数传递给单例 [domain] 的方法 [doSomethingInDomainLayer]。
7.5. 配置文件 [Spring]
文件 [spring-config-3tier-1.xml] 使用以下实现的第 1 版:
<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">
<objects>
<!-- DAO类 -->
<object id="dao" type="istia.st.spring3tier.dao.DaoImpl1, spring3tier"></object>
<!-- 域类 -->
<object id="domain" type="istia.st.spring3tier.domain.DomainImpl1, spring3tier">
<property name="dao">
<ref object="dao" />
</property>
</object>
<!-- 控制类 -->
<object id="control" type="istia.st.spring3tier.control.ControlImpl1, spring3tier">
<property name="domain">
<ref object="domain" />
</property>
</object>
</objects>
文件 [spring-config-3tier-2.xml] 使用以下实现的第 2 版:
<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">
<objects>
<!-- DAO类 -->
<object id="dao" type="istia.st.spring3tier.dao.DaoImpl2, spring3tier"></object>
<!-- 域类 -->
<object id="domain" type="istia.st.spring3tier.domain.DomainImpl2, spring3tier">
<property name="dao">
<ref object="dao" />
</property>
</object>
<!-- 控制类 -->
<object id="control" type="istia.st.spring3tier.control.ControlImpl2, spring3tier">
<property name="domain">
<ref object="domain" />
</property>
</object>
</objects>
7.6. 测试包 [istia.st.spring3tier.tests]
一个 Nunit 测试 [NunitTestSpring3tier.vb]:
Imports System
Imports Spring.Objects.Factory.Xml
Imports System.IO
Imports NUnit.Framework
Imports istia.st.spring3tier.control
Imports istia.st.spring3tier.domain
Namespace istia.st.springioc.tests
<TestFixture()> _
Public Class NunitTestSpring3tier
' 单例工厂
Private factory1 As XmlObjectFactory
Private factory2 As XmlObjectFactory
<SetUp()> _
Public Sub init()
' 创建单例工厂
factory1 = New XmlObjectFactory(New FileStream("spring-config-3tier-1.xml", FileMode.Open))
factory2 = New XmlObjectFactory(New FileStream("spring-config-3tier-2.xml", FileMode.Open))
End Sub
<TearDown()> _
Public Sub destroy()
' 销毁单例
factory1.Dispose()
factory2.Dispose()
' 释放单例工厂
factory1 = Nothing
factory2 = Nothing
End Sub
<Test()> _
Public Sub test()
'获取接口的实现IControl
Dim control1 As IControl = CType(factory1.GetObject("control"), IControl)
' 使用该类
Dim a1 As Integer = 10, b1 As Integer = 20
Dim res1 As Integer = control1.doSomethingInControlLayer(a1, b1)
Assert.AreEqual(34, res1)
' 获取接口的另一个实现 IControl
Dim control2 As IControl = CType(factory2.GetObject("control"), IControl)
' 使用该类
Dim a2 As Integer = 10, b2 As Integer = 20
Dim res2 As Integer = control2.doSomethingInControlLayer(a2, b2)
Assert.AreEqual(-10, res2)
End Sub
End Class
End Namespace
执行此测试得到以下结果:

使用该文档彩色版本的读者会发现结果显示为“绿色”,这表明测试成功。
7.7. 另一种 Spring 配置文件类型
应用程序 VB.net 可通过名为 [App.config] 的文件进行配置。该文件位于 Visual Studio 项目的根目录下:

Spring 可以利用此配置文件。以下是参考 [Spring.net] 文档中的示例编写的 [App.config] 文件:
<?xml version="1.0" encoding="iso-8859-1" ?>
<configuration>
<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
</sectionGroup>
</configSections>
<spring>
<context type="Spring.Context.Support.XmlApplicationContext, Spring.Core">
<resource uri="config://spring/objects" />
</context>
<objects>
<!-- 初始配置 -->
<!-- DAO类 -->
<object id="dao1" type="istia.st.spring3tier.dao.DaoImpl1, spring3tier"></object>
<!-- 域类 -->
<object id="domain1" type="istia.st.spring3tier.domain.DomainImpl1, spring3tier">
<property name="dao">
<ref object="dao1" />
</property>
</object>
<!-- 控制类 -->
<object id="control1" type="istia.st.spring3tier.control.ControlImpl1, spring3tier">
<property name="domain">
<ref object="domain1" />
</property>
</object>
<!-- 第二个配置 -->
<!-- DAO类 -->
<object id="dao2" type="istia.st.spring3tier.dao.DaoImpl2, spring3tier"></object>
<!-- 域类 -->
<object id="domain2" type="istia.st.spring3tier.domain.DomainImpl2, spring3tier">
<property name="dao">
<ref object="dao2" />
</property>
</object>
<!-- 控制类 -->
<object id="control2" type="istia.st.spring3tier.control.ControlImpl2, spring3tier">
<property name="domain">
<ref object="domain2" />
</property>
</object>
</objects>
</spring>
</configuration>
注:以下信息仅供参考。我不确定是否正确理解了上述配置文件中所有元素的含义。
文件 [App.config] 的语法 XML 要求其必须符合以下语法:
[App.config]中各部分的处理可委托给外部程序。[ConfigSections]部分即采用了这种做法:
<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
</sectionGroup>
</configSections>
上述代码表示名为 [spring/context] 的部分应由 [Spring.Context.Support.ContextHandler] 类管理,该类该类位于[Spring.Core.dll]组件中;而名为[spring/objects]的段应由[Spring.Context.Support.DefaultSectionHandler]类管理,该类同样位于[Spring.Core.dll]组件中。
[spring/context] 部分如下:
<spring>
<context type="Spring.Context.Support.XmlApplicationContext, Spring.Core">
<resource uri="config://spring/objects" />
</context>
...
</spring>
这似乎表明,配置文件中的 [spring/objects] 部分应由 [Spring.Core.dll] 组件中的 [Spring.Context.Support.XmlApplicationHandler] 类管理。 该类必须使用资源 XML,其位置为 [ config://spring/objects]、c.a.d 以及当前配置文件中的 [spring/objects] 部分。
在 [spring/objects] 部分中,我们可以看到我们已经习惯的 Spring 语法。
在文件 [App.config] 的 <objects>... </objects> 部分中,我们为三层应用定义了两种可能的配置:
- 一种使用接口实现的第 1 版
- 另一种使用这些接口的第 2 版实现
那么,如何使用文件 [App.config] 呢?
以下代码展示了一个利用前述 [App.config] 文件的控制台应用程序:
Imports System
Imports Spring.Context
Imports System.IO
Imports NUnit.Framework
Imports istia.st.spring3tier.control
Imports istia.st.spring3tier.domain
Imports System.Configuration
Namespace istia.st.springioc.tests
Module MainTestSpring3tier
Public Sub main()
' Spring 上下文,用于获取单例
Dim contexte As IApplicationContext = CType(ConfigurationSettings.GetConfig("spring/context"), IApplicationContext)
' 获取接口的第一个实现IControl
Dim control1 As IControl = CType(contexte.GetObject("control1"), IControl)
' 使用该类
Dim a1 As Integer = 10, b1 As Integer = 20
Console.WriteLine("res1({0},{1})={2}", a1, b1, control1.doSomethingInControlLayer(a1, b1))
' 获取该接口的另一个实现 IControl
Dim control2 As IControl = CType(contexte.GetObject("control2"), IControl)
' 使用该类
Dim a2 As Integer = 10, b2 As Integer = 20
Console.WriteLine("res2({0},{1})={2}", a2, b2, control2.doSomethingInControlLayer(a2, b2))
' 暂停
Console.WriteLine("Tapez [entrée] pour continuer...")
Console.ReadLine()
End Sub
End Module
End Namespace
注释:
- 在我们迄今为止使用的 NUnit 示例中,我们使用 [XmlObjectFactory] 对象来获取所需的单例。此处使用的对象类型为 [IApplicationContext],这是一个 Spring 接口。 它是通过 [ConfigurationSettings] 类从 [App.config] 派生而来的,该类通常用于 .NET 中以处理配置文件。
- 我们请求 [spring/context] 节点的管理器。 若查阅文件 [App.config],会发现该文件属于 [XmlApplicationContext] 类型,且负责管理 [App.config] 中的 [spring/objects] 部分。
- 一旦获取了类型为 [IApplicationContext] 的对象,其使用方式与我们此前使用的 [XmlObjectFactory] 对象相同。
上述程序名为 [MainTestSpring3tier.vb],位于 [tests] 包中:

项目 [spring3tier] 已配置为使用 [MainTestSpring3tier]:

执行该项目后,结果如下:
7.8. Conclusion
Spring框架在应用程序架构和配置方面都提供了真正的灵活性。我们采用了IoC这一概念,它是Spring的两大支柱之一。 另一个支柱是 AOP(面向切面编程),我们尚未介绍。它允许通过配置为类的方法添加“行为”,而无需修改该方法的代码。简而言之,AOP 可以过滤对某些方法的调用:
![]() |
- 过滤器可在目标方法 M 之前、之后或两者同时执行。
- 方法 M 不会感知这些过滤器的存在。这些过滤器在 Spring 的配置文件中定义。
- 方法 M 的代码不会被修改。过滤器是需要构建的 Java 类。Spring 提供了预定义的过滤器,特别是用于管理 SGBD 的事务。
- 过滤器是 Bean,因此作为 Bean 在 Spring 配置文件中进行定义。
一种常见的过滤器是事务过滤器。 假设业务层有一个方法 M,该方法对数据执行两项不可分割的操作(工作单元)。它调用 DAO 层中的两个方法 M1 和 M2 来完成这两项操作。
![]() |
由于位于业务层,方法 M 忽略了这些数据的存储介质。例如,它无需 需要假设数据位于 SGBD 中,并将其对 M1 和 M2 方法的两次调用置于 SGBD 的事务中。 这些细节应由 DAO 层负责处理。 因此,解决上述问题的方案是在 DAO 层创建一个方法,该方法将自行调用 M1 和 M2 方法, 并将这些调用封装在 SGBD 的事务中。
![]() |
AOP 的过滤方案更为灵活。它允许定义一个过滤器,该过滤器将在调用 M 之前启动一个事务,并在调用结束后根据具体情况执行提交或回滚。
![]() |
这种方法有以下几个优点:
- 一旦定义了过滤器,即可将其应用于多个方法,例如所有需要事务的方法
- 经过筛选的方法无需重写
- 由于要使用的过滤器通过配置定义,因此可以进行更改
更多信息请参阅:http://www.springframework.net。





