7. Una soluzione
7.1. Il progetto Visual Studio
![]() | ![]() |
Note:
- i file necessari per 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] si trovano le diverse classi dell’applicazione
Il progetto è stato configurato per generare DLL e [spring3tier.dll] nella cartella [bin]:

7.2. Il pacchetto [istia.st.spring3tier.dao]
L'interfaccia IDao:
Namespace istia.st.spring3tier.dao
Public Interface IDao
' fare qualcosa nel livello [dao]
Function doSomethingInDaoLayer(ByVal a As Integer, ByVal b As Integer) As Integer
End Interface
End Namespace
Un primo esempio di implementazione:
Namespace istia.st.spring3tier.dao
Public Class DaoImpl1
Implements istia.st.spring3tier.dao.IDao
' fare qualcosa nel livello [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
' fare qualcosa nel livello [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 dei suoi parametri come richiesto.
7.3. Il pacchetto [istia.st.spring3tier.domain]
L'interfaccia IDomain:
Namespace istia.st.spring3tier.domain
Public Interface IDomain
' fare qualcosa nel livello [domain]
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
' campi privati
Private _dao As IDao
' proprietà associata
Public WriteOnly Property dao() As IDao
Set(ByVal Value As IDao)
_dao = Value
End Set
End Property
' costruttore predefinito
Public Sub New()
End Sub
' fare qualcosa nel livello [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
- La classe dispone di un campo privato che è un riferimento al singleton di tipo [IDao], il quale consente 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
' campi privati
Private _dao As IDao
' proprietà associata
Public WriteOnly Property dao() As IDao
Set(ByVal Value As IDao)
_dao = Value
End Set
End Property
' costruttore predefinito
Public Sub New()
End Sub
' fare qualcosa nel livello [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
- la classe ha un campo privato che è un riferimento al singleton di tipo [IDao], il quale consente 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
' fare qualcosa nel livello [control]
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
' campi privati
Private _domain As IDomain
' proprietà associata
Public WriteOnly Property domain() As IDomain
Set(ByVal Value As IDomain)
_domain = Value
End Set
End Property
' costruttore predefinito
Public Sub New()
End Sub
' fare qualcosa nel livello [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
- la classe ha un campo privato che è un riferimento al singleton di tipo [IDomain], il quale consente 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
' campi privati
Private _domain As IDomain
' proprietà associata
Public WriteOnly Property domain() As IDomain
Set(ByVal Value As IDomain)
_domain = Value
End Set
End Property
' costruttore predefinito
Public Sub New()
End Sub
' fare qualcosa nel livello [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
- La classe dispone di un campo privato che è un riferimento al singleton di tipo [IDomain], il quale consente 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 le versioni 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>
<!-- la classe DAO -->
<object id="dao" type="istia.st.spring3tier.dao.DaoImpl1, spring3tier"></object>
<!-- la classe domain -->
<object id="domain" type="istia.st.spring3tier.domain.DomainImpl1, spring3tier">
<property name="dao">
<ref object="dao" />
</property>
</object>
<!-- la classe di controllo -->
<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 le versioni 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>
<!-- la classe DAO -->
<object id="dao" type="istia.st.spring3tier.dao.DaoImpl2, spring3tier"></object>
<!-- la classe domain -->
<object id="domain" type="istia.st.spring3tier.domain.DomainImpl2, spring3tier">
<property name="dao">
<ref object="dao" />
</property>
</object>
<!-- la classe di controllo -->
<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
' le fabbriche di singleton
Private factory1 As XmlObjectFactory
Private factory2 As XmlObjectFactory
<SetUp()> _
Public Sub init()
' si creano i singleton factory
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()
' si distruggono i singleton
factory1.Dispose()
factory2.Dispose()
' si liberano i singleton factory
factory1 = Nothing
factory2 = Nothing
End Sub
<Test()> _
Public Sub test()
'si recupera un'implementazione dell'interfaccia IControl
Dim control1 As IControl = CType(factory1.GetObject("control"), IControl)
' si utilizza la classe
Dim a1 As Integer = 10, b1 As Integer = 20
Dim res1 As Integer = control1.doSomethingInControlLayer(a1, b1)
Assert.AreEqual(34, res1)
' si recupera un'altra implementazione dell'interfaccia IControl
Dim control2 As IControl = CType(factory2.GetObject("control"), IControl)
' si utilizza la classe
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 fornisce i seguenti risultati:

Chi visualizza questa pagina in una versione a colori noterà che i risultati sono "verdi", a indicare che il test è stato superato.
7.7. Un altro tipo di file di configurazione di Spring
Un'applicazione VB.net può essere configurata utilizzando un file denominato [App.config]. Questo file viene collocato nella directory principale del progetto Visual Studio:

