2. Configurare un’applicazione con Spring
Consideriamo una classica applicazione a tre livelli:
![]() |
Supponiamo che l’accesso al livello DAO sia controllato da un’interfaccia [IArticlesDao]:
....
Namespace istia.st.articles.dao
Public Interface IArticlesDao
' elenco di tutti gli articoli
Function getAllArticles() As IList
' aggiunge un articolo
Function ajouteArticle(ByVal unArticle As Article) As Integer
' elimina un articolo
Function supprimeArticle(ByVal idArticle As Integer) As Integer
' modifica un articolo
Function modifieArticle(ByVal unArticle As Article) As Integer
' cerca un articolo
Function getArticleById(ByVal idArticle As Integer) As Article
' elimina tutti gli articoli
Sub clearAllArticles()
' inserisce articoli all’interno di una transazione
Sub doInsertionsInTransaction(ByVal articles As Article())
' modifica le giacenze di un articolo
Function changerStockArticle(ByVal idArticle As Integer, ByVal mouvement As Integer) As Integer
End Interface
End Namespace
Nel livello di accesso ai dati, o livello DAO (Data Access Object), è frequente lavorare con un SGBD. Consideriamo il caso in cui si acceda a quest’ultimo tramite un driver ODBC. Lo scheletro di una classe che accede a questa fonte ODBC potrebbe essere il seguente:
NameSpace istia.st.articles.dao
Imports System.Data.Odbc
...
Public Class ArticlesDaoPlainODBC
Implements istia.st.articles.dao.IArticlesDao
' campi privati
Private connexion As OdbcConnection = Nothing
Private DSN As String
Public Sub New(ByVal DSN As String, ByVal user As String, ByVal passwd As String)
'si recupera il nome della fonte ODBC
Me.DSN = DSN
' si crea la stringa di connessione
Dim connectString As String = String.Format("DSN={0};UID={1};PWD={2}", DSN, user, passwd)
'si istanzia la connessione
connexion = New OdbcConnection(connectString)
End Sub
....
End Class
End NameSpace
Per eseguire un'operazione sulla sorgente ODBC, ogni metodo richiede un oggetto [OdbcConnection] che rappresenta la connessione al database attraverso la quale transiteranno gli scambi tra quest'ultimo e l'applicazione. Per creare questo oggetto, sono necessarie tre informazioni:
il nome della fonte ODBC | |
l'identità con cui si crea la connessione | |
la password associata a tale identità |
La nostra classe [ArticlesDaoPlainODBC] ottiene queste informazioni tramite l'agente esterno che istanzia un membro della classe. Ci si potrebbe chiedere come quest'ultimo ottenga le tre informazioni necessarie per l'istanziazione della classe [ArticlesDaoPlainODBC]. Facciamo un esempio. Supponiamo di voler scrivere una classe di test del livello [Dao]. Si avrebbe la seguente architettura:
![]() |
Lo scheletro di una classe di test Nunit [http://www.nunit.org/] potrebbe essere il seguente:
Imports System
Imports System.Collections
Imports NUnit.Framework
Imports istia.st.articles.dao
Imports ArticlesDaoSqlmap = istia.st.articles.dao.ArticlesDaoSqlMap
Imports Article = istia.st.articles.domain.Article
Imports System.Threading
<TestFixture()> _
Public Class NunitTestArticlesDaoPlainOdbc
' l'oggetto da testare
Private articlesDao As IArticlesDao
<SetUp()> _
Public Sub init()
' si crea un'istanza dell'oggetto da testare
articlesDao = New ArticlesDaoPlainODBC("odbc-firebird-articles", "SYSDBA", "masterkey")
End Sub
<Test()> _
Public Sub testGetAllArticles()
' verifica visiva
listArticles()
End Sub
' listato dello schermo
Private Sub listArticles()
Dim articles As IList = articlesDao.getAllArticles
For i As Integer = 0 To articles.Count - 1
Console.WriteLine(CType(articles(i), Article).ToString)
Next
End Sub
End Class
L'ambiente di test Nunit è un porting sulla piattaforma .NET dell'ambiente JUnit esistente per la piattaforma Java. Nella classe sopra riportata, il metodo con l'attributo <SetUp()> viene eseguito prima di ogni metodo di test. Quello con l'attributo <TearDown()> viene invece eseguito dopo ogni test. Nell'esempio sopra riportato non ce ne sono. Qui si vede che il metodo [init], che ha l’attributo <SetUp()>, istanzia un oggetto [ArticlesDaoPlainODBC] passando in modo “hardcoded” le tre informazioni necessarie al costruttore di tale oggetto.
La nostra classe di test è vulnerabile a qualsiasi modifica di una delle informazioni codificate in modo "hard-coded". Sarebbe preferibile che queste fossero inserite in un file di configurazione, al fine di evitare inutili ricompilazioni quando cambiano. La soluzione abituale per configurare un'applicazione è l'utilizzo di un file in cui si trovano tutte le informazioni suscettibili di cambiare nel tempo. Esiste un’ampia varietà di file di configurazione. La tendenza attuale è quella di utilizzare file XML. È l’opzione scelta da Spring. Il file che configura un oggetto [ArticlesDaoPlainODBC] potrebbe essere il seguente:
<?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 di implementazione dell'interfaccia IArticlesDao -->
<description>Gestion d'une table d'articles</description>
<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>
</objects>
Il file di configurazione di Spring descrive gli oggetti da istanziare. In genere, non specifica quando questi verranno istanziati. Il momento della loro istanziazione viene quindi deciso dal codice che utilizza tale file. Gli oggetti descritti in un file di configurazione Spring possono essere istanziati e inizializzati in due modi diversi:
- indicando, come sopra, i parametri da passare al costruttore dell’oggetto
- fornendo valori alle proprietà dell’oggetto (Property). In questo caso, l’oggetto deve disporre di un costruttore predefinito che Spring utilizzerà per l’istanziazione.
Gli oggetti che, in un’applicazione, hanno il ruolo di fornire un servizio vengono spesso creati in un unico esemplare. Si chiamano singleton. Pertanto, nel nostro esempio di applicazione multi-tier presentato all’inizio di questo documento, l’accesso al database degli articoli sarà garantito da un unico esemplare della classe [ArticlesDaoPlainODBC]. In un’applicazione web, questi oggetti di servizio servono più clienti contemporaneamente. Non si crea un oggetto di servizio per ogni cliente.
Il file di configurazione Spring sopra riportato consente di creare un unico oggetto di servizio di tipo [ArticlesDaoPlainODBC] in un pacchetto denominato [istia.st.articles.dao]. Le tre informazioni necessarie al costruttore di questo oggetto sono definite all’interno di un tag <object>...</object>. Ci saranno tanti tag <object> quanti sono i singleton da costruire.
Analizziamo la configurazione in dettaglio:
<objects> è il tag radice di un file di configurazione Spring. Indica la descrizione degli oggetti singleton da istanziare.
Il tag <description> è facoltativo. Può essere utilizzato, ad esempio, per descrivere la funzione del file di configurazione.
<object id="articlesdao" type="istia.st.articles.dao.ArticlesDaoPlainODBC, articlesdao">
...
</object>
Il tag <object> serve a descrivere un oggetto da istanziare. In questo caso presenta due attributi:
- name: identificatore dell’oggetto. È tramite questo nome che il codice esterno farà riferimento all’oggetto.
- class: nella forma "nome della classe, nome dell'assembly". La prima informazione è il nome completo della classe da istanziare. La seconda è il nome dell'assembly DLL che contiene tale classe. Nel nostro esempio, la classe si trova in un file denominato [articlesdao.dll]
Il contenuto del tag <object> serve a descrivere la modalità di istanziazione dell’oggetto:
<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>
Ricordiamo la firma del costruttore della classe [ArticlesDaoPlainODBC]:
L'oggetto Spring [articlesdao] verrà istanziato dal costruttore sopra indicato con le tre informazioni contenute nel file di configurazione: [odbc-firebird-articles, SYSDBA, masterkey].
Quando avverrà la creazione degli oggetti definiti nel file Spring? In ogni applicazione è presente un metodo che viene eseguito per primo. È generalmente in questo metodo che viene richiesta la creazione dei singleton. L'inizializzazione di un'applicazione può essere affidata al metodo main della stessa applicazione, se ne possiede uno. Per un'applicazione ASP.NET, potrebbe trattarsi del metodo [Application_Start] del file [global.asax]. Per la nostra classe di test [Nunit], l'inizializzazione dell'applicazione avviene nel metodo associato all'attributo <Setup()>.
Come utilizzare il file di configurazione sopra riportato nella nostra classe [Nunit]? Ecco un esempio:
Imports System
Imports System.Collections
Imports NUnit.Framework
Imports istia.st.articles.dao
Imports ArticlesDaoSqlmap = istia.st.articles.dao.ArticlesDaoSqlMap
Imports Article = istia.st.articles.domain.Article
Imports System.Threading
Imports Spring.Objects.Factory.Xml
Imports System.IO
<TestFixture()> _
Public Class NunitSpringTestArticlesDaoPlainOdbc
' l'oggetto da testare
Private articlesDao As IArticlesDao
<SetUp()> _
Public Sub init()
' si recupera un'istanza del generatore di oggetti Spring
Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config-plainodbc.xml", FileMode.Open))
' si richiede l'istanziazione dell'oggetto DAO "articoli"
articlesDao = CType(factory.GetObject("articlesdao"), IArticlesDao)
End Sub
<Test()> _
Public Sub testGetAllArticles()
' verifica visiva
listArticles()
End Sub
' schermata
Private Sub listArticles()
Dim articles As IList = articlesDao.getAllArticles
For i As Integer = 0 To articles.Count - 1
Console.WriteLine(CType(articles(i), Article).ToString)
Next
End Sub
End Class
Note:
- per istanziare gli oggetti del file di configurazione di Spring, si utilizza un oggetto di tipo [XmlObjectFactory]. Si tratta di un oggetto di tipo "Factory", c.a.d, ovvero un oggetto che serve a creare altri oggetti (Factory = fabbrica, stabilimento). Spring dispone di diversi tipi di "Factory" a seconda del file di configurazione utilizzato. In questo caso, si tratta di un file XML e quindi si utilizza un tipo [XmlObjectFactory].
- In modo abbastanza logico, un oggetto di tipo [XmlObjectFactory] necessita del nome del file di configurazione XML, in questo caso [spring-config-plainodbc.xml]. Più precisamente, il tipo [XmlObjectFactory] viene istanziato con un flusso di lettura creato a partire dal file XML, di cui si specifica il nome.
- Una volta creato l’oggetto di tipo [XmlObjectFactory], si ottiene un oggetto dal file di configurazione tramite [XmlObjectFactory].getObject("identificativo") dove "identificativo" è l'attributo [id] di uno degli oggetti del file di configurazione.
- Se l’oggetto richiesto non è stato ancora istanziato, Spring lo istanzia utilizzando le informazioni presenti nel suo file di configurazione e restituisce un riferimento ad esso al programma chiamante. Se l’oggetto è già stato istanziato, Spring si limita a restituire il riferimento all’oggetto già esistente. Questo è il principio del singleton.
- Si noti che la classe di test [Nunit] non conosce il nome della classe di accesso ai dati. Tale nome si trova nel file di configurazione. La classe di test si limita a richiedere un oggetto che implementi l’interfaccia [IArticlesDao]:
' l'oggetto da testare
Private articlesDao As IArticlesDao
<SetUp()> _
Public Sub init()
...
articlesDao = CType(factory.GetObject("articlesdao"), IArticlesDao)
End Sub
È proprio questo il vantaggio di Spring. Se si cambia la classe di implementazione, non sarà necessario modificare la nostra classe di test. Basterà semplicemente modificare il file di configurazione di Spring. La classe di test, dal canto suo, si limita a lavorare con un'interfaccia e non con una classe.
Concludiamo questa presentazione con alcuni spunti pratici.
Quando si scrive «Spring istanzierà...», cosa si intende esattamente? Per la piattaforma .NET, Spring è contenuto in tre file:

Per un progetto .NET creato con Visual Studio e che deve utilizzare Spring, si procederà come segue:
- i tre file sopra indicati andranno inseriti nella cartella [bin] del progetto
- [Spring.Core.dll] deve essere incluso tra i riferimenti del progetto:

- le classi che utilizzano Spring dovranno importare alcuni spazi dei nomi, tra cui spesso il seguente:
Un altro aspetto pratico: dove si colloca il file di configurazione di Spring? Ci sono diverse posizioni possibili. Una di queste è la cartella [bin] del progetto. È lì che è stato collocato il file [spring-config-plainodbc.xml] dell’esempio esaminato.

