Spring parte1

Spring è essenzialmente un framework a container leggero.

La differenza tra container leggero e container pesante è il livello di indirettezza col quale il client accede ai servizi offerti dai Beans contenuti nel Server.


Il  modello a container pesante tipicamente ha il vantaggio di delegare al container funzionalità di base come

  • La gestione del ciclo di vita dei componenti
  • L’instance pooling
  • La dependency injection

Ovviamente questo è reso possibile dal fatto che ogni chiamata è intercettata dal container, e vengono eseguite delle operazioni in modo trasparente al codice del cliente.

Spring che invece è un container leggero tra le funzionalità di base ha solo la dependency injection, cioè come vedremo, si tratta di una funzionalità che consente, tramite una Factory opportuna e un meccanismo di inversion of control, di fornire all’applicazione client, le risorse richieste avendone già risolto le dipendenze.

Per quanto riguarda le funzionalità avanzate di instance pooling e gestione del ciclo di vita, Spring di default, gestisce le istanze non come veri e propri Bean, ma come semplici Plain Old Java Objects, cioè come semplici oggetti che risiedono nel container instanziati tramite pattern Singleton.

Questo ovviamente, comporta la possibilità di integrare del codice già sviluppato alla propria applicazione, senza dover cambiare nulla ed effettuando solo il deployment delle classi da instanziare nel proprio server Spring.

La potenza di Spring risiede nella sua modularità, tutte le complesse operazioni che generalmente richiedono l’uso di container pesanti o integrazione con altri servizi, in Spring possono essere ottenute tramite il deployment di un certo insieme di packages desiderati.

Di particolare successo è l’approccio ricorrente nell’utilizzare Spring come risolutore delle dipendenze, e accostare un motore delle servlet per integrare il Web-tier.  Anche l’integrazione con un sistema di supporto per la persistenza, contenuto nel package ORM, come JPA o Hibernate, è molto usato.

Tra i vari packages, quello che è il più peculiare in Spring è quello che realizza l’AOP, cioè la programmazione orientata agli aspetti.

Tale metodologia, consiste nel tenere separati degli “aspetti” orizzontali alla logica di business e nel farli richiamare da una struttura intermedia in un preciso punto della mia applicazione.  Tali aspetti possono essere ad esempio quella di dover mandare un log ad un server, monitorare delle performance dell’applicazione registrando i tempi di esecuzione, gestire delle operazioni relative alla sicurezza come verificare delle password nel DB etc…

Questo principio di astrazione è utile sia per la pulizia del codice, che per la riusabilità di tali funzioni in altre applicazioni.

In AOP si definisce JoinPoint il punto dove vogliamo agganciare l’aspetto esterno alla logica di business, ad esempio

  • Invocazione di metodi
  • Inizializzazione di classi
  • Inizializzazione di oggetti (creazione di istanze)

L’Advice, è il codice che implementa la logica addizionale dell’aspetto, si differiscono in base alla modalità di esecuzione rispetto al JoinPoint a cui vengono agganciati:

  • Before advice eseguono prima del joinpoint
  • After advice eseguono dopo il joinpoint
  • Around advice eseguono attorno (around) al joinpoint

I PointCut sono insiemi di JoinPoint, con eventuali  condizioni su di essi. Per esempio posso decidere di inserire un certo numero di JoinPoint nel PointCut, ad esempio un sottoinsieme dei metodi di una classe, e far scattare l’Advice solo dopo l’invocazione di un particolare metodo tra quelli del JoinPoint.

Il Weaving è il processo dell’effettivo inserimento di aspect dentro il codice applicativo nel punto appropriato, può essere

  • A tempo di compilazione
  • Runtime

Nel primo caso, il compilatore associa staticamente nel bytecode il codice della logica di business con l’Advice, avrò una modalità meno flessibile, ma un’esecuzione più veloce.

Nel secondo caso, le due parti rimangono separate anche dopo la compilazione, questo mi permette di avere un deployment più modulare, però ci sarà un middleware che andrà a risolvere delle dipendenze a runtime per trovare l’Advice: questo rallenta l’esecuzione e mi porta ad avere una possibilità di errori a runtime se ho dimenticato a fare il deployment dell’Aspetct.

Il Target è il componente applicativo del quale viene modificato il flusso d’esecuzione quando l’Advice viene invocato.

Vediamo però l’architettura con la quale viene implementato AOP in Spring.

Se noi vogliamo fare AOP, è chiaro che rispetto al modello base di Spring, ci serve un livello di indirettezza in più, se infatti avessi un semplice riferimento all’oggetto (al Target nel caso di AOP), chi potrebbe mai in modo trasparente agganciargli gli Aspect?

Serve dunque un riferimento ad un oggetto proxy che, dopo aver registrato gli Aspect e i Target opportuni, effettua il Weaving in modo trasparente al cliente.

A questo punto mostriamo come, a livello di codice è possibile effettuare queste operazioni.


public class MessageWriter implements IMessageWriter{
public void writeMessage() {
System.out.print("World");
}
}

Questo è il codice del Target, una semplice stampa della stringa “World”.


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;
}
}

Questo è il codice dell’Advice, notiamo che dobbiamo implementare un’interfaccia MethodInterceptor, l’invocazione del metodo del Target, che in genere implementa la logica di business è ottenuta con invocation.proceed().

Nel codice del Client abbiamo:


public static void main(String[] args) {
MessageWriter target = new MessageWriter();
ProxyFactory pf = new ProxyFactory();
// aggiunge advice alla coda della catena dell’advice
pf.addAdvice(new MessageDecorator());
// configura l’oggetto dato come target
pf.setTarget(target);
// crea un nuovo proxy in accordo con le configurazioni
// della factory
MessageWriter proxy = (MessageWriter) pf.getProxy();
proxy.writeMessage();
// Come farei invece a supportare lo stesso comportamento
// con chiamata diretta al metodo dell’oggetto target?
... } }

Notiamo che sulla proxy factory registriamo l’Advice e il Target, essa ci restituisce il riferimento all’oggetto proxy, quest’ultimo, proprio perchè si comporta da proxy, implementa l’interfaccia IMessageWriter, quindi ha gli stessi metodi della classe MessageWriter, che implementa la logica di business.

Per agganciare l’Interceptor al Target, bisogna effettuare il bind in un file xml


<beans>
<bean id="messageWriter" class="com.test.MessageWriter"></bean>
<bean id="interceptor" class="com.test.MessageDecorator"></bean>
<bean id="interceptedService"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target">
<ref bean="messageWriter"/> </property>
<property name="interceptorNames">
<list> <value>interceptor</value> </list> </property>
</bean>

</beans>

Immagini e codesnippets gentilmente concessi dal prof. Bellavista ed estratti dalle slides inerenti al corso di Sistemi Distribuiti M di Ingegneria Informatica, Università di Bologna, tutti i diritti riservati

Lascia un Commento

Fill in your details below or click an icon to log in:

Logo WordPress.com

You are commenting using your WordPress.com account. Log Out / Modifica )

Foto Twitter

You are commenting using your Twitter account. Log Out / Modifica )

Foto di Facebook

You are commenting using your Facebook account. Log Out / Modifica )

Connecting to %s

Follow

Get every new post delivered to your Inbox.