spring - unibo.itlia.deis.unibo.it/courses/sd0910-info/lucidi/07-spring(2... · 2010. 4. 22. ·...
TRANSCRIPT
Sistemi Distribuiti LS – Container Leggeri e Spring 1
SpringSpring
Università di BolognaCdS Laurea Specialistica in Ingegneria Informatica
II Ciclo - A.A. 2009/2010
Corso di Sistemi Distribuiti LS (6 cfu)
Lightweight Container: tecnologia Spring
Docente: Paolo [email protected]
http://lia.deis.unibo.it/Courses/sd0910-info/
http://lia.deis.unibo.it/Staff/PaoloBellavista/
Sistemi Distribuiti LS – Container Leggeri e Spring 2
IntroduzioneIntroduzione a Springa Spring
Che cos’è Spring?
Framework leggero per la costruzione diapplicazioni Java SE e Java EE
Molti dei concetti chiave alla base di Spring sono stati di successocosì rilevante da essere diventati linee guida per l’evoluzione diEJB3.0
Funzionalità chiave:
Inversion of Control (IoC) e Dependency injection
Supporto alla persistenza
Integrazione con Web tier
Aspect Oriented Programming (AOP)
Sistemi Distribuiti LS – Container Leggeri e Spring 3
FunzionalitFunzionalitàà ChiaveChiave::Dependency Injection e Dependency Injection e PersistenzaPersistenza
Dependency InjectionGestione della configurazione dei componenti applica principi diInversion-of-Control e utilizza Dependency Injection
Eliminazione della necessità di binding “manuale” fra componenti
Idea fondamentale di una factory per componenti (BeanFactory)utilizzabile globalmente. Si occupa del ritrovamento di oggetti per nome e della gestione delle relazioni fra oggetti (configuration management)
PersistenzaLivello di astrazione generico per la gestione delle transazioni con DB (senza essere forzati a lavorare dentro un EJB container)
Strategie generiche e built-in per JTA e l’interazione con una singolasorgente JDBC
Elimina dipendenza da container J2EE per il supporto alle transazioni
Integrazione con framework di persistenza come Hibernate, JDO, JPA
Sistemi Distribuiti LS – Container Leggeri e Spring 4
Integrazione con Web tierFramework MVC per applicazioni Web, costruito sullefunzionalità base di Spring, con supporto per diverse tecnologieper la generazione di viste, ad es. JSP, FreeMarker, Velocity, Tiles, iText e POI (Java API per l’accesso a file in formato MS)
Web Flow per navigazione a grana fine
FunzionalitFunzionalitàà ChiaveChiave::Web tier e AOPWeb tier e AOP
Supporto a Aspect Oriented ProgrammingFramework di supporto a servizi di sistema, come gestione delletransazioni, tramite tecniche AOP
Miglioramento soprattutto in termini di modularità
Parzialmente correlata anche la facilità di testing
Sistemi Distribuiti LS – Container Leggeri e Spring 5
No, Spring rappresenta un approccio piuttosto unico(che ha fortemente influenzato i container successivi, verso tecnologie a microcontainer – Spring 1.2 è datato Maggio 2005). In altre parole, proprietà originali:
Spring come framework modulare. Architettura a layer, possibilità di utilizzare anche solo alcune parti in isolamento
Anche possibilità di introdurre Spring incrementalmente in progettiesistenti e di imparare ad utilizzare la tecnologia “pezzo per pezzo”
Supporto a importanti aree non coperte da altri framework diffusi, come la gestione degli oggetti di business
Tecnologia di integrazione di soluzioni esistenti
Facilità di testing
Yet Another Framework?Yet Another Framework?
Sistemi Distribuiti LS – Container Leggeri e Spring 6
QuindiQuindi, , perchperchéé usareusare Spring?Spring?
Integrazione e cooperazione fra componenti (secondo il semplicemodello JavaBean) via Dependency Injection
Disaccoppiamento
Test-Driven Development (TDD)Possibilità di effettuare testing delle classi (POJO) senza esserelegati al framework
Programmazione dichiarativa via AOPFacile configurazione degli aspetti, ad esempio supporto alletransazioni
Uso semplificato di tecnologie diffuse e di successoAstrazioni che isolano il codice applicativo, eliminazione dicodice ridondante, gestione di comuni condizioni di errore (casodelle unchecked exception)Specificità delle tecnologie sottostanti sono comunque ancoraaccessibili (parziale visibilità)
Progettazione per interfacceOttimo isolamento delle funzionalità dai dettagli implementativi
Sistemi Distribuiti LS – Container Leggeri e Spring 7
NON è una soluzione “all-or-nothing”Estrema modularità e flessibilità
Progettata per essere facile da estendere e con molte classiriutilizzabili
Integrazione con altre tecnologieEJB per J2EE
Hibernate, iBates, JDBC per l’accesso a dati e O/RM
Java Persistence API per persistenza
Struts e WebWork per Web tier
…
QuindiQuindi, , perchperchéé usareusare Spring?Spring?
Sistemi Distribuiti LS – Container Leggeri e Spring 8
ArchitetturaArchitettura didi SpringSpring
Ruolo cruciale del container leggero che si occupa
sostanzialmente della sola Inversion of Control
Sistemi Distribuiti LS – Container Leggeri e Spring 9
Core PackageParte fondamentale del framework. Consiste in un container leggero che si occupa di Inversion of Control o, per dirla allaFowler, Dependency Injection
L’elemento fondamentale è BeanFactory, che fornisce unaimplementazione estesa del pattern factory ed elimina la necessità di gestione di singleton a livello di programmazione, permettendo di disaccoppiare configurazione e dipendenze dallalogica applicativa
DAO PackageLivello di astrazione che non rende più necessario boilerplate code per JDBC, né parsing di codici di errore database-specific
Gestione delle transazioni sia da codice che in modo dichiarativo, non solo per classi che implementano interfacce speciali(possibilità aperta a tutti i POJO)
ArchitetturaArchitettura didi SpringSpring
Vi ricordate bene, vero, che cos’è un
singleton?
Sistemi Distribuiti LS – Container Leggeri e Spring 10
ORM PackageLivello di integrazione con soluzioni diffuse per OR/M, come JPA, JDO, Hibernate, iBatis, …Le varie soluzioni O/RM suddette possono essere usate in combinazione con le altre funzionalità di Spring, come la gestionedichiarativa delle transazioni => Spring come tecnologia diintegrazione
MVC PackageImplementazione di Model-View-Controller (MVC) per applicazioniWeb; buona separazione fra codice del modello di dominio e form Web
ArchitetturaArchitettura didi SpringSpring
Sistemi Distribuiti LS – Container Leggeri e Spring 11
AOP PackageImplementazione di aspect-oriented programming conformeallo standard AOP Alliance. Permette di definire, ad esempio,intercettori di metodo e pointcut per disaccoppiamento pulito
Possibilità di utilizzare metadati a livello sorgente per incorporare informazioni aggiuntive di comportamento all’internodel codice
Necessità di aprire una parentesi su AOP ☺?
ArchitetturaArchitettura didi SpringSpring
Sistemi Distribuiti LS – Container Leggeri e Spring 12
Aspect Oriented Programming (AOP)Aspect Oriented Programming (AOP)
Aspect Oriented programming (AOP) come approccio didesign e tecnica per semplificare l’applicazione dicross-cutting concern (problematiche trasversali allalogica applicativa)Esempi di cross-cutting concern
LoggingLockingGestione degli eventiGestione delle transazioniSicurezza e auditing
Concetti rilevanti per AOP:JoinpointAdvicePointcut e AspectWeaving e TargetIntroduction
Sistemi Distribuiti LS – Container Leggeri e Spring 13
AOP: AOP: JoinpointJoinpoint & Advice& Advice
Joinpoint
Punto ben definito del codice applicativo, anchedeterminato a runtime, dove può essere inserita logicaaddizionaleEsempi di joinpoint
Invocazione di metodiInizializzazione di classiInizializzazione di oggetti (creazione di istanze)
Advice
Codice con logica addizionale che deve essereeseguito ad un determinato joinpointTipi di Advice
before advice eseguono prima del joinpoint
after advice eseguono dopo il joinpoint
around advice eseguono attorno (around) al joinpoint
Sistemi Distribuiti LS – Container Leggeri e Spring 14
Pointcut
Insieme di joinpoint usati per definire quandoeseguire un adviceControllo fine e flessibile su come applicare advice al codiceapplicativoAd esempio:
Invocazione di metodo è un tipico joinpointUn tipico pointcut è l’insieme di tutte le invocazioni di metodo in unaclasse determinata
Pointcut possono essere composti in relazioni anchecomplesse per vincolare il momento di esecuzione dell’advicecorrispondente
Aspect
Aspect come combinazione di advice e pointcut
AOP: AOP: PointcutPointcut & Aspect& Aspect
Sistemi Distribuiti LS – Container Leggeri e Spring 15
WeavingProcesso dell’effettivo inserimento di aspect dentro il codice applicativo nel punto appropriatoTipi di weaving
A tempo di compilazioneRuntime
TargetUn oggetto il cui flusso di esecuzione vienemodificato da qualche processo AOPViene anche indicato qualche volta come oggetto con advice(advised object)
AOP: Weaving & TargetAOP: Weaving & Target
Con quali costi, con quale flessibilità e con quale
potere espressivo?
Sistemi Distribuiti LS – Container Leggeri e Spring 16
Processo tramite il quale si può modificare la struttura di un oggetto introducendo in esso metodio campi addizionaliIn AOP si può usare la introduction per forzare un oggettoqualunque a implementare un’interfaccia specifica senza ilbisogno che la classe dell’oggetto implementi quella interfacciaesplicitamente
AOP: IntroductionAOP: Introduction
Sistemi Distribuiti LS – Container Leggeri e Spring 17
AOP AOP StaticoStatico o o DinamicoDinamico
AOP StaticoIl processo di weaving viene realizzato come passo ulteriore del processo di sviluppo, durante il build dell’applicazioneIncide sul codice dell’applicazione che vieneeseguitoAd esempio, in un programma Java, si può avere weaving attraverso la modifica del bytecode di una applicazione
AOP DinamicoProcesso di weaving realizzato dinamicamente a runtimePossibilità di cambiare weaving senza bisogno diricompilazione
Sistemi Distribuiti LS – Container Leggeri e Spring 18
AOP in Spring AOP in Spring
Spring realizza AOP sulla base dell’utilizzo diproxy
Se si desidera creare una classe advised, occorreuilizzare la classe ProxyFactory per creare un proxy per un’istanza di quella classe, fornendo a ProxyFactory tutti gli aspect con cui si desiderainformare il proxy
A questo punto, se voi doveste implementare AOP in Spring (in ambiente Java in generale), che tipo di
approccio usereste?
Sistemi Distribuiti LS – Container Leggeri e Spring 19
Spring e Spring e FlessibilitFlessibilitàà didiUtilizzoUtilizzo in in ScenariScenari DifferentiDifferenti
Una delle caratteristiche più apprezzate di Spring si è rivelata la suapossibilità di essere utilizzato, con complessità e costi differenti, in scenari fortemente differenziati, da semplici applet a applicazionienterprise complete
Sistemi Distribuiti LS – Container Leggeri e Spring 20
Ad esempio, solo middle tier e integrazione con Web
Ad esempio, solo remoting
Spring e Spring e varivari ScenariScenarididi UtilizzoUtilizzo
Sistemi Distribuiti LS – Container Leggeri e Spring 21
Spring e Spring e varivari ScenariScenarididi UtilizzoUtilizzo
Ad esempio, solo per integrazione di POJO in ambiente EJB (specie EJB2.x)
Sistemi Distribuiti LS – Container Leggeri e Spring 22
Dependency Injection in SpringDependency Injection in Spring
Applicazione più nota e di maggiore successo del principio di Inversion of Control“Hollywood Principle”
Don't call me, I'll call you
Container (in realtà il container leggero di Spring) si occupa dirisolvere (injection) le dipendenze dei componentiattraverso l’opportuna configurazionedell’implementazione dell’oggetto (push)Opposta ai pattern più classici di istanziazione di componenti o Service Locator, dove è il componente che deve determinarel’implementazione della risorsa desiderata (pull)Martin Fowler chiamò per primo Dependency Injection questo tipo di IoC
Sistemi Distribuiti LS – Container Leggeri e Spring 23
PotenzialiPotenziali BeneficiBenefici didiDependency InjectionDependency Injection
Dopo avere visto EJB3.0, oramai ne siete esperti mondiali ☺…
FlessibilitàEliminazione di codice di lookup nella logica di business
Possibilità e facilità di testingNessun bisogno di dipendere da risorse esterne o da container in fase di testing
Possibilità di abilitare testing automatico
ManutenibilitàPermette riutilizzo in diversi ambienti applicativi cambiandosemplicemente i file di configurazione (o in generale le specifichedi dependency injection) e non il codice
Sistemi Distribuiti LS – Container Leggeri e Spring 24
Due Due VariantiVarianti per per Dependency Injection in SpringDependency Injection in Spring
Dependency injection a livello di costruttoreDipendenze fornite attraverso i costruttori dei componenti
public class ConstructorInjection {
private Dependency dep;
public ConstructorInjection(Dependency dep) {
this.dep = dep; } }
Dependency injection a livello di metodi “setter”Dipendenze fornite attraverso i metodi di configurazione(metodi setter in stile JavaBean) dei componenti
Più frequentemente utilizzata nella comunità degli sviluppatori
public class SetterInjection {
private Dependency dep;
public void setMyDependency(Dependency dep) {
this.dep = dep; } }
Sistemi Distribuiti LS – Container Leggeri e Spring 25
BeanFactoryBeanFactory
L’oggetto BeanFactory è responsabile della gestionedei bean che usano Spring e delle loro dipendenzeOgni applicazione interagisce con la dependency injection diSpring (IoC container) tramite l’interfaccia BeanFactory
Oggetto BeanFactory viene creato dall’applicazione, tipicamente nella forma di XmlBeanFactory
Una volta creato, l’oggetto BeanFactory legge un file diconfigurazione e si occupa di fare l’injection (wiring)
XmlBeanFactory è estensione di DefaultListableBeanFactory per leggere definizioni di bean da un documento XML
public class XmlConfigWithBeanFactory {
public static void main(String[] args) {
XmlBeanFactory factory = new XmlBeanFactory(new
FileSystemResource("beans.xml"));
SomeBeanInterface b = (SomeBeanInterface) factory.
getBean(“nameOftheBean”); } }
Sistemi Distribuiti LS – Container Leggeri e Spring 26
File File didi ConfigurazioneConfigurazione
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="renderer" class="StandardOutMessageRenderer">
<property name="messageProvider">
<ref local="provider"/>
</property>
</bean>
<bean id="provider" class="HelloWorldMessageProvider"/> </beans>
Oppure a livello di costruttore…
<beans>
<bean id="provider" class="ConfigurableMessageProvider">
<constructor-arg>
<value> Questo è il messaggio configurabile</value>
</constructor-arg>
</bean> </beans>
Vedete anche l’esempio successivo, in cui in grande dettaglio sarà mostrato
come il container IoC faccia dependencyinjection
Sistemi Distribuiti LS – Container Leggeri e Spring 27
public class ConfigurableMessageProvider implements MessageProvider {
private String message;
// usa dependency injection per config. del messaggio
public ConfigurableMessageProvider(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
UsoUso delladella Dependency InjectionDependency Injection
Sistemi Distribuiti LS – Container Leggeri e Spring 28
Tipi Tipi didi ParametriParametri didi InjectionInjection
Spring supporta diversi tipi di parametri con cui fare injection1. Valori semplici2. Bean all’interno della stessa factory3. Bean anche in diverse factory4. Collezioni (collection)5. Proprietà definite esternamenteTutti questi tipi possono essere usati sia per injection sui costruttori che sui metodi setter
Ad esempio, injection di valori semplici<beans>
<bean id="injectSimple" class="InjectSimple"><property name="name"> <value>John Smith</value></property><property name="age"> <value>35</value></property><property name="height"> <value>1.78</value></property>
</bean> </beans>
Sistemi Distribuiti LS – Container Leggeri e Spring 29
EsempioEsempio: Injection : Injection didi Bean Bean delladella stessastessa FactoryFactory
Usata quando è necessario fare injection di un bean all’interno diun altro (target bean) Uso del tag <ref> in <property> o <constructor-arg> del target bean
Controllo lasco sul tipo del bean “iniettato” rispetto a quantodefinito nel target
Se il tipo definito nel target è un’interfaccia, il bean injected deveessere un’implementazione di tale interfaccia
Se il tipo definito nel target è una classe, il bean injected deveessere della stessa classe o di una sottoclasse
<beans><bean id="injectRef" class="InjectRef">
<property name="oracle"><ref local="oracle"/>
</property></bean>
</beans>
Sistemi Distribuiti LS – Container Leggeri e Spring 30
Naming Naming deidei ComponentiComponenti SpringSpring
Come fa BeanFactory a trovare il bean richiesto (pattern singleton come comportamento di default)?
Ogni bean deve avere un nome unico all’internodella BeanFactory contenente
Procedura di risoluzione dei nomiSe un tag <bean> ha un attributo di nome id, il valore di questoattributo viene usato come nome
Se non c’è attributo id, Spring cerca un attributo di nome name
Se non è definito né id né name, Spring usa il nome della classe del bean come suo nome
Sistemi Distribuiti LS – Container Leggeri e Spring 31
CodiceCodice Base Base didi HelloWorldHelloWorld
PARENTESI: proviamo a vedere con il più semplicedegli esempi se a questo punto del corso capiamofino in fondo che cosa si intende per dependency injection, quali vantaggi produce e che tipo disupporto è necessario per realizzarla
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
Quali problemi?
Necessità di cambiare il codice (e di ricompilare) per imporre unamodifica del messaggio
Codice non estensibile e modificabile
Sistemi Distribuiti LS – Container Leggeri e Spring 32
VersioneVersione didi HelloWorldHelloWorldcon con ArgomentiArgomenti a Linea a Linea didi ComandoComando
public class HelloWorldWithCommandLine {
public static void main(String[] args) {if(args.length > 0) {
System.out.println(args[0]);} else { System.out.println("Hello World!"); }
} }
In questo modo si “esternalizza” il contenuto del messaggio, cheviene letto a runtime dagli argomenti a linea di comando
Problemi?
Codice responsabile del rendering del messaggio (println) si occupa anche diottenere il messaggio
Cambiare come il messaggio deve essere ottenuto obbliga a cambiare ilcodice del renderer
Il renderer non può essere modificato facilmente (messaggio verso stderr? O in tag HTML invece che plain text?)
Sistemi Distribuiti LS – Container Leggeri e Spring 33
DisaccoppiamentoDisaccoppiamento
1) Disaccoppiamento dell’implementazione della logica del message provider rispetto al resto del codice tramite creazione di una classeseparata
public class HelloWorldMessageProvider {
public String getMessage() {return "Hello World!"; }
}
2) Disaccoppiamento dell’implementazione della logica di message renderingdal resto del codice
La logica di message rendering è data all’oggetto HelloWorldMessageProvider daqualcun altro – questo è ciò che si intende con Dependency Injection
public class StandardOutMessageRenderer {
private HelloWorldMessageProvider messageProvider = null;
public void render() {
if (messageProvider == null) {
throw new RuntimeException("You must set the property messageProvider of class:“ + StandardOutMessageRenderer.class. getName()); }
Sistemi Distribuiti LS – Container Leggeri e Spring 34
// continua
System.out.println(messageProvider.getMessage()); }
// dependency injection tramite metodo setter
public void setMessageProvider(HelloWorldMessageProvider provider) { this.messageProvider = provider;
}
public HelloWorldMessageProvider getMessageProvider() {
return this.messageProvider;
}
}
DisaccoppiamentoDisaccoppiamento
Sistemi Distribuiti LS – Container Leggeri e Spring 35
HelloWorld con HelloWorld con DisaccoppiamentoDisaccoppiamento
public class HelloWorldDecoupled {
public static void main(String[] args) {
StandardOutMessageRenderer mr =
new StandardOutMessageRenderer();
HelloWorldMessageProvider mp =
new HelloWorldMessageProvider();
mr.setMessageProvider(mp);
mr.render();
} }
A questo punto, la logica del message provider e quella del message renderer sono separate dal resto del codice
Quali problemi ancora?
Implementazioni specifiche di MessageRenderer e diMessageProvider sono hard-coded nella logica applicativa (in questo launcher in questo lucido)
Aumentiamo il disaccoppiamento tramite interfacce
Sistemi Distribuiti LS – Container Leggeri e Spring 36
public interface MessageProvider {
public String getMessage(); }
public class HelloWorldMessageProvider
implements MessageProvider {
public String getMessage() {
return "Hello World!"; }
}
HelloWorld con HelloWorld con DisaccoppiamentoDisaccoppiamento: : InterfacceInterfacce
public interface MessageRenderer {
public void render();
public void setMessageProvider(MessageProvider provider);
public MessageProvider getMessageProvider();
}
Sistemi Distribuiti LS – Container Leggeri e Spring 37
public class StandardOutMessageRenderer
implements MessageRenderer {
// MessageProvider è una interfaccia Java ora
private MessageProvider messageProvider = null;
public void render() {
if (messageProvider == null) {
throw new RuntimeException( "You must set the property messageProvider of class:“ + StandardOutMessageRenderer.class. getName()); }
System.out.println(messageProvider.getMessage()); }
…
public void setMessageProvider(MessageProvider provider) { this.messageProvider = provider; }
public MessageProvider getMessageProvider() {
return this.messageProvider; }
}
HelloWorld con HelloWorld con DisaccoppiamentoDisaccoppiamento: : InterfacceInterfacce
Sistemi Distribuiti LS – Container Leggeri e Spring 38
Rimane responsabilità del launcher di effettuare la “dependency injection”
public class HelloWorldDecoupled {
public static void main(String[] args) {
MessageRenderer mr = new StandardOutMessageRenderer();
MessageProvider mp = new HelloWorldMessageProvider();
mr.setMessageProvider(mp);
mr.render();
}
}
HelloWorld con HelloWorld con DisaccoppiamentoDisaccoppiamento: : InterfacceInterfacce
Ora è possibile modificare la logica di message rendering senzaalcun impatto sulla logica di message provider
Allo stesso modo, è possibile cambiare la logica di message provider senza bisogno di modificare la logica di message rendering
Sistemi Distribuiti LS – Container Leggeri e Spring 39
Quali problemi ancora?
L’uso di differenti implementazioni delle interfacceMessageRenderer o MessageProvider necessita comunque di unamodifica (limitata) del codice della logica di business logic (launcher)
=> Creare una semplice classe factory che legga i nomidelle classi desiderate per le implementazioni delleinterfacce da un file (property file) e le istanzi a runtime, facendo le veci dell’applicazione
HelloWorld con HelloWorld con DisaccoppiamentoDisaccoppiamento: : InterfacceInterfacce
Sistemi Distribuiti LS – Container Leggeri e Spring 40
HelloWorld con HelloWorld con ClasseClasse FactoryFactory
public class MessageSupportFactory {
private static MessageSupportFactory instance = null;
private Properties props = null;
private MessageRenderer renderer = null;
private MessageProvider provider = null;
private MessageSupportFactory() {
props = new Properties();
try {
props.load(new FileInputStream("msf.properties"));
// ottiene i nomi delle classi per le interfacce
String rendererClass = props.getProperty("renderer.class");
String providerClass = props.getProperty("provider.class");
renderer = (MessageRenderer) Class.forName(rendererClass).
newInstance();
provider = (MessageProvider) Class.forName(providerClass).
newInstance();
} catch (Exception ex) { ex.printStackTrace(); }
}
Sistemi Distribuiti LS – Container Leggeri e Spring 41
static { instance = new MessageSupportFactory(); }
public static MessageSupportFactory getInstance() {
return instance; }
public MessageRenderer getMessageRenderer() {
return renderer; }
public MessageProvider getMessageProvider() {
return provider; }
}
HelloWorld con HelloWorld con ClasseClasse FactoryFactory
public class HelloWorldDecoupledWithFactory {
public static void main(String[] args) {
MessageRenderer mr = MessageSupportFactory.getInstance().
getMessageRenderer();
MessageProvider mp = MessageSupportFactory.getInstance().
getMessageProvider();
mr.setMessageProvider(mp);
mr.render(); }
}
Sistemi Distribuiti LS – Container Leggeri e Spring 42
File di proprietà# msf.properties
renderer.class=StandardOutMessageRenderer
provider.class=HelloWorldMessageProvider
HelloWorld con HelloWorld con ClasseClasse FactoryFactory
Ora le implementazioni di message provider e message renderer possono essere modificate tramite semplice modifica del file di proprietà
Quali problemi ancora?
Necessità di scrivere molto “glue code” per mettere insiemel’applicazione
Necessità di scrivere una classe MessageSupportFactory
L’istanza di MessageProvider deve essere ancora iniettatamanualmente nell’implementazione di MessageRenderer
Sistemi Distribuiti LS – Container Leggeri e Spring 43
HelloWorld HelloWorld usandousando SpringSpring
public class HelloWorldSpring {
public static void main(String[] args) throws Exception {
// ottiene il riferimento a bean factory
BeanFactory factory = getBeanFactory();
MessageRenderer mr = (MessageRenderer) factory.
getBean("renderer");
MessageProvider mp = (MessageProvider) factory.
getBean("provider");
mr.setMessageProvider(mp);
mr.render();
}
// contina…
Sistemi Distribuiti LS – Container Leggeri e Spring 44
// Possibilità di scrivere il proprio metodo getBeanFactory()
// a partire da Spring DefaultListableBeanFactoryclass
private static BeanFactory getBeanFactory() throws Exception {
DefaultListableBeanFactory factory = new
DefaultListableBeanFactory();
// creare un proprio lettore delle definizioni
PropertiesBeanDefinitionReader rdr = new
PropertiesBeanDefinitionReader(factory);
// caricare le opzioni di configurazione
Properties props = new Properties();
props.load(new FileInputStream("beans.properties"));
rdr.registerBeanDefinitions(props);
return factory;
}
}
HelloWorld HelloWorld usandousando SpringSpring
Sistemi Distribuiti LS – Container Leggeri e Spring 45
HelloWorld con Spring: HelloWorld con Spring: QualiQuali ProblemiProblemi??
Quali vantaggi già raggiunti in questo modo?
Eliminata la necessità di produrre glue code (MessageSupportFactory)
Migliore gestione degli errori e meccanismo di configurazionecompletamente disaccoppiato
Quali problemi ancora?
Il codice di startup deve avere conoscenza delle dipendenzedi MessageRenderer, deve ottenerle e deve passarle a MessageRenderer
In questo caso Spring agirebbe come non più di unaclasse factory sofisticataRimarrebbe al programmatore il compito di fornire il proprio metodogetBeanFactory() usando le API di basso livello del framework Spring
Sistemi Distribuiti LS – Container Leggeri e Spring 46
HelloWorld con Spring DIHelloWorld con Spring DI
Finalmente ☺, utilizzo della Dependency Injection (DI) del framework Spring
File di configurazione
#Message renderer
renderer.class=StandardOutMessageRenderer
# Chiede a Spring di assegnare l’effettivo provider alla
# proprietà MessageProvider del bean Message renderer
renderer.messageProvider(ref)=provider
#Message provider
provider.class=HelloWorldMessageProvider
Sistemi Distribuiti LS – Container Leggeri e Spring 47
public class HelloWorldSpringWithDI {
public static void main(String[] args) throws Exception {
BeanFactory factory = getBeanFactory();
MessageRenderer mr = (MessageRenderer) factory.
getBean("renderer");
// Nota che non è più necessaria nessuna injection manuale
// del message provider al message renderer
mr.render(); }
private static BeanFactory getBeanFactory() throws Exception {
DefaultListableBeanFactory factory = new
DefaultListableBeanFactory();
PropertiesBeanDefinitionReader rdr = new
PropertiesBeanDefinitionReader(factory);
Properties props = new Properties();
props.load(new FileInputStream("beans.properties"));
rdr.registerBeanDefinitions(props);
return factory; }
}
HelloWorld con Spring DIHelloWorld con Spring DI
Sistemi Distribuiti LS – Container Leggeri e Spring 48
HelloWorld con Spring DI: HelloWorld con Spring DI: UltimeUltime OsservazioniOsservazioni
Il metodo main() deve semplicemente ottenere il bean MessageRenderer e richiamare render()
Non deve ottenere prima il MessageProvider e configurare la proprietà MessageProvider del bean MessageRenderer
“wiring” realizzato automaticamente dalla Dependency Injection di Spring
Nota che non serve nessuna modifica alle classi da collegareinsieme tramite DI
Queste classi NON fanno alcun riferimento a SpringNessun bisogno di implementare interfacce del framework Spring
Nessun bisogno di estendere classi del framework Spring
Classi come POJO puri che possono essere sottoposte a testing senza alcuna dipendenza da Spring
Sistemi Distribuiti LS – Container Leggeri e Spring 49
Spring DI con file XMLSpring DI con file XML
Più usualmente, dipendenze dei bean sono specificatetramite un file XML
<beans><bean id="renderer"
class="StandardOutMessageRenderer"><property name="messageProvider">
<ref local="provider"/></property>
</bean><bean id="provider"
class="HelloWorldMessageProvider"/></beans>
Sistemi Distribuiti LS – Container Leggeri e Spring 50
public class HelloWorldSpringWithDIXMLFile {
public static void main(String[] args) throws Exception {
BeanFactory factory = getBeanFactory();
MessageRenderer mr = (MessageRenderer) factory.
getBean("renderer");
mr.render();
}
private static BeanFactory getBeanFactory() throws Exception {
BeanFactory factory = new XmlBeanFactory(new
FileSystemResource("beans.xml"));
return factory;
}
}
Spring DI con file XMLSpring DI con file XML
Sistemi Distribuiti LS – Container Leggeri e Spring 51
<beans><bean id="renderer" class="StandardOutMessageRenderer">
<property name="messageProvider"><ref local="provider"/>
</property></bean><bean id="provider" class="ConfigurableMessageProvider">
<constructor-arg><value>Questo è il messaggio configurabile</value>
</constructor-arg></bean>
</beans>
Spring DI con file XML:Spring DI con file XML:UsoUso didi CostruttoreCostruttore per per MessageProviderMessageProvider
Sistemi Distribuiti LS – Container Leggeri e Spring 52
public class ConfigurableMessageProvider implements MessageProvider {
private String message;public ConfigurableMessageProvider(String message) {
this.message = message;}
public String getMessage() {return message;
}
}
Spring DI con file XML:Spring DI con file XML:UsoUso didi CostruttoreCostruttore
Sistemi Distribuiti LS – Container Leggeri e Spring 53
HelloWorldHelloWorld usandousando Spring AOPSpring AOP
Infine, se volessimo scrivere a video “Hello World!”sfruttando AOP
public class MessageWriter implements IMessageWriter{
public void writeMessage() {System.out.print("World");
}
}
joinpoint è l’invocazione del metodo writeMessage()
Necessità di un “around advice”
Sistemi Distribuiti LS – Container Leggeri e Spring 54
Gli advice Spring sono scritti in Java (nessun linguaggio AOP-specific)Pointcut tipicamente specificati in file XML di configurazioneSpring supporta solo joinpoint a livello di metodo (ad esempio, impossibile associare advice alla modifica di un campo di un oggetto)
public class MessageDecorator implements MethodInterceptor {
public Object invoke(MethodInvocation invocation)throws Throwable {
System.out.print("Hello ");Object retVal = invocation.proceed();System.out.println("!");return retVal;
}
}
HelloWorldHelloWorld usandousando Spring AOPSpring AOP
Sistemi Distribuiti LS – Container Leggeri e Spring 55
Uso della classe ProxyFactory per creare il proxy dell’oggetto target
Anche modalità più di base, tramite uso di possibilitàpredeterminate e file XML, senza istanziare uno specifico proxy per AOP
public static void main(String[] args) {MessageWriter target = new MessageWriter();ProxyFactory pf = new ProxyFactory();// aggiunge advice alla coda della catena dell’advicepf.addAdvice(new MessageDecorator());// configura l’oggetto dato come targetpf.setTarget(target);// crea un nuovo proxy in accordo con le configurazioni// della factoryMessageWriter proxy = (MessageWriter) pf.getProxy();proxy.writeMessage();// Come farei invece a supportare lo stesso comportamento// con chiamata diretta al metodo dell’oggetto target?
… } }
HelloWorldHelloWorld usandousando Spring AOPSpring AOP
Sistemi Distribuiti LS – Container Leggeri e Spring 56
Guarda caso ☺, anche in Spring possiamo definire intercettori, ma questa volta sfruttando i concetti di AOP e gli oggetti con proxy
Un intercettore Spring può eseguire immediatamente prima o dopol’invocazione della richiesta corrispondenteImplementa l’interfaccia HandlerInterceptor o estendeHandlerInterceptorAdaptor
public class MyService {
public void doSomething() {
for (int i = 1; i < 10000; i++) {
System.out.println("i=" + i); } }
}
public class ServiceMethodInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = methodInvocation.proceed();
long duration = System.currentTimeMillis() - startTime;
IntercettoriIntercettori SpringSpring
Sistemi Distribuiti LS – Container Leggeri e Spring 57
Method method = methodInvocation.getMethod();
String methodName = method.getDeclaringClass().getName() + "." +
method.getName();
System.out.println("Method '" + methodName + "' took " + duration
+ " milliseconds to run");
return null; }
}
<beans>
<bean id="myService" class="com.test.MyService"> </bean>
<bean id="interceptor" class="com.test.ServiceMethodInterceptor"> </bean>
<bean id="interceptedService"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target">
<ref bean="myService"/> </property>
<property name="interceptorNames">
<list> <value>interceptor</value> </list> </property>
</bean> </beans>
IntercettoriIntercettori SpringSpring
Sistemi Distribuiti LS – Container Leggeri e Spring 58
Tipi Tipi didi TransazioneTransazione
Altra vecchia conoscenza ☺, le transazioni. Con scarsa sorpresa:
Transazione localeSpecifica per una singola risorsa transazionale, ad esempio unicarisorsa JDBC
Transazione globaleGestita dal container
Può includere risorse transazionali multiple
Il programmatore può specificare in modo dichiarativo che un metodo di bean deve avere proprietà transazionali
Anche l’implementazione delle transazioni è basata su AOP
Intercettazione di chiamate a metodi per gestione transazioni
Nessuna necessità di modificare la logica di business, né al cambiodella transazionalità desiderata né al cambio del provider di transazionalità
Sistemi Distribuiti LS – Container Leggeri e Spring 59
LivelliLivelli didi IsolamentoIsolamentodelledelle TransazioniTransazioni
ISOLATION_DEFAULT
ISOLATION_READ_UNCOMMITTED
Possono accadere letture “sporche” (dirty read), non ripetibili e fantasma (phantom read)
ISOLATION_READ_COMMITTED
Letture sporche rese impossibili; possibilità di accadimento diletture non ripetibili e fantasma
ISOLATION_REPEATABLE_READ
Possibilità delle sole letture fantasma; dirty e non-repeatable rese non possibili
ISOLATION_SERIALIZABLE
Tutte le possibilità “spiacevoli” sopra per la lettura sono reseimpossibili
Sistemi Distribuiti LS – Container Leggeri e Spring 60
Parentesi su Letture Parentesi su Letture PhantomPhantom//NonNon--RepeatableRepeatable//DirtyDirty
Phantom readQuando, nel corso di una transazione, vengono eseguite due query
identiche e i risultati restituiti dalla seconda query sonodifferenti da quelli per la prima. Causa: “lock di range” non acquisiti (solo acquisizione di read lock) in fase di SELECT
Transaction 1
/* Query 1 */
SELECT * FROM users
WHERE age BETWEEN 10 AND 30;
/* più tardi, dopo l’esecuzione di Query 2 */
SELECT * FROM users
WHERE age BETWEEN 10 AND 30;
Transaction 2
/* Query 2 */
INSERT INTO users VALUES ( 3, 'Bob', 27 );
COMMIT;
Sistemi Distribuiti LS – Container Leggeri e Spring 61
Non-repeatable readIn soluzioni di controllo della concorrenza basate su lock, letture non-
repeatable possono avvenire quando i lock in lettura non sonoacquisiti durante una SELECT. In soluzioni di controllo dellaconcorrenza multiversion, letture non-repeatable possono avvenirequando si rilassa il vincolo che una transazione con conflitto dicommit debba effettuare rollback
Transaction 1
/* Query 1 */
SELECT * FROM users WHERE id = 1;
/* Dopo Query 2 e suo commit parziale */
SELECT * FROM users WHERE id = 1;
Transaction 2
/* Query 2 */
UPDATE users SET age = 21 WHERE id = 1;
COMMIT; /* ad es. per read-committed isolation: read lock rilasciati prima della fine della transazione totale */
COMMIT; /* fine della transazione totale, anche write lock rilasciati */
Parentesi su Letture Parentesi su Letture PhantomPhantom//NonNon--RepeatableRepeatable//DirtyDirty
Sistemi Distribuiti LS – Container Leggeri e Spring 62
Dirty readQuando una transazione legge dati che sono stati modificati da
un’altra transazione non ancora committed. Letture dirty sonosimili a letture non-repeatable, ma la seconda transazione non necessita di commitment per la prima query per restituire un risultatodiverso
Transaction 1
/* Query 1 */
SELECT * FROM users WHERE id = 1;
/* Dopo Query 2 */
SELECT * FROM users WHERE id = 1;
Transaction 2
/* Query 2 */
UPDATE users SET age = 21 WHERE id = 1;
/* Nessun commit immediato, solo alla fine della transazione */
COMMIT;
Parentesi su Letture Parentesi su Letture PhantomPhantom//NonNon--RepeatableRepeatable//DirtyDirty
Sistemi Distribuiti LS – Container Leggeri e Spring 63
PropagazionePropagazione delledelle TransazioniTransazioni
PROPAGATION_REQUIREDSupporto alla propagazione della transazione di partenza; creauna nuova transazione se non era transazionale il contesto dipartenza
PROPAGATION_SUPPORTSSupporto alla propagazione della transazione di partenza; eseguenon transazionalmente se la partenza non era transazionale
PROPAGATION_MANDATORYSupporto alla propagazione della transazione di partenza; lanciaun’eccezione se la partenza non era transazionale
PROPAGATION_REQUIRES_NEWCrea una nuova transazione, sospendendo quella di partenza, se esistente
PROPAGATION_NOT_SUPPORTEDPROPAGATION_NEVER PROPAGATION_NESTED
Sistemi Distribuiti LS – Container Leggeri e Spring 64
Abbiamo visto che alla base dell’architettura Spring c’è l’idea diInversion of Control (prima che in EJB3.0!) e di factory leggera per l’istanziazione, il ritrovamento e la gestione delle relazioni fra oggetti
Factory supportano due modalità di oggetto:
Singleton (default) – unica istanza condivisa dell’oggetto con nomespecificato, ideale per oggetti stateless. Riduce la proliferazione disingleton nel codice applicativo
Prototype – ogni operazione di ritrovamento di un oggetto produrrà la creazione di una nuova istanza. Utile per far avere ad ogni invocante unaistanza distinta
Un bean Spring può essere anche un FactoryBean (implementazionedell’interfaccia corrispondente)
Aggiunge un livello di indirettezza
Usato di solito per creare oggetti con proxy utilizzando ad es. AOP (concettualmente simile a interception in EJB, ma di più sempliceutilizzo)
Spring: Spring: qualchequalcheConsiderazioneConsiderazione AvanzataAvanzata (1)(1)
Sistemi Distribuiti LS – Container Leggeri e Spring 65
La possibilità di semplice Dependency Injection tramite costruttori o metodi semplifica il testing delle applicazioni Spring
Per esempio è semplice scrivere un test JUnit che crea l’oggetto Spring e configura le sue proprietà a fini di testing
Il container IoC non è invasivo: molti oggetti di business non dipendono dalle API di invocazione del container => possonoessere portabili verso altre implementazioni di container(PicoContainer, HiveMind, …) ed è facile “introdurre” vecchi POJO in ambiente Spring
Factory Spring sono leggere: anche implementazioni all’interno disingole applet o come applicazioni Swing standalone
Secondario, ma anche successo per unchecked runtime exception
Spring: Spring: qualchequalcheConsiderazioneConsiderazione AvanzataAvanzata (2)(2)
Sistemi Distribuiti LS – Container Leggeri e Spring 66
InoltreInoltre, , AutowiringAutowiring
Spring può occuparsi automaticamente di risolveredipendenze tramite introspezione delle classi bean. In questomodo il programmatore non si deve preoccupare di specificareesplicitamente le proprietà del bean o gli argomenti del costruttore
Ovvero, non necessario l’utilizzo di <ref>
Le proprietà del bean sono “autowired” attraverso matching basato su nome o su tipo
autowire=“name” (configurazione di default)Autowiring fatto sui nomi delle proprietà (metodi di nomeset<Property-name>() del bean)
autowire=“type”Autowiring fatto sui tipi di proprietà del bean (set<Property-name>(ArgumentType arg))
autowire=”constructor”Match fatto sui tipi degli argomenti del costruttore
…
Vi ricorda qualcosa?
Sistemi Distribuiti LS – Container Leggeri e Spring 67
ApplicationContextApplicationContext
In realtà, per accedere ad alcune funzionalità avanzate di Spring, non è sufficiente l’uso della semplice interfaccia BeanFactory => ApplicationContext è l’estensione dell’interfaccia BeanFactory
Fornisce tutte le funzionalità base + gestione delle transazioni e diAOP, ad esempio (non supportate dalla BeanFactory di base)
ApplicationContext si utilizza in modalità più “tradizionale” e framework-oriented
Funzionalità aggiuntive di ApplicationContextInterfacce per la gestione del ciclo di vitaPropagazione di eventi a bean che implementano l’interfacciaApplicationListenerAccesso a risorse come URL e file…
Sistemi Distribuiti LS – Container Leggeri e Spring 68
GestioneGestione del del CicloCiclo didi VitaVita
La gestione del ciclo di vita si basa su di un accordosull’implementazione di interfacce standardizzate. Ad esempio:Interfaccia ApplicationContextAware
Un bean che implementa questa interfaccia avrà il suo metodo diinterfaccia setApplicationContext() automaticamente invocato allacreazione del bean stesso. Riceverà così un riferimento al contesto su cui poter effettuare invocazioni nel seguito
public class Publisher implements ApplicationContextAware {private ApplicationContext ctx;
// Questo metodo sarà automaticamente invocato da IoC containerpublic void setApplicationContext(
ApplicationContext applicationContext)throws BeansException {
this.ctx = applicationContext;}
Sistemi Distribuiti LS – Container Leggeri e Spring 69
PropagazionePropagazione didi EventiEventi
La gestione degli eventi in ApplicationContext è realizzata tramitela classe ApplicationEvent e l’interfaccia ApplicationListener
Se un bean implementa l’interfaccia ApplicationListener e ne è fatto il deployment in un ApplicationContext ac1, quel bean viene notificato ogni volta che un ApplicationEvent vienepubblicato in ac1
Essenzialmente, il solito design pattern Observer
Tre tipologie di eventi built-in:
ContextRefreshEventInizializzazione o refresh di ApplicationContext
ContextClosedEventChiusura di ApplicationContext
RequestHandleEventEvento specifico per il Web – una richiesta HTTP è stataappena servita
Sistemi Distribuiti LS – Container Leggeri e Spring 70
EsempioEsempio didi GestioneGestione EventiEventi
Ad esempio, configurazione in ApplicationContext.xml del comportamento “ad ogni ricezione di un email da un indirizzo in black list, invia un email di notifica a [email protected]”
<bean id="emailer" class="example.EmailBean">
<property name="blackList">
<list>
<value>[email protected]</value>
<value>[email protected]</value>
<value>[email protected]</value>
</list>
</property> </bean>
<bean id="blackListListener" class="example.BlackListNotifier">
<property name="notificationAddress" value="[email protected]"/>
</bean>
Sistemi Distribuiti LS – Container Leggeri e Spring 71
Classe bean che pubblica eventi tramite l’oggetto ApplicationContext
public class EmailBean implements ApplicationContextAware {
private List blackList;
public void setBlackList(List blackList) {
this.blackList = blackList;
}
public void setApplicationContext(ApplicationContext ctx) {
this.ctx = ctx;
}
public void sendEmail(String address, String text) {
if (blackList.contains(address)) {
BlackListEvent evt = new BlackListEvent(address, text);
ctx.publishEvent(evt);
return;
}
}
}
EsempioEsempio didi GestioneGestione EventiEventi
Sistemi Distribuiti LS – Container Leggeri e Spring 72
Classe Notifier, che riceve le notifiche degli eventi generati
public class BlackListNotifier implement ApplicationListener {
private String notificationAddress;
public void setNotificationAddress(String notificationAddress) {
this.notificationAddress = notificationAddress;
}
public void onApplicationEvent(ApplicationEvent evt) {
if (evt instanceof BlackListEvent) {
// invio dell’email di notifica all’indirizzo appropriato
}
}
}
EsempioEsempio didi GestioneGestione EventiEventi
Sistemi Distribuiti LS – Container Leggeri e Spring 73
PossibilitPossibilitàà didi EffettuareEffettuareDependency CheckingDependency Checking
Utilizzabile per controllare l’esistenza di dipendenze non risoltequando abbiamo già fatto il deployment di un bean all’interno di un container Spring
Proprietà che non hanno valori configurati all’interno delladefinizione del bean, per i quali anche autowiring non ha prodotto alcun setting
Caratteristica utile quando ci si vuole assicurare che tutte le proprietà (o tutte le proprietà di un determinato tipo) siano state configurate su un bean
Modalità possibili:
none – nessun checksimple – dependency checking effettuato solo per tipi primitivi e collectionobject – dependency checking effettuato solo per altri bean associatiall’interno della stessa factory (collaborator)all – dependency checking effettuato per collaborator, tipi primitivi e collection