Spring può sfruttare questo file di configurazione. Consideriamo il seguente file [App.config], ispirato agli esempi presenti nella documentazione di [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>
<!-- una prima configurazione -->
<!-- la classe DAO -->
<object id="dao1" type="istia.st.spring3tier.dao.DaoImpl1, spring3tier"></object>
<!-- la classe domain -->
<object id="domain1" type="istia.st.spring3tier.domain.DomainImpl1, spring3tier">
<property name="dao">
<ref object="dao1" />
</property>
</object>
<!-- la classe control -->
<object id="control1" type="istia.st.spring3tier.control.ControlImpl1, spring3tier">
<property name="domain">
<ref object="domain1" />
</property>
</object>
<!-- una seconda configurazione -->
<!-- la classe DAO -->
<object id="dao2" type="istia.st.spring3tier.dao.DaoImpl2, spring3tier"></object>
<!-- la classe domain -->
<object id="domain2" type="istia.st.spring3tier.domain.DomainImpl2, spring3tier">
<property name="dao">
<ref object="dao2" />
</property>
</object>
<!-- la classe di controllo -->
<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 da intendersi con riserva. Non sono sicuro di aver compreso correttamente il significato di tutti gli elementi del file di configurazione sopra riportato.
La sintassi XML del file [App.config] richiede che rispetti la sintassi:
La gestione delle diverse sezioni di [App.config] può essere delegata a programmi esterni. È 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 indica che la sezione denominata [spring/context] deve essere gestita dalla classe [Spring.Context.Support.ContextHandler], chesi trova nell'assieme [Spring.Core.dll] e che la sezione denominata [spring/objects] deve essere gestita dalla classe [Spring.Context.Support.DefaultSectionHandler], che si trova anch'essa nell'assieme [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>
Sembra indicare che la sezione [spring/objects] del file di configurazione debba essere gestita dalla classe [Spring.Context.Support.XmlApplicationHandler], che si trova nell'assieme [Spring.Core.dll]. Questa classe deve utilizzare una risorsa XML il cui percorso è [ config://spring/objects], c.a.d. la sezione [spring/objects] del file di configurazione corrente.
Nella sezione [spring/objects] ritroviamo la sintassi Spring a cui ormai siamo abituati.
Nella sezione <objects>... </objects> del file [App.config] abbiamo definito due possibili configurazioni per la nostra applicazione a tre livelli:
- una che utilizza la versione 1 delle implementazioni delle interfacce
- un'altra che utilizza la versione 2 di queste stesse implementazioni
Ora, come si utilizza il file [App.config]?
Il codice seguente mostra un’applicazione da console che utilizza il file [App.config] precedente:
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()
' il contesto Spring che ci consentirà di recuperare i singleton
Dim contexte As IApplicationContext = CType(ConfigurationSettings.GetConfig("spring/context"), IApplicationContext)
' recuperiamo una prima implementazione dell'interfaccia IControl
Dim control1 As IControl = CType(contexte.GetObject("control1"), IControl)
' si utilizza la classe
Dim a1 As Integer = 10, b1 As Integer = 20
Console.WriteLine("res1({0},{1})={2}", a1, b1, control1.doSomethingInControlLayer(a1, b1))
' si recupera un'altra implementazione dell'interfaccia IControl
Dim control2 As IControl = CType(contexte.GetObject("control2"), IControl)
' si utilizza la classe
Dim a2 As Integer = 10, b2 As Integer = 20
Console.WriteLine("res2({0},{1})={2}", a2, b2, control2.doSomethingInControlLayer(a2, b2))
' pausa
Console.WriteLine("Tapez [entrée] pour continuer...")
Console.ReadLine()
End Sub
End Module
End Namespace
Note:
- negli esempi NUnit che abbiamo utilizzato finora, utilizzavamo un oggetto [XmlObjectFactory] per ottenere i singleton di cui avevamo bisogno. In questo caso l'oggetto utilizzato è di tipo [IApplicationContext], un'interfaccia di Spring. Si ottiene da [App.config] grazie alla classe [ConfigurationSettings], classe tradizionalmente utilizzata in .NET per gestire i file di configurazione.
- Si richiede il gestore della sezione [spring/context]. Se si consulta il file [App.config], si scopre che è di tipo [XmlApplicationContext] e che ha il compito di gestire la sezione [spring/objects] di [App.config].
- Una volta recuperato l’oggetto di tipo [IApplicationContext], lo si utilizza come l’oggetto [XmlObjectFactory] che avevamo utilizzato fino a quel momento.
Il programma precedente si chiama [MainTestSpring3tier.vb] e si trova nel pacchetto [tests]:

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

L'esecuzione del progetto produce i seguenti risultati:
7.8. Conclusion
Il framework Spring offre una reale flessibilità sia nell'architettura delle applicazioni che nella loro configurazione. Abbiamo utilizzato il concetto IoC, uno dei due pilastri di Spring. L’altro pilastro è AOP (Aspect Oriented Programming), che non abbiamo presentato. Consente di aggiungere, tramite configurazione, un “comportamento” a un metodo di classe senza modificarne il codice. Schematicamente, AOP permette di filtrare le chiamate a determinati metodi:
![]() |
- il filtro può essere eseguito prima o dopo il metodo M di destinazione, oppure in entrambi i casi.
- Il metodo M ignora l’esistenza di questi filtri. Questi ultimi sono definiti nel file di configurazione di Spring.
- Il codice del metodo M non viene modificato. I filtri sono classi Java da implementare. Spring fornisce filtri predefiniti, in particolare per gestire le transazioni di SGBD.
- I filtri sono bean e, in quanto tali, sono definiti nel file di configurazione di Spring come bean.
Un filtro comune è il filtro transazionale. Prendiamo un metodo M del livello business che esegue due operazioni indissociabili sui dati (unità di lavoro). Esso richiama due metodi M1 e M2 del livello DAO per eseguire queste due operazioni.
![]() |
Poiché si trova nel livello business, il metodo M non tiene conto del supporto di questi dati. Ad esempio, non deve di ipotizzare che i dati si trovino in un SGBD e che sia necessario inserire le due chiamate ai metodi M1 e M2 all’interno di una transazione di SGBD. Spetta al livello DAO occuparsi di questi dettagli. Una soluzione al problema precedente consiste quindi nel creare un metodo nel livello DAO che a sua volta chiami i metodi M1 e M2, chiamate che includerebbe in una transazione di SGBD.
![]() |
La soluzione di filtraggio AOP è più flessibile. Consentirà di definire un filtro che, prima della chiamata a 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
- i metodi così filtrati non devono essere riscritti
- poiché i filtri da utilizzare sono definiti tramite configurazione, è possibile modificarli
Per ulteriori informazioni: http://www.springframework.net.





