7. Una soluzione
7.1. Il progetto Visual Studio
![]() | ![]() |
Commenti:
- I file richiesti da Spring sono stati inseriti nella cartella [bin]: Spring.Core.dll, log4net.dll, Spring.Core.xml
- Anche i file di configurazione di Spring [spring-config-3tier-*.xml] sono stati inseriti nella cartella [bin]
- Nella directory principale [istia] sono presenti le varie classi dell'applicazione
Il progetto è stato configurato per generare la DLL [spring3tier.dll] nella cartella [bin]:

7.2. Il pacchetto [istia.st.spring3tier.dao]
L'interfaccia 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
Una prima classe di implementazione:
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
- La classe non ha campi privati
- Il metodo [doSomethingInDaoLayer] restituisce la somma dei suoi parametri come richiesto.
Una seconda classe di implementazione:
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
- La classe non ha campi privati
- Il metodo [doSomethingInDaoLayer] restituisce la differenza tra i suoi parametri come richiesto.
7.3. Il pacchetto [istia.st.spring3tier.domain]
L'interfaccia 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
Una prima classe di implementazione 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
- La classe ha un campo privato che è un riferimento al singleton di tipo [IDao], il quale fornisce l'accesso al livello [Dao]. Questo campo verrà inizializzato da Spring (iniezione di dipendenze) al momento della creazione dell'oggetto.
- Il metodo [doSomethingInDomainLayer] incrementa i propri parametri e li passa al metodo [doSomethingInDaoLayer] del singleton [dao]
Una seconda classe di implementazione, 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
- La classe ha un campo privato che è un riferimento al singleton [IDao], il quale fornisce l'accesso al livello [Dao]. Questo campo verrà inizializzato da Spring (iniezione di dipendenze) al momento della creazione dell'oggetto.
- Il metodo [doSomethingInDomainLayer] decrementa i propri parametri e li passa al metodo [doSomethingInDaoLayer] del singleton [dao]
7.4. Il pacchetto [istia.st.spring3tier.control]
L'interfaccia 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
Una prima classe di implementazione 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
- La classe ha un campo privato che è un riferimento al singleton di tipo [IDomain], il quale fornisce l'accesso al livello [Domain]. Questo campo verrà inizializzato da Spring (iniezione di dipendenze) al momento della creazione dell'oggetto.
- Il metodo [doSomethingInControlLayer] incrementa i propri parametri e li passa al metodo [doSomethingInDomainLayer] del singleton [domain]
Una seconda classe di implementazione, 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
- La classe ha un campo privato che è un riferimento al singleton di tipo [IDomain], che fornisce l'accesso al livello [Domain]. Questo campo verrà inizializzato da Spring (iniezione di dipendenze) al momento della creazione dell'oggetto.
- Il metodo [doSomethingInControlLayer] decrementa i propri parametri e li passa al metodo [doSomethingInDomainLayer] del singleton [domain].
7.5. I file di configurazione [Spring]
Il file [spring-config-3tier-1.xml] utilizza la versione 1 delle implementazioni:
<?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>
Il file [spring-config-3tier-2.xml] utilizza la versione 2 delle implementazioni:
<?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. Il pacchetto di test [istia.st.spring3tier.tests]
Un test 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
L'esecuzione di questo test produce i seguenti risultati:

I lettori che visualizzano una versione a colori di questo documento noteranno che i risultati sono "verdi", a indicare che il test ha avuto esito positivo.
7.7. Un altro tipo di file di configurazione Spring
Un'applicazione VB.NET può essere configurata utilizzando un file denominato [App.config]. Questo file si trova nella directory principale del progetto Visual Studio:

