2. Configurazione di un'applicazione con Spring
Consideriamo una classica applicazione a 3 livelli:
![]() |
Supponiamo che l'accesso al livello DAO sia controllato da un'interfaccia [IArticlesDao]:
....
Namespace istia.st.articles.dao
Public Interface IArticlesDao
' list of all items
Function getAllArticles() As IList
' add an article
Function ajouteArticle(ByVal unArticle As Article) As Integer
' deletes an article
Function supprimeArticle(ByVal idArticle As Integer) As Integer
' modify an article
Function modifieArticle(ByVal unArticle As Article) As Integer
' search for an article
Function getArticleById(ByVal idArticle As Integer) As Article
' deletes all articles
Sub clearAllArticles()
' inserts items within a transaction
Sub doInsertionsInTransaction(ByVal articles As Article())
' changes the stock of an item
Function changerStockArticle(ByVal idArticle As Integer, ByVal mouvement As Integer) As Integer
End Interface
End Namespace
End Namespace
Nel livello di accesso ai dati o livello DAO (Data Access Object), è comune lavorare con un DBMS. Si consideri il caso in cui l'accesso avvenga tramite un driver ODBC. Lo scheletro di una classe che accede a questa sorgente ODBC potrebbe essere il seguente:
NameSpace istia.st.articles.dao
Imports System.Data.Odbc
...
Public Class ArticlesDaoPlainODBC
Implements istia.st.articles.dao.IArticlesDao
' private fields
Private connexion As OdbcConnection = Nothing
Private DSN As String
Public Sub New(ByVal DSN As String, ByVal user As String, ByVal passwd As String)
'retrieve the source name ODBC
Me.DSN = DSN
' we create the connection string
Dim connectString As String = String.Format("DSN={0};UID={1};PWD={2}", DSN, user, passwd)
'we instantiate the connection
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 avverrà lo scambio di dati tra il database e l'applicazione. Per creare questo oggetto, sono necessarie tre informazioni:
il nome della sorgente ODBC | |
il nome utente utilizzato per l'accesso | |
la password associata a questa identità |
La nostra classe [ArticlesDaoPlainODBC] ottiene queste informazioni tramite l'agente esterno che istanzia un'istanza della classe. Ci si potrebbe chiedere come l'agente ottenga le tre informazioni necessarie per istanziare la classe [ArticlesDaoPlainODBC]. Consideriamo un esempio. Supponiamo di voler scrivere una classe di test per il livello [Dao]. Avremmo la seguente architettura:
![]() |
Lo scheletro di una classe di test NUnit [http://www.nunit.org/] potrebbe presentarsi così:
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
' the test object
Private articlesDao As IArticlesDao
<SetUp()> _
Public Sub init()
' create an instance of the object to be tested
articlesDao = New ArticlesDaoPlainODBC("odbc-firebird-articles", "SYSDBA", "masterkey")
End Sub
<Test()> _
Public Sub testGetAllArticles()
' visual check
listArticles()
End Sub
' screen listing
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
Il framework di test NUnit è un porting sulla piattaforma .NET del framework 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 eseguito dopo ogni test. Nell'esempio sopra non ce ne sono. Qui vediamo che il metodo [init], che ha l'attributo <SetUp()>, istanzia un oggetto [ArticlesDaoPlainODBC] hard-codificando le tre informazioni richieste dal costruttore dell'oggetto.
La nostra classe di test è vulnerabile alle modifiche di uno qualsiasi dei valori hard-coded. Sarebbe preferibile memorizzarli in un file di configurazione per evitare ricompilazioni non necessarie quando cambiano. L'approccio standard alla configurazione di un'applicazione consiste nell'utilizzare un file contenente tutte le informazioni che potrebbero cambiare nel tempo. Esiste un'ampia varietà di file di configurazione disponibili. La tendenza attuale è quella di utilizzare file XML. Questa è l'opzione scelta da Spring. Il file che configura un oggetto [ArticlesDaoPlainODBC] potrebbe apparire così:
<?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 IArticlesDao interface implementation class -->
<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 Spring descrive gli oggetti da istanziare. Generalmente non specifica quando saranno istanziati. Il momento della loro istanziazione è quindi determinato dal codice che utilizza questo file. Gli oggetti descritti in un file di configurazione Spring possono essere istanziati e inizializzati in due modi diversi:
- specificando, come mostrato sopra, i parametri da passare al costruttore dell'oggetto
- fornendo valori per le proprietà dell'oggetto. In questo caso, l'oggetto deve disporre di un costruttore predefinito che Spring utilizzerà per l'istanziazione.
Gli oggetti in un'applicazione il cui ruolo è quello di fornire un servizio vengono spesso creati come singola istanza. Questi sono chiamati singleton. Pertanto, nel nostro esempio di applicazione multilivello presentato all'inizio di questo documento, l'accesso al database dei prodotti sarà gestito da una singola istanza della classe [ArticlesDaoPlainODBC]. In un'applicazione web, questi oggetti di servizio servono più client contemporaneamente. Non creiamo un oggetto di servizio per ogni client.
Il file di configurazione Spring sopra riportato consente di creare un singolo oggetto di servizio di tipo [ArticlesDaoPlainODBC] in un pacchetto denominato [istia.st.articles.dao]. Le tre informazioni richieste dal costruttore di questo oggetto sono definite all'interno di un tag <object>...</object>. Ci saranno tanti tag <object> quanti sono i singleton da creare.
Analizziamo la configurazione:
<objects> è il tag radice di un file di configurazione Spring. Dichiara la descrizione degli oggetti singleton da istanziare.
Il tag <description> è facoltativo. Può essere utilizzato, ad esempio, per descrivere lo scopo del file di configurazione.
<object id="articlesdao" type="istia.st.articles.dao.ArticlesDaoPlainODBC, articlesdao">
...
</object>
Il tag <object> viene utilizzato per descrivere un oggetto da istanziare. In questo caso presenta due attributi:
- name: l'identificatore dell'oggetto. Il codice esterno farà riferimento all'oggetto utilizzando questo nome.
- class: nella forma "nome classe, nome assembly". La prima parte è il nome completo della classe da istanziare. La seconda parte è il nome della DLL contenente questa classe. Nel nostro esempio, la classe si trova in un file denominato [articlesdao.dll]
Il contenuto del tag <object> viene utilizzato per descrivere come viene istanziato l'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>
Rivediamo la firma del costruttore per la classe [ArticlesDaoPlainODBC]:
L'oggetto Spring [articlesdao] verrà istanziato dal costruttore sopra riportato utilizzando le tre informazioni presenti nel file di configurazione: [odbc-firebird-articles, SYSDBA, masterkey].
Quando verranno creati gli oggetti definiti nel file Spring? Ogni applicazione ha un metodo che viene eseguito per primo. Di solito è in questo metodo che viene richiesta la creazione dei singleton. L'inizializzazione dell'applicazione può essere gestita dal metodo main dell'applicazione, se presente. Per un'applicazione ASP.NET, questo potrebbe essere il metodo [**Application\_Start**] nel file [**global.asax**]. Per la nostra classe di test [Nunit], l'inizializzazione dell'applicazione avviene nel metodo associato all'attributo <Setup().
Come si utilizza 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
' the test object
Private articlesDao As IArticlesDao
<SetUp()> _
Public Sub init()
' retrieve an instance of the Spring object manufacturer
Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config-plainodbc.xml", FileMode.Open))
' request instantiation of the articles dao object
articlesDao = CType(factory.GetObject("articlesdao"), IArticlesDao)
End Sub
<Test()> _
Public Sub testGetAllArticles()
' visual check
listArticles()
End Sub
' screen listing
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
Commenti:
- Per istanziare oggetti dal file di configurazione Spring, utilizziamo un oggetto di tipo [XmlObjectFactory]. Si tratta di un oggetto "Factory", ovvero un oggetto utilizzato per creare altri oggetti (Factory = fabbrica). Spring fornisce diversi tipi di "Factory" a seconda del file di configurazione utilizzato. In questo caso, il file è un file XML, quindi utilizziamo un tipo [XmlObjectFactory].
- Logicamente, un oggetto [XmlObjectFactory] richiede il 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 dal file XML il cui nome è stato fornito.
- Una volta creato l'oggetto [XmlObjectFactory], si ottiene un oggetto dal file di configurazione tramite [XmlObjectFactory].getObject("identifier"), dove "identifier" è l'attributo [id] di uno degli oggetti nel file di configurazione.
- Se l'oggetto richiesto non è stato ancora istanziato, Spring lo istanzia utilizzando le informazioni contenute nel suo file di configurazione e restituisce un riferimento ad esso al programma chiamante. Se l'oggetto è già stato istanziato, Spring restituisce semplicemente il riferimento all'oggetto esistente. Questo è il principio del singleton.
- Si noti che la classe di test [Nunit] non conosce il nome della classe di accesso ai dati. Questo nome si trova nel file di configurazione. La classe di test richiede semplicemente un oggetto che implementi l'interfaccia [IArticlesDao]:
' the test object
Private articlesDao As IArticlesDao
<SetUp()> _
Public Sub init()
...
articlesDao = CType(factory.GetObject("articlesdao"), IArticlesDao)
End Sub
Questo è il punto centrale di Spring. Se cambiamo la classe di implementazione, non sarà necessario modificare la nostra classe di test. Modificheremo semplicemente il file di configurazione di Spring. La classe di test, dal canto suo, funziona semplicemente con un'interfaccia anziché con una classe.
Concludiamo questa presentazione con alcuni punti pratici.
Quando scriviamo "Spring istanzierà...", cosa intendiamo esattamente? Per la piattaforma .NET, Spring è contenuto in tre file:

Per un progetto .NET creato con Visual Studio che deve utilizzare Spring, procedere come segue:
- I tre file sopra indicati andranno inseriti nella cartella [bin] del progetto
- [Spring.Core.dll] deve essere incluso nei riferimenti del progetto:

- e le classi che utilizzano Spring devono importare determinati spazi dei nomi, che spesso includono i seguenti:
Un altro aspetto pratico: dove si colloca il file di configurazione di Spring? Esistono diverse posizioni possibili. Una di queste è la cartella [bin] del progetto. È lì che è stato collocato il file [spring-config-plainodbc.xml] dell'esempio.

