Il framework Spring - Ingegneria Informatica

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