Spring può utilizzare questo file di configurazione. Si consideri il seguente file [App.config], ispirato agli esempi della documentazione [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>
Nota: le informazioni riportate di seguito sono fornite con riserva. Non sono sicuro di aver compreso correttamente il significato di tutti gli elementi presenti nel file di configurazione sopra riportato.
La sintassi XML del file [App.config] richiede che segua questo formato:
La gestione delle varie sezioni di [App.config] può essere delegata a programmi esterni. Questo è ciò che viene fatto qui nella sezione [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>
Il codice sopra riportato significa che la sezione denominata [spring/context] deve essere gestita dalla classe [Spring.Context.Support.ContextHandler] presente nell'assembly [Spring.Core.dll], e che la sezione denominata [spring/objects] deve essere gestita dalla classe [Spring.Context.Support.DefaultSectionHandler], anch'essa presente nell'assembly [Spring.Core.dll].
La sezione [spring/context] è la seguente:
<spring>
<context type="Spring.Context.Support.XmlApplicationContext, Spring.Core">
<resource uri="config://spring/objects" />
</context>
...
</spring>
Ciò sembra indicare che la sezione [spring/objects] del file di configurazione debba essere gestita dalla classe [Spring.Context.Support.XmlApplicationHandler], che si trova nell'assembly [Spring.Core.dll]. Questa classe deve utilizzare una risorsa XML situata in [config://spring/objects], ovvero la sezione [spring/objects] del file di configurazione corrente.
Nella sezione [spring/objects] troviamo la sintassi Spring che ormai conosciamo bene.
Nella sezione <objects>... </objects> del file [App.config], abbiamo definito due possibili configurazioni per la nostra applicazione a 3 livelli:
- una che utilizza la versione 1 delle implementazioni dell'interfaccia
- un'altra che utilizza la versione 2 di quelle stesse implementazioni
Ora, come si utilizza il file [App.config]?
Il codice seguente mostra un'applicazione console che utilizza il precedente file [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
Commenti:
- Negli esempi NUnit che abbiamo utilizzato finora, abbiamo usato un oggetto [XmlObjectFactory] per ottenere i singleton di cui avevamo bisogno. Qui, l'oggetto utilizzato è di tipo [IApplicationContext], un'interfaccia Spring. Viene ottenuto da [App.config] utilizzando la classe [ConfigurationSettings], una classe tradizionalmente utilizzata in .NET per elaborare i file di configurazione.
- Richiediamo l'handler per la sezione [spring/context]. Se consultiamo il file [App.config], vediamo che questo handler è di tipo [XmlApplicationContext] ed è responsabile della gestione della sezione [spring/objects] di [App.config].
- Una volta recuperato l'oggetto [IApplicationContext], viene utilizzato proprio come l'oggetto [XmlObjectFactory] che abbiamo utilizzato fino ad ora.
Il programma precedente si chiama [MainTestSpring3tier.vb] e si trova nel pacchetto [tests]:

Il progetto [spring3tier] è configurato in modo tale che [MainTestSpring3tier]:

L'esecuzione del progetto produce i seguenti risultati:
7.8. Conclusione
Il framework Spring offre una vera flessibilità sia nell'architettura che nella configurazione delle applicazioni. Abbiamo utilizzato il concetto di IoC, uno dei due pilastri di Spring. L'altro pilastro è l'AOP (Aspect-Oriented Programming), che non abbiamo trattato. Esso consente di aggiungere un "comportamento" a un metodo di classe tramite configurazione senza modificare il codice del metodo. In termini semplici, l'AOP consente di filtrare le chiamate a determinati metodi:
![]() |
- il filtro può essere eseguito prima o dopo il metodo di destinazione M, oppure in entrambi i casi.
- Il metodo M non è a conoscenza di questi filtri. Essi sono definiti nel file di configurazione di Spring.
- Il codice del metodo M non viene modificato. I filtri sono classi Java che devono essere implementate. Spring fornisce filtri predefiniti, in particolare per la gestione delle transazioni DBMS.
- I filtri sono bean e, in quanto tali, sono definiti nel file di configurazione di Spring come bean.
Un filtro comune è il filtro delle transazioni. Si consideri un metodo M del livello business che esegue due operazioni inseparabili sui dati (un'unità di lavoro). Esso chiama due metodi del livello DAO, M1 e M2, per eseguire queste due operazioni.
![]() |
Poiché si trova nel livello business, il metodo M astrae l'archiviazione dei dati sottostante. Non deve, ad esempio, presumere che i dati si trovino in un DBMS e che sia necessario inserire le due chiamate ai metodi M1 e M2 all'interno di una transazione DBMS. Spetta al livello DAO gestire questi dettagli. Una soluzione al problema precedente consiste nel creare un metodo nel livello DAO che chiami a sua volta i metodi M1 e M2, racchiudendo queste chiamate all'interno di una transazione DBMS.
![]() |
La soluzione di filtraggio AOP è più flessibile. Consente di definire un filtro che, prima di chiamare M, avvierà una transazione e, dopo la chiamata, eseguirà un commit o un rollback a seconda dei casi.
![]() |
Questo approccio presenta diversi vantaggi:
- una volta definito il filtro, può essere applicato a più metodi, ad esempio a tutti quelli che richiedono una transazione
- non è necessario riscrivere i metodi filtrati
- poiché i filtri da utilizzare sono definiti dalla configurazione, possono essere modificati
Per ulteriori informazioni: http://www.springframework.net.





