Scuola Politecnica e delle Scienze di Base Corso di Laurea in Ingegneria Informatica Elaborato finale in Programmazione II Il framework Spring Anno Accademico 2015-2016 Relatore: Ch.mo prof. Marcello Cinque Candidato: Pasquale Tremante matr. N46001295 Indice 1 Introduzione 2 2 Cenni introduttivi 2.1 Introduzione a Java 2.2 Cos'è un Framework? 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 3 Spring Framework 4 3.1 Descrizione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 3.2 Proprietà e Vantaggi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 3.3 3.4 3.5 Moduli . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 3.3.1 Core Container . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 3.3.2 Data Access/Integration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 3.3.3 Web . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 3.3.4 AOP, Aspect and Instrumentation 3.3.5 Test . . . . . . . . . . . . . . . . . . . . . . . 6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 Inversion of Control e Dependency Injection . . . . . . . . . . . . . . . . . . . . . . 7 Spring Beans 9 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.5.1 Constructor Dependency Injection . . . . . . . . . . . . . . . . . . . . . . . 9 3.5.2 Setter Dependency Injection . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 3.5.3 Richiamo dei beans . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 3.6 Utilizzo delle Annotations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 3.7 Sviluppo di un applicazione con Spring . . . . . . . . . . . . . . . . . . . . . . . . . 15 4 Integrazione di Enterprise JavaBeans 19 5 Spring MVC 20 5.1 Descrizione delle componenti 5.2 Esempio di un applicazione con Spring MVC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 22 6 Conclusioni 26 7 Bibliograa 27 1 1 Introduzione Negli ultimi anni il continuo evolversi delle tecnologie per lo sviluppo di software, ha portato privati e aziende a richiedere applicazioni sempre più complesse e strutturate, solitamente dotate di un interfaccia graca per gestire al meglio i propri dati. Questo richiede al programmatore la capacità di doversi adattare alle diverse problematiche che si incontrano durante lo sviluppo di applicazioni; da qui nasce l'esigenza di lavorare su piattaforme che agevolino il compito del programmatore. In un primo momento, venne molto apprezzata la tecnologia JavaBeans, ancora in uso, che prevedeva l`utilizzo di semplici classi java (beans) con regole determinate di nomenclatura per la loro introspezione (proprietà accessibili mediante metodi pressati da get e set); tuttavia anche questa col tempo ha cominciato a mostrare i suoi limiti (di cui discuteremo nell'elaborato), motivo per cui si è continuata la ricerca di tecnologie sempre più adeguate. Ed è qui che entra in scena Spring, un framework nato per supportare i programmatori nello sviluppo di software per componenti e integrabile con altre tecnologie(EJB compresa). Nel seguente elaborato viene riportata una panoramica di questo framework mostrandone la struttura, i punti di forza e qualche esempio che mostra come realizzare un'applicazione sia standalone che Web. 2 2 Cenni introduttivi 2.1 Introduzione a Java Java è un linguaggio di programmazione orientato agli oggetti progettato per essere indipendente dalla piattaforma sica di esecuzione; tale progetto venne iniziato da James Gosling ed altri ingegneri presso la Sun MicroSystems e ucializzato nel 1995. I programmi scritti con questo linguaggio vengono compilati ed eseguiti su una piattaforma software(piattaforma Java) composta da due blocchi compositivi: 1. Java Virtual Machine(JVM): provvede all'esecuzione vera e proprio del programma. 2. API Java: una collezione di componenti software (librerie) già scritti e pronti all'uso. Il codice sorgente Java viene dapprima compilato con un compilatore java ( il codice compilato (detto bytecode) javac), il quale genera che verrà poi passato alla JVM, la quale dispone di un interprete che le consente di tradurre il bytecode in linguaggio macchina per poi eseguirlo. Java si divide essenzialmente in tre piattaforme dierenti, utili a seconda dell'uso e del tipo di progetto da sviluppare: 1. Java 2 Standard Edition (J2SE): E' la piattaforma più utilizzata, contiene il Java Developer Kit che serve per compilare i codici sorgenti Java. 2. Java 2 Enterprise Edition (J2EE): Una piattaforma di sviluppo interamente dedicata a soluzioni Client - Server, utilizza un Application Server che memorizza gli oggetti, i database ed i componenti nel server, che saranno poi utilizzati dallo sviluppatore Java Client: alcuni esempi sono le JSP, le Servlet gli EJB. 3. Java 2 Micro Edition (J2ME): La piattaforma di sviluppo per i dispositivi portabili, come cellulari o palmari. 2.2 Cos'è un Framework? Un framework è un'architettura logica di supporto (spesso un'implementazione logica di un particolare design pattern) su cui un software può essere progettato e realizzato, spesso facilitandone lo sviluppo da parte del programmatore. La sua funzione è quella di creare una infrastruttura generale, lasciando al programmatore il contenuto vero e proprio dell'applicazione. Lo scopo di un framework è infatti quello di risparmiare allo sviluppatore la riscrittura di codice già scritto in precedenza per compiti simili. Esso è denito da un insieme di classi astratte e dalle relazioni tra esse. Istanziare un framework signica fornire un'implementazione di tali classi. Nella pratica un framework viene messo a disposizione sotto forma di archivio contenente le classi software che lo sviluppatore può utilizzare e gestire. Ovviamente questo non coincide con il semplice mettere a disposizione delle librerie di classi in quanto quando si utilizza un framework bisogna attenersi alle speciche denite dal framework stesso e lasciargli il usso di controllo. I framework possono essere creati per diversi obiettivi: gestione della persistenza, realizzazione di applicazioni Web, O/R mapping, etc... 3 3 Spring Framework 3.1 Descrizione Spring è un framework open source per lo sviluppo di applicazioni Java, in particolare per applicazioni J2EE. La prima versione del framework fu rilasciata nel 2002 da Rod Johnson, subendo poi molti cambiamenti che hanno portato questo framework ad essere molto apprezzato dalla comunità internazionale ed a vincere nel 2006 un Jolt productivity award e un JAX Innovation Award. Spring è molto apprezzato nella comunità Java grazie degli innumerevoli vantaggi che esso ore, come la modulartà, l'integrabilità, la portabilità...di cui tratteremo a breve; per di più tale framework rappresenta anche una valida alternativa a Enterprise JavaBean(EJB), in quanto lascia una maggiore libertà al programmatore mettendo a disposizione un'ampia gamma di soluzioni ben documentate, semplici e adatte alle problematiche più comuni. Tuttavia, nonostante Spring metta a disposizione strumenti per realizzare qualsiasi tipo di applicazioni Java, il settore in cui più viene utilizzato è quello delle applicazioni web-based (in particolare quelle che si basano sul modello MVC) costruite sul modello della piattaforma Java EE. Questo ha permesso a Spring di essere riconosciuto anche da importanti vendor commerciali quale framework di importanza strategica. 3.2 Proprietà e Vantaggi Il framework Spring gode delle seguenti proprietà: 1. Modulare: Pur essendo molto ampio, grazie alla sua modularità si può scegliere di integrare solo alcuni dei suoi moduli all'interno del nostro progetto. Per questo Spring è facilmente integrabile anche in progetti già esistenti. 2. Lightweight: Spring è stato progettato in modo da rendere le dipendenze dal framework all'interno della nostra business logic praticamente nulle, e ove presenti facilmente isolabili. Per instaziare eøinizializzare oggetti nel framework non si devono implementare interfacce speciche o classi astratte. 3. Integrabile: In Spring non ci sono package di logging, connection pool o O/R mapping, poichè tutte queste funzionalità sono fornite da altri framework open source. Spring non ha l'obbiettivo di sostituirli, ma solo di renderli più facili da utilizzare, attraverso i suoi tool. 4. Portabile: Un'applicazione scritta con Spring può essere trasferita senza problemi da un Application Server all'altro. 5. POJO-based programming enabled: Spring ci permette di sviluppare una completa applicazione J2EE usanso solo POJO, dotandoli di funzionalità enterprise in modo non invasivo. 6. Test driven enabled: Spring ci permette di scrivere software facile da testare, quindi risulta un framework ideale per i software che sono scritti basandosi sul Test Driven Development. 4 Inoltre ore anche i seguenti vantaggi: • Elimina la proliferazione di custom properties le. Per capire quali sono le dipendenze di una classe, basta solo guardarne il costruttore o le sue property. Questo è possibile grazie all'uso dell' Inversion of Control e del Dependency Injection; • Ci aiutia a risolvere i problemi comuni delle applicazioni enterprise, rendendo opzionale l'uso degli EJB. In questo modo possiamo scegliere di implemenrare le interfacce di business nei nostri POJOs o nei nostri EJB senza modicare il codice chiamante; • Ore un consistente framework per il Data Access, sia usando JDBC, sia usando altri prodotti di O/R mapping, come Hibernate o TopLink. 3.3 Moduli Come accennato nel precedente paragrafo, Spring è un framework modulare. Le sue funzionalità sono distribuite all'interno di circa 20 moduli, alcuni dei quali sono raggruppati all'interno di Container, come in gura seguente: Figura 1: Moduli Spring 3.3.1 Core Container Core e Beans costituiscono la parte fondamentale del framework, includendo le funzionalità di Inversion of Control e Dependency Injection che sono implementate tramite la BeanFactory I moduli che è una sosticata implementazione del Factory Method Pattern. Esso consente il disaccoppiamento tra denizione e congurazione delle dipendenze e della logica applicativa. Il modulo Context costituisce il mezzo di accesso agli oggetti attraverso la sua interfaccia Applica- tionContext. L'ApplicationContext eredita tutti le funzionalità della BeanFactory, aggiungendone altre quali: supporto per l'internazionalizzazione, propagazione di eventi, caricamento di risorse, il supporto per J2EE. 5 Il modulo Expression Language fornisce una potente espressione di linguaggio per la ricerca e la manipolazione degli oggetti a runtime. Tale linguaggio supporta le operazioni di modica e restituzione dei valori(setvalue e getvalue),operazioni di assegnamento, invocazione di metodi, utilizzo di operatori logici e aritmetici, denizione di variabili, l'elencazione di una lista e l'aggregazione tra più liste. 3.3.2 Data Access/Integration Il modulo JDBC fornisce un'astrazione delle API JBDC, eliminando la necessità di codicare e analizzare il codice dei dierenti database-vendor. Il modulo ORM fornisce un livello di integrazione con le API di Object-Relational(O/R) mapping come JPA, IDO, Hibernate e iRatis. Usando il package ORM è possibile combinare le potenzialità di questi framework con le peculiarità oerte da Spring, come la gestione delle transazioni. Il modulo OXM fornisce un livello di astrazione che supporta le implementazione del mapping Object/XML per JAXB, Castor, XMLBeans, JiBX and XStream. Il modulo JMS contiene le funzionalità per la produzione e consumazione dei messaggi. 3.3.3 Web Il modulo Web contiene le funzionalità di base per creare applicationi enterprise, come ad esempio il supporto per l'upload dei le, orendo anche l'inizializzazione del container IoC attraverso un Servlet Listener. Il modulo Servlet contiene l'implementazione del Model-View-Controller(MVC) di Spring per le applicazioni web. Il framework MVC di Spring provvede a una netta separazione del dominio del codice del Model da quello della View ; per di più si integra con tutte le altre caratteristiche di Spring Framework. Il modulo Portlet fornisce funzionalità identiche al modulo Web-Servlet, ma orientato ad ap- plicazioni che fanno uso di portlet. Il modulo Struts contiene classi per l'integrazione con Struts2, che però è sconsiglita nella versione 3 di Spring. 3.3.4 AOP, Aspect and Instrumentation Il modulo AOP fornisce un'implementazione aderente alla AOP Alliance, permettendo una pro- grammazione aspect-oriented. Questo layer consente di creare codice che deve essere disaccoppiato dalle altre funzionalità, anche se legato al usso generale dell'applicativo. Questo layer ha anche un modulo Aspects per l'integrazione con AspectJ, ed un modulo Instrumention che consente il monitoraggio delle classi. 3.3.5 Test Il modulo Test ore la possibiltà di testare i componenti di Spring, usando JUnit o TestNG. 6 3.4 Inversion of Control e Dependency Injection Nella programmazione orientata agli oggetti uno degli obiettivi principali che ci si impone è quello di realizzare applicazioni che siano quanto più possibili modulari e fare sicchè tra i vari moduli che costituiscono un'applicazione vi sia un basso grado di accoppiamento (cioè siano quanto più possibile indipendenti gli uni dagli altri); in tal modo infatti si garantisce la riusabilità e la testabilità dei vari moduli. Tuttavia quando si realizzano applicazioni molto complesse, è inevitabile che si instaurino delle dipendenze tra vari i vari oggetti (si pensi all'ereditarietà e alla composizione tra le classi). Il framework Spring perciò propone una strategia per eliminare queste dipendenze tra classi, ovvero l'Inversione del Controllo, e questa funzionalità rappresenta un' altro dei suoi punti forti. Inversione del Controllo L' (in inglese Inversion of Control, abbreviato in IoC) è un pattern per cui un componente di livello applicativo riceve il controllo da un componente appartenente a un libreria riusabile; in parole povere, sposta la denizione delle dipendenze tra gli oggetti, dal codice (scritto dallo sviluppatore) al framework. Spring realizza l'IoC tramite la tecnica della De- pendency Injection che di base consiste nell'avere un oggetto separato(un le .xml) che si occupi della risoluzione delle dipendenze e della loro inizializzazione. Normalmente, senza l'utilizzo di questa tecnica, se un oggetto necessita di accedere ad un particolare servizio, l'oggetto stesso si prende la responsabilità di gestirlo, o avendo un diretto riferimento al servizio, o individuandolo con un Service Locator che gli restituisce un riferimento ad una specica implementazione del servizio. Con l'utilizzo della dependency injection, l'oggetto ha in sé solamente una proprietà che può ospitare un riferimento a quel servizio e, quando l'oggetto viene istanziato, un riferimento ad una implementazione di questo servizio gli viene iniettata dal framework esterno, senza che il programmatore che crea l'oggetto sappia nulla sul posizionamento del servizio o altri dettagli dello stesso. Vediamo un esempio di dipendenza: Agenda.java 1 package agenda ; 2 3 4 import java . util . ArrayList ; import utente . Utente ; 5 6 7 8 public class Agenda implements IAgenda { private ArrayList < Utente > users ; 9 10 11 public Agenda () { users = new ArrayList < >();} 12 13 14 15 16 17 public void printAll () { for ( int i =0; i < users . size (); i ++) users . get (i ). print (); } 18 19 20 public ArrayList < Utente > getAgenda () { return users ;} 7 21 public void setAgenda ( ArrayList < Utente > agenda ) { this . users = agenda ;} 22 23 24 public void addUser ( String nome , String cognome , int eta ) { users . add ( new Utente ( nome , cognome , eta ));} 25 26 27 } Inne la classe di servizio Gestore istanzia al suo interno un vettore di Utenti e ne invoca il servizio esposto Gestore.java 1 2 3 public class Gestore { private Agenda agenda ; 4 public Gestore () { // Configurazione e risoluzione dipendenze agenda = new Agenda (); } 5 6 7 8 9 10 public void addUser ( String nome , String cognome , int eta ) { agenda . addUser ( nome , cognome , eta );} 11 12 13 public void printUsersMaggiorenni () { ArrayList < Utente > lista = agenda . getAgenda (); for ( int i =0; i < lista . size (); i ++) if ( lista . get (i ). getEta () >=18) System . out . println ( lista . get (i ). getNome ()); } 14 15 16 17 18 19 20 21 } Utilizzando invece la tecnica del Dependency Injection la dipendenza dall'oggetto Agenda viene risolta all'interno del framework Spring, evitando di risolvere la stessa nel costruttore della classe Gestore. Ci sono come vedremo 3 tipi di Dependency Injection: 1. Constructor Injection; 2. Setter Injection; 3. Interface Injection. Spring tuttavia implementa solo le prime due. 8 3.5 Spring Beans In questa sezione vedremo come congurare un IoC Container attraverso i metadati xml. Per istanziare un bean ci occorrono due informazioni: id per identicare il bean(generalmente una stringa); • un • Qualied Name della classe a cui appartiene l'oggetto. ad esempio: 1 2 < bean id =" utente1 " class =" com . Utente " > </ bean > E' possibile poi includere un terzo parametro cioè la visibilità del bean(scope) che ne stabilisce il ciclo di vita; Spring ammette 5 tipi di scope e sono: • singleton : viene creata una sola istanza a livello di container; • prototype : una nuova istanza è creata ogni volta che è richiesta; • request : viene creata una sola istanza per le richieste HTTP; • session : viene creata una sola istanza per le sessioni HTTP; • globalsession : viene creata una sola istanza per l'intera sessione HTTP; Se l'attributo non è specicato viene settato singleton di default. ad esempio: 1 2 < bean id =" utente1 " class =" com . Utente " scope =" singleton " > </ bean > I beans possono essere istanziati attraverso 3 metodi diversi: • Attraverso il costruttore; • Attraverso un factory method; • Attraverso un factory bean. Se non è specicato un metodo, Spring di default utilizza un costruttore per istanziare i bean, quindi ogni bean deve avere il costruttore di default. Vediamo ora come realizzare l'IoC attraverso la Setter Dependency Injection e la Constructor Setter Dependency. 3.5.1 Constructor Dependency Injection In questo caso modo le dipendenze tra due oggetti vengono risolte attraverso il costruttore del bean che vuole utilizzare un certo servizio di un'altro bean. Per chiarire riportiamo un esempio. Supponiamo di avere la seguente classe: 1 2 3 4 public class BeanA { private beanName ; private BeanB beanB ; 5 9 public BeanA ( String beanName , BeanB beanB ) { this . beanName = beanName ; this . beanB = beanB ; } 6 7 8 9 10 11 // ... business logic 12 13 14 } La classe BeanA ha una dipendenza da quella BeanB. Vediamo come congurarla nel le .xml: 1 2 3 // Istanziamo un oggetto BeanB < bean id =" bean_B " class =" com . BeanB " scope =" singleton " > </ bean > 4 5 6 7 8 < bean id =" bean_A " class =" com . BeanA " scope =" prototype " > < constructor - arg value = " nome_del_bean " index ="0"/ > < constructor - arg ref =" bean_B " index ="1 "/ > </ bean > Quindi per congurare la dipendenza tramite costruttore si utilizza il tag constructor-arg insieme agli attributi: • value • ref per passare valori predeniti come stringhe e numeri per passare come argomento un altro bean gia istanziato nell'IoC Container. L'attributo index serve per specicare l'ordine con cui passare i parametri al costruttore. Non è necessario quando i tipi degli argomenti da passare sono tutti diversi tra loro. 3.5.2 Setter Dependency Injection La Constuctor Dependency Injection non è adatta però per risolvere dipendenze cicliche tra due oggetti(A dipende da B e viceversa), compito che invece puo essere svolto dalla Setter Dependency Injection. Tale tecnica permette di iniettare le dipendenze dopo che l'oggetto è stato istanziato; è solo necessario denire i metodi Getter e Setter all'interno della classe che ha la dipendenza. Cioè riprendendo l'esempio del paragrafo precedente(BeanA che dipende da BeanB) avremo: 1 2 3 4 public class BeanA { private beanName ; private BeanB beanB ; 5 6 7 8 9 10 public BeanA ( String beanName , BeanB beanB ) { this . beanName = beanName ; this . beanB = beanB ; } 11 12 13 public void setBeanB ( BeanB beanB ) { this . beanB = beanB ;} 14 10 public BeanB getBeanB () { return this . beanB ;} 15 16 17 // ... business logic 18 19 20 } e la dipendenza sarà risolta così: 1 2 3 // Istanziamo un oggetto BeanB < bean id =" bean_B " class =" com . BeanB " scope =" singleton " > </ bean > 4 5 6 7 < bean id =" bean_A " class =" com . BeanA " scope =" prototype " > < property name =" beanB " ref = " bean_B " / > </ bean > E' anche possibile iniettare degli oggetti con dei valori statici e rappresentazione in string. Ad esempio se abbiamo la seguente classe: 1 2 3 4 5 public class Utente { private String nome ; private String cognome ; private int eta ; 6 public Utente ( String nome , String cognome , int eta ) { this . nome = nome ; this . cognome = cognome ; this . eta = eta ; } 7 8 9 10 11 12 13 // ... e relativi metodi Setters e Getters per ogni attributo 14 15 } Allora è possibile istanziare un bean Utente nel seguente modo: 1 2 3 4 5 < bean id =" utente1 " class =" com . Utente " > < property name =" nome " value =" Pasquale " / > < property name =" cognome " value =" Tremante "/ > < property name =" eta " value =25/ > </ bean > 11 3.5.3 Richiamo dei beans Supponiamo di aver istanziato un beanA e un beanB all'interno di un le 'beans.xml', vediamo ora come richiamarli all'interno di un programma principale: 1 2 3 4 5 6 7 8 9 10 public class Main { public static void main ( String [] args ) { // Istanzio IoC Container ApplicationContext context = new ClassPathXmlApplicationContext (" beans . xml " ); // Recupero del beanB BeanB beanB =( BeanB ) context . getBean (" bean_B " ); // Recupero del beanA BeanA beanA =( BeanA ) context . getBean (" bean_A " ); 11 // ... utilizzo dei beans 12 13 } 14 15 } L'ApplicationContext è un container più avanzato rispetto alla BeanFactory. Estende la BeanFactory e fornisce altre funzionalità. Ci sono diverse implementazioni dell'ApplicationContext che si possono utilizzare tra le quali: • ClassPathXmlApplicationContext : permette di caricare l'ApplicationContext da un le xml presente nel classpath dell'applicazione; • FileSystemXmlApplicationContext : permette di caricare l'ApplicationContext da un le xml presente nel le system (occorre specicare un percorso assoluto) 12 3.6 Utilizzo delle Annotations Finora abbiamo visto come congurare le dipendenze tra i bean attraverso metadati in formato xml, tuttavia Spring ore anche la possibilità di farlo tramite le Ad esempio viene fornita l'annotation @Autowired Annotations. la quale può essere usata per congurare l'iniezione di dipendenza attraverso un metodo setter,un campo o il costruttore di una classe. Vediamo come... Supponiamo di avere la seguente classe: 1 2 3 4 public class AutowiredBean { private BeanA beanA ; private BeanB beanB ; 5 @Autowired public AutowiredBean ( BeanA beanA , BeanB beanB ) { this . beanA = beanA ; this . beanB = beanB ; } 6 7 8 9 10 11 12 // business logic .......... 13 14 } In tal caso @Autowired è stata utilizzata sul costruttore della classe, quindi il le di congurazione xml avrà quindi la seguente forma: 1 < context : annotation - config /> 2 3 4 < bean id =" bean_A " class =" com . BeanA "/> < bean id =" bean_B " class =" com . BeanB "/> 5 6 < bean id =" autowiredBean " class =" com . AutowiredBean "/> Da notare che di default la congurazione attraverso le Java Annotation non è abilitata, per abilitarla è necessario inserire nel le xml il tag <context:annotation-cong/>. Nel caso invece volessimo iniettare la dipendenza attraverso un metodo setter o un campo, implementiamo la classe precedente come segue: 1 2 3 4 public class AutowiredBean { @Autowired // iniezione attraverso un campo private BeanA beanA ; 5 6 private BeanB beanB ; 7 8 9 public BeanA getBeanA () { return this . beanA ;} 10 11 12 public BeanB getBeanB () { return this . beanB ;} 13 14 15 public void setBeanA ( BeanA beanA ) { this . beanA = beanA ;} 16 17 @Autowired // iniezione attraverso un metodo setter 13 public void setBeanB ( BeanB beanB ) { this . beanB = beanB ;} 18 19 20 // business logic .......... 21 22 } e il le xml resta identico a quello precedente. Un'altra annotation utile è @Qualier che serve per specicare l'id del bean che si vuole iniettare, nel caso ce ne siano due della stessa classe. Ad esempio: 1 2 3 4 5 public class AutowiredBean { @Autowired @Qualifier ( " bean_1 ") private BeanA beanA ; 6 private BeanB beanB ; 7 8 // Metodi Setter e Getter 9 10 // business logic .... 11 12 } oppure: 1 2 3 4 public class AutowiredBean { private BeanA beanA ; private BeanB beanB ; 5 @Autowired public AutowiredBean ( @Qualifier (" bean_1 " ) BeanA beanA , BeanB beanB ) { this . beanA = beanA ; this . beanB = beanB ; } 6 7 8 9 10 11 12 } così, nel caso avessimo un le xml come il seguente: 1 < context : annotation - config / > 2 3 4 5 < bean id =" bean_1 " class =" com . BeanA "/ > < bean id =" bean_2 " class =" com . BeanA "/ > < bean id =" bean_B " class =" com . BeanB "/ > 6 7 < bean id =" autowiredBean " class =" com . AutowiredBean "/ > solo il bean_1 sarà iniettato all'interno del bean autowiredBean. 14 3.7 Sviluppo di un applicazione con Spring Sviluppiamo ora un'applicazione in Spring che riassuma e mostri le funzionalità discusse no ad ora. Riportiamo innanzitutto l'implementazione dell'interfaccia e della classe Utente: IUtente.java 1 package com . utente ; 2 3 4 5 6 7 8 9 10 11 public interface IUtente { public String getNome (); public void setNome ( String nome ); public String getCognome (); public void setCognome ( String cognome ); public int getEta (); public void setEta ( int eta ); } Utente.java 1 package com . utente ; 2 3 4 5 6 7 public class Utente implements IUtente { private String nome ; private String cognome ; private int eta ; 8 public Utente ( String nome , String cognome , int eta ) { this . nome = nome ; this . cognome = cognome ; this . eta = eta ; } 9 10 11 12 13 14 15 public String getNome () { return nome ;} 16 17 18 public void setNome ( String nome ) { this . nome = nome ;} 19 20 21 public String getCognome () { return cognome ;} 22 23 24 public void setCognome ( String cognome ) { this . cognome = cognome ;} 25 26 27 public int getEta () { return eta ;} 28 29 30 public void setEta ( int eta ) { this . eta = eta ;} 31 32 33 } 15 Riportiamo l'implementazione dell'interfaccia e della classe Agenda: IAgenda.java 1 package com . agenda ; 2 3 4 import java . util . ArrayList ; import com . utente . Utente ; 5 6 7 8 9 10 11 public interface IAgenda { public void setAgenda ( ArrayList < Utente > agenda ); public ArrayList < Utente > getAgenda (); public void addUser ( String nome , String cognome , int eta ); } Agenda.java 1 package com . agenda ; 2 3 4 import java . util . ArrayList ; import com . utente . Utente ; 5 6 7 8 public class Agenda implements IAgenda { private ArrayList < Utente > users ; 9 public Agenda () { users = new ArrayList < >();} 10 11 12 public ArrayList < Utente > getAgenda () { return users ;} 13 14 15 public void setAgenda ( ArrayList < Utente > agenda ) { this . users = agenda ;} 16 17 18 public void addUser ( String nome , String cognome , int eta ) { users . add ( new Utente ( nome , cognome , eta ));} 19 20 21 } 16 Riportiamo l'implementazione della classe Gestore: Gestore.java 1 package com . service ; 2 3 4 5 6 7 8 import import import import import import java . util . ArrayList ; org . springframework . context . ApplicationContext ; org . springframework . context . support . ClassPathXmlApplicationContext ; com . agenda . Agenda ; com . agenda . IAgenda ; com . utente . Utente ; 9 10 11 12 public class Gestore { private IAgenda agenda ; 13 public Gestore () { super ();} // Non ci sono risoluzioni di dipendenze nel costr . 14 15 16 // Metodi Getter and Setter necessari realizzare la Dep . Injection public IAgenda getAgenda () { return agenda ;} 17 18 19 20 public void setAgenda ( IAgenda agenda ) { this . agenda = agenda ;} 21 22 23 public void printUsersMaggiorenni () { ArrayList < Utente > lista = agenda . getAgenda (); for ( int i =0; i < lista . size (); i ++) if ( lista . get (i ). getEta () >=18) System . out . println ( lista . get (i ). getNome ()); } 24 25 26 27 28 29 30 31 public void printAllUsers () { ArrayList < Utente > lista = agenda . getAgenda (); for ( int i =0; i < lista . size (); i ++) System . out . println ( lista . get (i ). getNome ()); } 32 33 34 35 36 37 38 public static void main ( String [] args ) { // Riportato in seguito per commentarlo meglio ... } 39 40 41 42 43 } Da notare che nel costruttore della classe Gestore non è stata risolta la dipendenza con la classe Agenda; questa verrà risolta nel le di congurazione 'Beans.xml' tramite la Setter Dependency Injection(infatti abbiamo aggiunto alla classe Gestore anche i metodi setAgenda e getAgenda). Riportiamo quindi anche il le di congurazione 'Beans.xml' : 17 Beans.xml 1 2 3 4 5 6 7 <? xml version ="1.0" encoding =" UTF -8" ? > < beans // Righe necessarie per la configurazione di Spring xmlns =" http :// www . springframework . org / schema / beans " xmlns : xsi =" http :// www . w3 . org /2001/ XMLSchema - instance " xsi : schemaLocation =" http :// www . springframework . org / schema / beans http :// www . springframework . org / schema / beans / spring - beans . xsd " > 8 9 10 11 // Definizione del bean di tipo Agenda < bean id =" agnd " class = " com . agenda . Agenda " scope = " singleton " > </ bean > 12 13 14 15 16 // Definizione del bean di tipo Gestore con uso della Setter Injection < bean id =" gest " class = " com . service . Gestore " scope =" prototype " > < property name =" agenda " ref =" agnd " ></ property > </ bean > 17 18 </ beans > Riportiamo inne il programma principale(denito come metodo della classe Gestore) per vedere come i bean vengono richiamati e utilizzati: Gestore.java 1 package com . service ; 2 3 4 5 6 7 8 import import import import import import java . util . ArrayList ; org . springframework . context . ApplicationContext ; org . springframework . context . support . ClassPathXmlApplicationContext ; com . agenda . Agenda ; com . agenda . IAgenda ; com . utente . Utente ; 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class Gestore { ... // business logic g i definita prima public static void main ( String [] args ) { // Recupero del file Beans . xml dal classpath ApplicationContext context = new ClassPathXmlApplicationContext (" Beans . xml " ); // Recupero e utilizzo del bean agnd Agenda agnd =( Agenda ) context . getBean (" agnd " ); agnd . addUser ( " Pasquale " ," Tremante " ,25); agnd . addUser ( " Emanuele " ," Tremante " ,9); agnd . addUser ( " Miky " ," Cuomo " ,27); agnd . addUser ( " Lucia " ," Oione " ,26); agnd . addUser ( " Francesco " ," Bonifacio " ,15); agnd . addUser ( " Antonio " ," Miccio " ,18); // Recupero e utilizzo del bean Gestore Gestore gest =( Gestore ) context . getBean (" gest " ); gest . printAllUsers (); System . out . println (); gest . printUsersMaggiorenni (); } } 18 4 Integrazione di Enterprise JavaBeans Spring viene spesso considerato una valida alternativa a Enterprise JavaBeans,questo perchè quasi la maggior parte delle applicazioni realizzate combinando le funzionalità di Spring con quelle di accesso ORM e JBDC, si sono rivelate una scelta migliore rispetto a quelle realizzate con i container EJB e relativi EJBs. Tuttavia è importante precisare che l'utilizzo di Spring non previene l'utilizzo degli EJBs, bensì ne rende più semplice l'accesso e l'implementazione delle loro funzionalità. Per di più, usando Spring, l'accesso ai servizi forniti dagli EJBs permette l'implementazione di quei servizi che rendono trasparente il passaggio da EJB locali a remoti, senza che il codice Client debba essere modicato, ma vediamo ora qualcosa più nel dettaglio. Ricordiamo che, utilizzando EJB, per invocare un metodo su un session bean stateless(locale o remoto), il codice cliente deve innanzitutto eettuare un'operazione di JNDI lookup per ottenere un oggetto EJB-Home tramite il quale otterrà l'oggetto vero e proprio,cioè EJB-Object, sul quale potrà invocare i metodi desiderati. Quindi per ridurre la quantità di codice, molte applicazioni EJB utilizzano i pattern Service Locator e Business Delegate, i quali costituiscono un'alternativa migliore alle operazioni di JNDI lookup; tuttavia presentano i seguenti svantaggi: • Solitamente il codice che utilizza gli EJBs dipende dai Service Locator e Business Delegate singletons, rendendo dicile il testing del codice; • Nel caso venga utilizzato il pattern Service Locator senza quello Business Delegate, il codice dell'applicazione nisce con il dover richiamare il metodo create() su un EJB home, e arontare le eccezioni derivanti. Di conseguenza, rimane legata all'API EJB la complessità di programmare il modello EJB; • L'implementazione del pattern Business Delegate consiste nella duplicazione di codice, in cui bisogna scrivere numerosi metodi che semplicemente richiamano gli stessi metodi dell' EJB. L'approccio Spring permette la creazione e l'utilizzo di oggetti proxy, normalmente congurati all'interno di un container Spring, i quali operano come delegati del codice; quindi non è necessario scrivere un altro Service Locator, eettuare un altro JNDI lookup o duplicare metodi nel Business Delegate. 19 5 Spring MVC Spring MVC è la parte del framework che fornisce le funzionalità per la realizzazione di applicazioni WEB basate sul pattern Model-View-Control(MVC), orendo sempre la possibilità di sfruttare i punti di forza di Spring come l'inversion of control (tramite dependency injection) e la aspect oriented programming. Spring MVC implementa perfettamente il pattern mantenendone sia i concetti che la nomenclatura. All'interno di una applicazione Spring MVC troviamo infatti: • i Model che sono rappresentati dalle classi che a loro volta rappresentano gli oggetti gestiti e le classi di accesso al database; • le View che sono rappresentate dai vari le JSP (compilati in HTML) e da eventuali classi per l'esportazione in formati diversi da HTML (PDF, XLS, CSV. . . ); • i Controller sono rappresentati da classi (chiamate appositamante Controller) che rimango- no in ascolto su un determinato URL e, grazie ai Model e alle View, si occupano di gestire la richiesta dell'utente. 5.1 Descrizione delle componenti Per implementare il pattern MVC, Spring usa tre elementi principali: La • DispatcherServlet • View Resolver • Handlers DispatcherServlet è una servlet(oggetto scritto in linguaggio Java che opera all'interno di un server web) che si occupa di smistare tutte le richieste (POST,GET,PUT ...) ai vari handlers, quindi funge da Front Controller. La DispatcherServlet, essendo una servlet, deve essere mappata nel le web.xml nel seguente modo: web.xml 1 2 3 4 5 < servlet > < servlet - name > dispatcher </ servlet - name > < servlet - class > org . springframework . web . servlet . DispatcherServlet </ servlet - class > <load -on - startup >1 </ load -on - startup > </ servlet > 6 7 8 9 10 < servlet - mapping > < servlet - name > dispatcher </ servlet - name > <url - pattern >/ example /* </ url - pattern > </ servlet - mapping > La servlet dispone di un proprio WebApplicationContext che estende quello principale, aggiungendo tutti i beans dichiarati nel le WEB-INF/dispatcher-servlet.xml. In particolare tra questi beans ci saranno gli handlers delle richieste e i View Resolver. Gli handlers (detti anche controller) sono i beans che si occupano di servire realmente le richieste fornite dalla DispatcherServlet, e sono quindi quelli che implementano la business logic dell'applicazione. Vediamo un esempio di implementazione di un controller: 20 controller.java 1 2 3 4 5 6 7 8 9 @Controller public class MyController { @RequestMapping ( value = "/" , method = RequestMethod . GET ) public String paginaPersonale ( Model model ) { model . addAttribute (" nome " , " Pasquale " ); model . addAttribute (" cognome " , " Tremante " ); return " pagina - personale "; } } Anche qui si può notare l'utilizzo di annotations (sempre oerte da Spring). • @Controller serve per identicare la classe come controller; • @RequestMapping serve per evidenziare il metodo e il path (tramite l'attributo value ) per il quale deve essere invocato tale metodo. In più si può denire l'HTTP method per il quale eseguire la richiesta (POST, GET) attraverso l'attributo I controller devono poi essere deniti nel le method. WEB-INF/dispatcher-servlet.xml ; per farlo deniamo all'interno del le uno scanner che si occupa di trovare le classi annotate con l' annotation di Spring e Spring MVC. dispatcher-servlet.xml 1 2 <! -- Configurazione utilizzo annotation -- > < mvc : annotation - driven / > 3 4 5 <! -- Definizone dello scanner -- > < context : component - scan base - package =" com . prova . HelloMVC " / > Attraverso questa congurazione di esempio viene detto a Spring che il mapping dei controllers avviene attraverso le annotation, quindi, quando viene fatta una richiesta del tipo http://..../example/ la DispatcherServlet reindirizza la chiamata al nostro controller di esempio, che eettua delle operazioni sul model e poi restituisce una stringa che rappresenta il view resolver. Il View Resolver è un InternalResourceViewResolver che attraverso la stringa restituita dal controller reindirizza ad una jsp. Per congurarlo aggiungiamo al le dispatcher-servlet.xml : dispatcher-servlet.xml 1 2 3 4 5 6 < bean id =" viewResolver " class =" org . springframework . web . servlet . view . InternalResourceViewResolver " > < property name =" viewClass " value =" org . springframework . web . servlet . view . JstlView "/ > < property name =" prefix " value ="/ WEB - INF / jsp /"/ > < property name =" suffix " value =". jsp "/ > </ bean > Nel nostro esempio la jsp è /WEB-INF/jsp/pagina-personale.jsp. 21 Riportiamo uno schema riassuntivo del funzionamento delle tre componenti appena illustrate: Figura 2: Spring MVC 5.2 Esempio di un applicazione con Spring MVC Riportiamo ora un semplice esempio che mostri come realizzare una semplice applicazione HelloWorld, utilizzando le funzionalità di Spring MVC precedentemente illustrate. Il primo passo è quello di creare il le web.xml all'interno del quale va congurato il WebApplica- tionContext e mappata la DispatcherServlet. web.xml 1 2 3 4 5 6 7 8 9 10 <? xml version ="1.0" encoding =" UTF -8" ? > <web - app version = " 2.5 " xmlns =" http :// java . sun . com / xml / ns / javaee " xmlns : xsi =" http :// www . w3 . org /2001/ XMLSchema - instance " xsi : schemaLocation =" http :// java . sun . com / xml / ns / javaee http :// java . sun . com / xml / ns / javaee / web - app_2_5 . xsd " > <! -- The definition of the Root Spring Container -- > < context - param > < param - name > contextConfigLocation </ param - name > < param - value >/ WEB - INF / spring / root - context . xml </ param - value > </ context - param > 11 12 13 14 15 16 17 <! -- Creates the Spring Container shared by all Servlets and Filters -- > < listener > < listener - class > org . springframework . web . context . ContextLoaderListener </ listener - class > </ listener > 18 19 20 21 22 23 24 <! -- Processes application requests -- > < servlet > < servlet - name > appServlet </ servlet - name > < servlet - class > org . springframework . web . servlet . DispatcherServlet </ servlet - class > 22 <init - param > < param - name > contextConfigLocation </ param - name > < param - value > / WEB - INF / spring / appServlet / servlet - context . xml </ param - value > </ init - param > <load -on - startup >1 </ load -on - startup > </ servlet > 25 26 27 28 29 30 31 32 33 34 35 36 37 38 < servlet - mapping > < servlet - name > appServlet </ servlet - name > <url - pattern >/ </ url - pattern > </ servlet - mapping > </ web - app > Il passo seguente è quello di implementare il controller: HomeController.java 1 package com . prova . hello ; 2 3 4 5 6 7 import import import import import java . util . Locale ; org . springframework . stereotype . Controller ; org . springframework . ui . Model ; org . springframework . web . bind . annotation . RequestMapping ; org . springframework . web . bind . annotation . RequestMethod ; 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @Controller public class HomeController { @RequestMapping ( value = "/ " , method = RequestMethod . GET ) public String home ( Locale locale , Model model ) { /* Aggiungiamo gli attributi nome , cognome e matricola al model e ne settiamo i valori . Tali attributi verranno poi utilizzati nella pagine jsp per visualizzarne i valori */ model . addAttribute (" nome " ," Pasquale " ); model . addAttribute (" cognome " ," Tremante " ); model . addAttribute (" matricola " ," N46001295 " ); /* Restituisce una stringa che corrisponde al nome della pagina jsp a cui il model reindirizzera ' */ return " home " ; } } Implementiamo poi la pagina jsp che avrà il compito di mostrare i dati contenuti nel model. Ricordiamo che jsp(Java Server Page) è un linguaggio che permette di scrivere pagine che hanno sia codice HTML sia codice Java che verrà eseguito dal Server su cui si troverà la pagina. 23 home.jsp 1 2 3 4 5 6 7 8 9 10 <%@ taglib uri =" http :// java . sun . com / jsp / jstl / core " prefix ="c" %> <%@ page session =" false " %> <html > <head > < title > Home </ title > </ head > <body > <h1 > Hello world ! </h1 > 11 12 13 14 <P > Io sono ${ nome } ${ cognome } e il mio numero di matricola </ body > </ html > Per nire implementiamo il le : $ { matricola } </P > servlet-context.xml : servlet-context.xml 1 2 3 4 5 6 7 8 9 10 11 <? xml version ="1.0" encoding =" UTF -8" ? > < beans : beans xmlns =" http :// www . springframework . org / schema / mvc " xmlns : xsi =" http :// www . w3 . org /2001/ XMLSchema - instance " xmlns : beans =" http :// www . springframework . org / schema / beans " xmlns : context =" http :// www . springframework . org / schema / context " xsi : schemaLocation =" http :// www . springframework . org / schema / mvc http :// www . springframework . org / schema / mvc / spring - mvc . xsd http :// www . springframework . org / schema / beans http :// www . springframework . org / schema / beans / spring - beans . xsd http :// www . springframework . org / schema / context http :// www . springframework . org / schema / context / spring - context . xsd " > 12 13 14 <!-- Abilita l ' utilizzo delle notazioni Spring -- > < annotation - driven / > 15 16 17 <!-- Si utilizza quando si vogliono inserire delle risorse come immagini , ecc -- > < resources mapping ="/ resources /** " location ="/ resources /" / > 18 19 20 21 22 23 24 <!-- Permette al @Controller di reindirizzare a pagine jsp -- > < beans : bean class =" org . springframework . web . servlet . view . InternalResourceViewResolver " > < beans : property name =" prefix " value ="/ WEB - INF / views /" / > < beans : property name =" suffix " value =". jsp " / > </ beans : bean > 25 26 27 28 <!-- Definisce uno scanner per la ricerca delle classi @Controller all ' interno del package ' com . prova . hello ' -- > < context : component - scan base - package =" com . prova . hello " / > 29 30 </ beans : beans > 24 Il risultato ottenuto sarà il seguente: Per lo sviluppo dell'applicazione abbiamo utilizzato SpringToolSuite, una variante di Eclipse realizzata appositamante per utilizzare le funzionalità di Spring e scaricabile direttamente dal sito uciale di Spring. L'applicazione fornisce svariate agevolazioni tra cui quella di generare automaticamente i le di congurazione servlet-context.xml e web.xml. 25 6 Conclusioni Si conclude qui il nostro tour su Spring. In realtà ci sarebbero ancora tantissimi altri aspetti importanti da trattare, altrettante funzionalità da mostrare e ovviamente anche gli esempi mostrati non riescono ad evidenziare al meglio tutti i vantaggi che il framework ore, anche perchè i beneci veri e propri cominciano a farsi sentire nella realizzazione di applicazioni su larga scala. Tuttavia, nonostante il percorso sia stato molto breve, esso è stato comunque intenso. Abbiamo infatti visto come grazie all'Inversion of Control e alla Dependency Injection si possano implementare classi di oggetti, minimizzando le dipendenze che vi sono semplicemente scrivendo poche righe di xml. Abbiamo poi visto come Spring si sia rivelato un'ottima alternativa a Enterprise JavaBean, non solo perchè lo integra al proprio interno ma anche perchè ne rende più semplice e funzionale l'utilizzo. Inne abbiamo visto come Spring implementi in modo facile ed eciente il pattern MVC mantenendo i vantaggi illustrati prima, capendo quindi come mai il framework sia molto apprezzato da molti vendor commerciali. Insomma il framework Spring rappresenta al momento il tentativo più riuscito per semplicare lo sviluppo di applicazioni enterprise di una certa complessità nell`ambito Java. Esso interpreta ottimamente i concetti dell`IoC e dall`AOP che gli permettono di raggiungere un livello di disaccoppiamento tra le componenti software che non era mai stato raggiunto della tecnologia EJB. In più ore una vasta gamma di soluzioni per coprire i problemi più comuni che si incontrano nello sviluppo del software. 26 7 Bibliograa 1. Spring Framework: Reference Documentation, http://docs.spring.io/spring-framework/docs/3.0.x/reference/index.html 2. Mr.Webmaster: Guida Java Spring, http://www.mrwebmaster.it/java/guide/guida-spring/ 3. HTML.it: Guida Spring MVC, http://www.html.it/guide/guida-al-framework-spring-mvc/ 4. Wikipedia: Framework https://it.wikipedia.org/wiki/Framework 5. Wikipedia: Spring Framework, https://it.wikipedia.org/wiki/Spring_framework 6. Wikipedia: Inversione di Controllo, https://it.wikipedia.org/wiki/Inversione_del_controllo 7. Pietro Corasaniti's Blog: Introduzione a Spring, https://pierocorasaniti.wordpress.com/2013/03/18/introduzione-a-spring/ 8. Pietro Corasaniti's Blog: Constructor Dependency Injection, https://pierocorasaniti.wordpress.com/2013/04/02/constructor-dependecy-injection-cdi/ 9. Pietro Corasaniti's Blog: Setter Dependency Injection, https://pierocorasaniti.wordpress.com/2013/03/21/setter-dependency-injection/ 10. Pietro Corasaniti's Blog: Integrazione EJB3 e Spring, https://pierocorasaniti.wordpress.com/2013/03/28/integrazione-ejb3-e-spring/ 27