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

7.2. 包 [istia.st.spring3tier.dao]
IDao 接口:
Namespace istia.st.spring3tier.dao
Public Interface IDao
' do something in the [dao] layer
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
' do something in the diaper [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
' do something in the diaper [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
' do something in the [domain] layer
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 fields
Private _dao As IDao
' associated property
Public WriteOnly Property dao() As IDao
Set(ByVal Value As IDao)
_dao = Value
End Set
End Property
' default builder
Public Sub New()
End Sub
' do something in the [domain] layer
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 fields
Private _dao As IDao
' associated property
Public WriteOnly Property dao() As IDao
Set(ByVal Value As IDao)
_dao = Value
End Set
End Property
' default builder
Public Sub New()
End Sub
' do something in the [domain] layer
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
' do something in the [control] layer
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 fields
Private _domain As IDomain
' associated property
Public WriteOnly Property domain() As IDomain
Set(ByVal Value As IDomain)
_domain = Value
End Set
End Property
' default builder
Public Sub New()
End Sub
' do something in the [control] layer
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 fields
Private _domain As IDomain
' associated property
Public WriteOnly Property domain() As IDomain
Set(ByVal Value As IDomain)
_domain = Value
End Set
End Property
' default builder
Public Sub New()
End Sub
' do something in the [control] layer
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>
<!-- the dao class -->
<object id="dao" type="istia.st.spring3tier.dao.DaoImpl1, spring3tier"></object>
<!-- the domain class -->
<object id="domain" type="istia.st.spring3tier.domain.DomainImpl1, spring3tier">
<property name="dao">
<ref object="dao" />
</property>
</object>
<!-- the control class -->
<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>
<!-- the dao class -->
<object id="dao" type="istia.st.spring3tier.dao.DaoImpl2, spring3tier"></object>
<!-- the domain class -->
<object id="domain" type="istia.st.spring3tier.domain.DomainImpl2, spring3tier">
<property name="dao">
<ref object="dao" />
</property>
</object>
<!-- the control class -->
<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
' the singleton factories
Private factory1 As XmlObjectFactory
Private factory2 As XmlObjectFactory
<SetUp()> _
Public Sub init()
' singleton factories are created
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()
' we destroy the singletons
factory1.Dispose()
factory2.Dispose()
' we free the singletons factories
factory1 = Nothing
factory2 = Nothing
End Sub
<Test()> _
Public Sub test()
'we retrieve an implementation of the IControl interface
Dim control1 As IControl = CType(factory1.GetObject("control"), IControl)
' we use the
Dim a1 As Integer = 10, b1 As Integer = 20
Dim res1 As Integer = control1.doSomethingInControlLayer(a1, b1)
Assert.AreEqual(34, res1)
' we retrieve another implementation of the IControl interface
Dim control2 As IControl = CType(factory2.GetObject("control"), IControl)
' we use the
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 配置文件
可以通过名为 [App.config] 的文件来配置 VB.NET 应用程序。该文件位于 Visual Studio 项目的根目录下:

Spring 可以利用此配置文件。请看以下 [App.config] 文件,其灵感来源于 [Spring.net] 文档中的示例:
<?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>
<!-- an initial configuration -->
<!-- the dao class -->
<object id="dao1" type="istia.st.spring3tier.dao.DaoImpl1, spring3tier"></object>
<!-- the domain class -->
<object id="domain1" type="istia.st.spring3tier.domain.DomainImpl1, spring3tier">
<property name="dao">
<ref object="dao1" />
</property>
</object>
<!-- the control class -->
<object id="control1" type="istia.st.spring3tier.control.ControlImpl1, spring3tier">
<property name="domain">
<ref object="domain1" />
</property>
</object>
<!-- a second configuration -->
<!-- the dao class -->
<object id="dao2" type="istia.st.spring3tier.dao.DaoImpl2, spring3tier"></object>
<!-- the domain class -->
<object id="domain2" type="istia.st.spring3tier.domain.DomainImpl2, spring3tier">
<property name="dao">
<ref object="dao2" />
</property>
</object>
<!-- the control class -->
<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.Core.dll] 程序集中的 [Spring.Context.Support.ContextHandler] 类进行管理,而名为 [spring/objects] 的配置段必须由同样位于 [Spring.Core.dll] 程序集中的 [Spring.Context.Support.DefaultSectionHandler] 类进行处理。
[spring/context] 部分如下:
<spring>
<context type="Spring.Context.Support.XmlApplicationContext, Spring.Core">
<resource uri="config://spring/objects" />
</context>
...
</spring>
这似乎表明,配置文件中的 [spring/objects] 部分必须由 [Spring.Context.Support.XmlApplicationHandler] 类进行管理,该类位于 [Spring.Core.dll] 程序集之中。该类必须使用位于 [config://spring/objects] 的 XML 资源,即当前配置文件中的 [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()
' the Spring context that will allow us to retrieve singletons
Dim contexte As IApplicationContext = CType(ConfigurationSettings.GetConfig("spring/context"), IApplicationContext)
' we retrieve a 1st implementation of the IControl interface
Dim control1 As IControl = CType(contexte.GetObject("control1"), IControl)
' we use the
Dim a1 As Integer = 10, b1 As Integer = 20
Console.WriteLine("res1({0},{1})={2}", a1, b1, control1.doSomethingInControlLayer(a1, b1))
' we retrieve another implementation of the IControl interface
Dim control2 As IControl = CType(contexte.GetObject("control2"), IControl)
' we use the
Dim a2 As Integer = 10, b2 As Integer = 20
Console.WriteLine("res2({0},{1})={2}", a2, b2, control2.doSomethingInControlLayer(a2, b2))
' break
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. 结论
Spring 框架在应用程序架构和配置方面都提供了真正的灵活性。我们使用了 IoC 概念,这是 Spring 的两大支柱之一。另一个支柱是 AOP(面向切面的编程),我们在此未作探讨。它允许您通过配置向类方法添加“行为”,而无需修改方法的代码。简而言之,AOP 允许您过滤对某些方法的调用:
![]() |
- 该过滤器可以在目标方法 M 之前、之后,或两者同时执行。
- 方法 M 并不知道这些过滤器。它们在 Spring 配置文件中定义。
- 方法 M 的代码不会被修改。过滤器是必须实现的 Java 类。Spring 提供了预定义的过滤器,特别是用于管理 DBMS 事务的过滤器。
- 过滤器是 Bean,因此需作为 Bean 在 Spring 配置文件中进行定义。
一个常见的过滤器是事务过滤器。假设有一个业务层方法 M,它对数据执行两项不可分割的操作(即一个工作单元)。它调用两个 DAO 层方法 M1 和 M2 来执行这两项操作。
![]() |
由于位于业务层,方法 M 抽象掉了底层的数据存储。例如,它无需假设数据位于数据库管理系统(DBMS)中,也无需将对方法 M1 和 M2 的两次调用置于 DBMS 事务中。这些细节应由 DAO 层来处理。 解决上述问题的一种方案是在 DAO 层创建一个方法,该方法自身调用 M1 和 M2 方法,并将这些调用封装在 DBMS 事务中。
![]() |
基于AOP的过滤方案更为灵活。它允许您定义一个过滤器,该过滤器在调用M之前会启动事务,并在调用结束后根据情况执行提交或回滚。
![]() |
这种方法有以下几个优点:
- 一旦定义了过滤器,它就可以应用于多个方法,例如所有需要事务的方法
- 经过筛选的方法无需重写
- 由于要使用的过滤器是通过配置定义的,因此可以进行更改
更多信息请参阅:http://www.springframework.net。





