Java User Group Marche http://www.jugancona.org – [email protected] Spring tutorial: Spring core di Andrea Del Bene Introduzione. In questa parte del tutorial inizieremo a “sporcarci” le mani con un pò di codice basato su spring. Vedremo in particolare come sono strutturati i file xml usati da spring per configurare il contesto. Va detto subito che l'xml non è l'unico modo per configurare dei bean all'interno di un contesto Spring, ma è sicuramente la tecnica più utilizzata e meno invasiva. Metodi alternativi esistono solo da qualche mese e sarebbe prematuro introdurli ora. Dizionario e richiami. Per familiarizzare con Spring occorre avere chiari alcuni concetti chiave della programmazione orientata agli oggetti. Di seguito richiameremo quei pochi che ci occorrono per continuare la lettura del tutorial. • Classe: la classe è un'entità che modella un concetto reale trattato dalla nostra applicazione. Es:una persona, un ordine acquisto, un conto corrente, ecc... La classe rappresenta un sottoinsieme delle caratteristiche del concetto reale corrispondente, in modo da fornire un modello semplice ed affidabile allo stesso tempo. Es: se si vuole creare una classe Persona da usare in un'anagrafica clienti difficilmente si terrà traccia del loro numero di scarpe, a meno che il programma non gestisca un negozio di calzature :). In Java la classe corrisponde proprio al costrutto class che usiamo per definire le classi del nostro dominio. • Attributi: gli attributi sono le caratteristiche proprie di una classe, ossia le informazioni che abbiamo deciso di modellare nella nostra classe rispetto il suo corrispettivo reale. Es: se di una persona mi interessano nome, cognome e data di nascita, la mia classe persona avrò proprio tre attributi nome, cognome, dataDiNascita (nei nomi attributi non ci sono spazi ovviamente). • Metodi: sono funzioni interne alla classe che possono operare sugli attributi della classe o su altri dati forniti dall'esterno. I metodi rappresentano anche le operazioni che la classe espone all'esterno e si parla in questo caso di metodi public(pubblici). Per avvicinarsi a Spring è necessario sapere cosa sono i metodi getter e setter di una classe. I primi restituiscono il valore di un attributo di una classe, i secondi impostano il valore di una un attributo. Es: per ottenere e impostare il nome di una Persona la classe esporrà i metodi getNome() e setNome(String nome) entrambi pubblici. Per convenzione i getter e setter hanno come nome rispettivamente get<NomeCampo> e <setNomeCampo>. Andrea Del Bene – Spring core 1 / 12 Java User Group Marche http://www.jugancona.org – [email protected] • Istanza: l'istanza di una classe è un'entità non generica e dotata di una sua ben precisa entità. Questa entità fa parte dei dati prodotti dall'applicazione software. Es: se ho una classe Persona che tiene traccia del nome e del cognome di una persona reale, le istanze di Persona saranno zone di memoria distinte dove vengono memorizzate coppie di attributi nome e cognome: Mario Rossi, Giuseppe Brambilla, Sandro De Pretis Dove eravamo rimasti... Nella seguente figura si cerca di riassumere il rapporto esistente tra le nostre applicazioni e Spring: Classe1 Classe2 3 <beans> ... </beans> ......... ClasseN 3 3 2 Spring 1 4 Applicazione generica La nostra applicazione invece che creare istanze delle classi (Classe1,Classe2,...ClasseN), le chiederà a Spring che farà da “intermediario” ( ossia container) compiendo i seguenti passi: 1) il notro programma crea una nuova istanza di classe contesto Spring indicandole il file xml da caricare per costruire i nostri bean. 2) Il contesto carica il file xml contente le istruzioni su quali istanze caricare in esso 3) In base al file xml letto il contesto crea una sorta di indice delle istanze che può restituire (istanze di Classe1, Classe2,...ClasseN e così via). 4) la nostra applicazione chiede al contesto una delle istanze configurate. Andrea Del Bene – Spring core 2 / 12 Java User Group Marche http://www.jugancona.org – [email protected] Visto che il miglior modo per imparare è "toccare con mano" faremo subito dei test di caricamento del contesto da file xml. Spring per nostra fortuna ci fornisce delle classi speciali di test unit basate su JUnit, in modo che possiamo usare questo potente framework di test ed Eclipse per eseguire le nostre prove. Progetto di test. Assieme a questo tutorial si può scaricare un progetto da importare in Eclipse con il quale eseguire le prove illustrate. Il progetto è distribuito come semplice file zip. Per importarlo dal menù file scegliere “Import...”: Immagine 1: Comando Import dal menù file Successivamente ci verrà richiesto che tipo di risorsa importare. Bisogna scegliere “Existing Projects into Workspace”: Immagine 2: Scegliere la risorsa da importare Andrea Del Bene – Spring core 3 / 12 Java User Group Marche http://www.jugancona.org – [email protected] Immagine 3: La finestra finale di importazione Ora come ultimo passo bisogna indicare al wizzard il file zip da importare (SpringTutorial.zip) e scegliere il progetto da importare (progetto SpringTutorial): Nota: se esiste già un progetto di nome SpringTutorial nel workspace non sarà possibile importarlo! Il plugin SpringIDE Per lavorare con i file xml e Spring e pressoché indispensabile installare su Eclipse il plugin SpringIDE. Questo mini-ide specifico per Spring è un sottoprogetto di Spring stesso ed è curato sempre da membri dell' Interface21, l'azienda madre di Spring. L'installazione segue la procedura standard di installazione dei plugin Eclipse. Se non si conosce tale procedura si consulti il tutorial su Eclipse dalla slide 12 o il sito di SpringIDE. L'url da inserire per l'update site di springIDE è http://springide.org/updatesite/. L'uso del plugin verrà discusso in seguito. Andrea Del Bene – Spring core 4 / 12 Java User Group Marche http://www.jugancona.org – [email protected] File di context. Una volta importato il progetto apriamo il file jug4tendaContext.xml che rappresenta il file di contesto che passeremo a spring per fare i nostri test con Spring. Immagine 4: Il file di contesto jug4tendaContext.xml In questo tutorial il file di contesto sarà molto semplice: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="persona" class="org.jugancona.jug4tenda.domain.Persona"> </bean> </beans> Come possiamo vedere il tag radice del file è <beans>. Come si intuisce dal nome all'interno di esso vi saranno le dichiarazioni dei bean che vogliamo inserire nel contesto. La dichiarazione di un bean è contenuta nel tag <bean>. Il punto di partenza della dichiarazione di un bean è l'attributo class del tag bean. Con questo attributo indichiamo di quale classe sarà istanza il bean che stiamo dichiarando. Andrea Del Bene – Spring core 5 / 12 Java User Group Marche http://www.jugancona.org – [email protected] Es: in sostanza scrivere class="org.jug4tenda.domain.Persona" equivale a scrivere l'istruzione new Persona(); Un altro attributo usatissimo nel tag <bean> è id. id è una stringa che rappresenterà l'identificativo che useremo per recuperare tramite Spring il bean che stiamo costruendo. Il recupero del bean viene fatto il metodo getBean(idBean) esposto dal contesto di Spring. Es: getBean(“persona”) ci farà recuperare l'istanza della classe Persona configurata nel file xml ed identificata dalla stringa “persona”. Da notare che getBean(...) restituisce un tipo generico Object, e occorrerà quindi fare un cast al tipo voluto per utilizzare l'istanza ottenuta. Es: (Persona)getBean(“persona”) La classe di test. Ora passiamo al codice della classe da test fornita con il progetto. Si trova sotto il package org.jugancona.jug4tenda.tes e il codice è il seguente: Immagine 5: Codice della classe di test La classe estende AbstractDependencyInjectionSpringContextTests appartenente a Spring e presente nel package org.springframework.test. E' una classe di utilità che ci permette di fare test unitari caricando il contesto Spring. Si noti il metodo getConfigLocation() dove specifichiamo la posizione del file xml di configurazione da caricare (in realtà possiamo indicare più di un file xml...). Il secondo dei due metodi presente nella classe di test è testSameInstance(). E' un metodo di test con un nome arbitrario fatta eccezione per il suffisso test che fa in modo che il framework JUnit lo eseguirà quando lanceremo il test. Andrea Del Bene – Spring core 6 / 12 Java User Group Marche http://www.jugancona.org – [email protected] Osservando le poche righe del metodo di test dobbiamo a questo punto familiarizzare con un concetto fondamentale in spring: le istanze dei bean ottenute da Spring (la chiamata getBean(“...”)) sono create per default una ed una sola volta (pattern singleton). Il concetto è semplice ma chiariamolo con un esempio: se nel mio programma richiedo a Spring un bean che avevo già richiesto in passato (stesso id passato a getBean(“...”)), spring mi restituisce sempre la stessa istanza creata la prima volta, ossia non riesegue un new sul bean. L'esempio appena descritto è riscritto esattamente nel codice di testSameInstance() : Immagine 6: Codice di testSameInstance La nostra classe di test eredita dalla classe madre l'oggetto applicationContex che rappresenta il contesto caricato da xml dalla classe al momento della sua costruzione. Come si può vedere la variabile p di tipo Persona è valorizzata con l'istanza vuota configurata in spring della classe Persona. Tramite p impostiamo la proprietà nome poi impostiamo p a null perdendo il riferimento all'istanza. Per dimostrare che Spring tratta le istanze configurate come singleton riotteniamo il bean “persona” dal contesto e dichiariamo che la sua proprietà nome ha lo stesso valore di prima. Andrea Del Bene – Spring core 7 / 12 Java User Group Marche http://www.jugancona.org – [email protected] Eseguire la classe di test. Ora vediamo come eseguire la classe di test all'interno di Eclipse. Basta fare click con il tasto destro e scegliere “Junit Test” dal menù “Run As” come mostrato in figura: Immagine 7: Esecuzione della classe di test Il lancio di una classe di test JUnit mostra automaticamente la vista relativa di Eclipse che ci accoglie con la riga verde, segno che il test è andato a buon fine: Immagine 8: Il nostro test ha avuto successo Andrea Del Bene – Spring core 8 / 12 Java User Group Marche http://www.jugancona.org – [email protected] Le proprietà dei <bean> Per concludere la nostra intrudizione a Spring, vedremo come valorizzare direttamente nel file xml le proprietà delle istanze in esso configutrate. Iniziamo con la valorizzazione delle proprietà base (numeri, stringhe e date). Per questo gruppo di tipi primitivi si segue la seguente sintassi: <beans> ... <bean id="idBean" class="classeBean"> ... <property name="propertyName">aValue</property> ... </bean> ... </beans> Si tratta in sostanza di usare il tag <property> innestato dentro <bean>. Questo tag presenta l'attributo name che identifica la proprietà della classe che stiamo valorizzando. Si può usare anche la forma contratta: ... <property name="cognome" value="rossi" /> ... L'ultimo aspetto da esplorare ora è la valorizzazione di proprietà non primitive, ovvero proprietà di tipo arbitrario. La valorizzazione di queste proprietà non può essere fatta come appena visto poichè i loro valori sono oggetti complessi, non esprimibili tramite caratteri alfanumerici. prendiamo come esempio il package domain del nostro progetto Jug4Tenda. Vogliamo configurare nel nostro contesto un'istanza di classe Indirizzo. Nel nostro dominio un Indirizzo deve essere sempre associato ad una Persona (solitamente un Ospite) e non puà esistere senza un riferimento ad essa: In questi casi (comunissimi!) la proprietà è valorizzata referenziando un altro bean configurato nel file xml tramite la seguente sintassi (in grassetto): <beans> ... <bean id="persona" class="org.jugancona.jug4tenda.domain.Persona"> ... <bean id="indirizzo" class="org.jugancona.jug4tenda.domain.Indirizzo"> ... <property name="ospite" ref="persona"/> </bean> </beans> In sostanza si usa l'attributo ref, valorizzato con l'id bean desiderato. Andrea Del Bene – Spring core 9 / 12 Java User Group Marche http://www.jugancona.org – [email protected] Una pausa di riflessione: IoC e DI Come abbiamo appena visto attraverso il file di configurazione oltre a specificare delle istanze di classe, possiamo anche indicare (con il costrutto ref="...") la relazione che esiste fra di esse e come vanno "legate" (wired in inglese) l'una con l'altra. Ci penserà poi Spring a costruire queste istanze tenendo conto delle dipendenze indicate. Nel nostro caso Spring si occuperà di costruire l'istanza indirizzo legandola a quella di persona. Tutto ciò è un semplice esempio di Inversio Of Control (abbreviato come IoC). Nel nostro caso concreto IoC significa che non sarà l'istanza di Indirizzo a doversi preoccupare di soddisfare la sua dipendenza da un'istanza di Persona, ma sarà Spring ad occuparsi di farlo. L'IoC è molto efficace perchè ci evita di scrivere tutto quel codice che si usa per "saldare" un'istanza ad un'altra. Es: Class1 classeUtile = new Class1(); Class2 classeDipendeDaClass1 = new Class2(); ... //”saldo” classeUtile dentro classeDipendeDaClass1 classeDipendeDaClass1.setClass2(classeUtile); Questa impostazione dei "legami" (delle dipendenze...) tra le due classi con Spring viene fatta in maniera dinamica in base a quanto scritto nel file di contesto. Non esiste più quella "saldatura" statica che lega a tempo di compilazione le istanze. Questo aspetto che forse inizialmente sembra poca cosa, dimostra tutta la sua potenza quando le dipendneze di una classe sono rivolte ad una interfaccia e noi possiamo cambiarne l'implementazione semplicemente agendo sul file xml di configurazione. Altro vantaggio di questo approccio è che incentiva la progettazione del software in moduli indipendenti e intercambiabili (una sorta di plugin per intenderci...) più facilmente testabili e riutilizzabili. A questo punto possiamo affrontare serenamente un'altra sigla ricorrente nell'universo Spring, la Dependency Injection (DI). In realtà di DI ne abbiamo parlato sino ad ora dicendo che nel modello IoC di Spring è il container di Spring che di occupa di soddisfare le dipenze (DI appunto...) delle istanze "legandole" insieme. La DI non è altro che la dipendenza tra due entità impostata nel file di configurazione xml. Fino ad ora abbiamo usato una DI basata sui metodi setter (set<ClasseDaCuiDipendo>). Dei setter si è parlato nel corso del primo capitolo introduttivo di questo tutorial. E'utile ricordare che la DI oltre che con i setter si può fare anche con i costruttori di classe qualora la nostra classe debba necessariamente configurarsi al momento della sua costruzione. Andrea Del Bene – Spring core 10 / 12 Java User Group Marche http://www.jugancona.org – [email protected] In futuro vedremo degli esempi di questo costrutto. Per ulteriori informazioni sulle verie modalità tramite cui effettuare la DI si rimanda alla documentazione ufficiale di Spring. Il introduzione al plugin SpringIDE NOTA: Assicuriamoci di aprire i file xml con l'editor xml di Eclipse come mostrato in figura e non con qualche altro editor di qualche altro plugin(ad esempio Amateras), altrimenti non si potrenno usare le funzioni descritte di seguito! Immagine 9: Apriamo il file xml con l'editor opportuno. Andrea Del Bene – Spring core 11 / 12 Java User Group Marche http://www.jugancona.org – [email protected] La funzione più interessante offerta dal plugin è l'autocompletamento nell'attributo class del tag <bean> e dell'attributo del tag <property>. Con SpringIDE è possible attribuire una classe ad un istanza usufruendo del completamento automatico (ctrl + spazio) del nome package e del nome classe, risparmiandoci tempo e tanti errori frustranti. La stessa cosa vale per i nomi delle proprietà di classe che vogliamo valorizzare. Immagine 10: Proposta delle proprietà di Indirizzo I nomi ci vengono proposti in automatico. Anche i tag <bean> o <property> vengono proposti in una lista sensibile al contesto, che ci propone solo le alternative sintatticamente corrette. Vi rimando ancora una volta al wiki del plugin dove vengono mostrati screenshot di quanto appena descritto SpringIDE effettua anche la validazione del nostro file xml ogni volta che lo salviamo, per scoprire in anticipo eventuali errori presenti in esso. Per ulteriori informazioni ed approfondimenti su questo utilissimo strumento vi rimando all'indice del wiki, molto completo ed esaustivo. Andrea Del Bene – Spring core 12 / 12