Programmazione Java Avanzata

Transcript

Programmazione Java Avanzata
Programmazione Java Avanzata
Spring (2)
Ing. Giuseppe D'Aquì
Testi Consigliati
●
Beginning Spring 2 (Apress)
●
●
Spring Reference Docs
●
●
Sul sito è possibile scaricare, tra gli “extra”, il
codice sorgente e il capitolo 1
http://static.springsource.org/spring/docs/2.5.x/refer
Spring In Action (Manning)
2
Riepilogo Concetti AOP
●
Aspect
●
●
Joinpoint
●
●
Punto di interruzione
Advice
●
●
Il “modulo” che rappresenta la funzionalità
L'implementazione
Pointcut
●
Punto di “iniezione” dell'aspetto
Riepilogo Concetti AOP (2)
Pointcut expressions
●
Per definire i pointcuts useremo le Pointcut
Expressions
●
Una espressione di esempio è:
●
execution(* *.getUser(*))
La prima parte, “execution”, rappresenta la
tipologia di pointcut (pointcut designator, PCD)
●
●
La parte tra parentesi rappresenta l'argomento
del PCD
PCD execution
●
●
●
●
Ogni PCD definisce i suoi argomenti
Nel caso di execution(), l'argomento è
rappresentato dalla definizione di un metodo
Il formato è:
●
tipo_return package.nome_classe.metodo(argomenti)
●
Es: String it.unirc.pja.User.setName(String name)
Possiamo anche non specificare alcune
componenti, sostituendole con asterisco
●
●
String it.unirc.pja.*(*)
Significa: tutti i metodi del package it.unirc.pja che
restituiscono String
Interface BeforeAdvice
●
public void before(
●
Method method,
●
Object[] args,
●
Object target
●
) throws Throwable
Interface BeforeAdvice
●
Argomenti:
●
●
●
Method method: il metodo di cui abbiamo
fermato l'esecuzione, sotto forma di oggetto
Java
Object[] args: gli argomenti passati al
metodo di cui sopra
Object target: l'oggetto target su cui viene
eseguito il metodo
Interface AfterReturningAdvice
●
public void afterReturning(
●
Object returnValue,
●
Method method,
●
Object[] args,
●
Object target
●
) throws Throwable
Interface AfterReturningAdvice
●
Argomenti:
●
●
●
●
Object returnValue: il valore restituito
Method method: il metodo di cui abbiamo
fermato l'esecuzione, sotto forma di oggetto
Java
Object[] args: gli argomenti passati al
metodo di cui sopra
Object target: l'oggetto target su cui viene
eseguito il metodo
Interface ThrowsAdvice
●
public void afterThrowing(
●
Method method,
●
Object[] args,
●
Object target,
●
NullPointerException e
●
)
●
Il tipo dell'ultimo argomento rappresenta la
tipologia di eccezioni che vogliamo
catturare
Interface Interceptor
●
●
L'interface Interceptor (detta anche
AroundAdvice) è la più potente
Permette di eseguire operazioni prima e dopo
l'invocazione del metodo, e durante il lancio di
eccezioni
Interface Interceptor
●
●
●
●
public Object invoke(
●
MethodInvocation invocation
●
) throws Throwable
L'unico argomento è MethodInvocation, che
contiene tutto il necessario per eseguire il
metodo interrotto
Dentro il metodo invoke() possiamo eseguire
operazioni qualsiasi, e quando vogliamo
invocare il metodo originale basta usare
invocation.proceed()
ProxyFactory XML configuration
●
●
●
●
Abbiamo già visto che Spring AOP funziona
creando degli oggetti proxy che incapsulano
l'oggetto target
Il generatore di questi proxy è
ProxyFactoryBean
Abbiamo visto come ottenere un proxy di un
nostro oggetto, programmaticamente
Possiamo semplificare la configurazione
sfruttando le funzionalità di Spring
ProxyFactory XML configuration
●
●
●
Possiamo far creare il proxy da Spring IoC,
iniettandovi l'oggetto target
Per creare un bean proxy, basta creare un
bean che abbia come classe ProxyFactoryBean
e poi configurarlo
Per configurarlo è necessario definire degli
advisor, ovvero coppie advice/pointcut
Autoproxy
●
●
●
Una funzione molto utile è la creazione
automatica dei proxy
Se configurata correttamente, Spring creerà in
automatico i proxy
Si potranno usare gli oggetti direttamente
senza creare prima un ProxyFactoryBean
Autoproxy
●
●
Il meccanismo prevede l'uso di alcune classi
dette ProxyCreator
Vengono configurate specificando:
●
●
Su quali oggetti vengono creati i proxy
Quali advice vengono aggiunti ai proxy
Autoproxy BeanName
●
●
BeanNameAutoProxyCreator associa un proxy
ai bean, specificandone il nome
Si crea un <bean> con classe:
●
●
org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator
Si devono settare due property:
●
●
beanNames: lista dei bean che verranno
modificati
interceptorNames: lista delle classi aspect
da applicare ai bean
Autoproxy BeanName
●
Esempio Spring 3
Schema-based configuration
●
●
●
●
Le ultime versioni di Spring permettono di
configurare le funzionalità di AOP anche
tramite particolari tag XML
Spring mette a disposizione un XML Schema
che aggiunge il i tag con il namespace aop:
Quando si usa la configurazione basata su XML
Schema cambiano anche le regole di utilizzo
dei bean
In particolare, può essere usata qualunque
classe come Aspect (non è necessario che
implementi una delle interface viste)
Schema-based configuration
●
Tag:
●
<aop:config>
–
●
<aop:aspect>
–
●
●
Contiene riferimento (attributo “ref”) al bean che
rappresenta l'aspect
<aop:pointcut>
–
●
Tag radice della configurazione
Definisce un pointcut: attri
<aop:advice>
Schema-based configuration
●
Tag:
●
●
●
●
●
●
●
<aop:config>
<aop:aspect>
<aop:pointcut>
<aop:before>
<aop:around>
<aop:after-returning>
<aop:after-throwing>
<aop:aspect>
●
●
●
●
Contiene riferimento al bean che rappresenta
l'aspect
Per utilizzarlo, si definisce un bean tramite
<bean>
Tale bean è la classe che definisce l'aspect
Si imposta un riferimento a questo bean
tramite attributo ref
●
Es: <aop:aspect ref=”myAspect”>
<aop:pointcut>
●
Definisce un pointcut
●
Attributi:
●
●
id
expression: espressione pointcut come vista
in precedenza
<aop:before>, <aop:after-*>,...
●
Definiscono gli advice
●
Attributi:
method: il metodo della classe aspect che
verrà lanciato come advice
●
pointcut: l'espressione pointcut che indica
quando applicare l'advice
●
pointcut-ref: riferimento ad una espressione
pointcut già definita con il tag
<aop:pointcut>
Gli ultimi due sono alternativi
●
●
Esempio tag <aop>
●
Vedi esempio 4
Autoproxy con tag <aop>
●
●
Quando si configura l'AOP tramite i tag del
namespace aop, l'autoproxy è pressoché
automatico
Basta aggiungere il tag:
●
<aop:aspectj-autoproxy />
AspectJ
●
●
●
●
AspectJ è una estensione Java per la
programmazione orientata agli aspetti
Introduce nuove parole chiave e annotazioni
per l'AOP
Spring AOP ha un sottoinsieme delle
funzionalità di AspectJ
Tuttavia le funzionalità di Spring AOP vanno
bene nella maggior parte dei casi comuni
Altre funzionalità di Spring
Spring IDE Eclipse Plugin
●
Lo Spring IDE Plugin per Eclipse semplifica il
lavoro, permettendo di creare, modificare,
visualizzare i file di configurazione di Spring
●
Sito: http://www.springide.org
●
Eclipse update site:
●
http://dist.springframework.org/release/IDE
Progetto Spring
●
●
●
Per attivare il plugin in un nostro progetto,
dovremo trasformarlo in progetto Spring
Quando un progetto è di tipo Spring, cambia
l'icona e viene aggiunto un elemento “Spring
Elements” ai file di progetto
vedi
Diagramma delle dipendenze
Accedere all'ApplicationContext
●
Può capitare di dover accedere, dll'interno dei
bean, all'oggetto ApplicationContext
(che, ricordiamo, è un BeanFactory
avanzato)
È una funzionalità da usare con attenzione,
perché avere un BeanFactory dentro un bean
significa creare una dipendenza
●
●
●
Mentre noi stiamo creando i bean per
eliminarle!
Accedere all'ApplicationContext
●
●
●
Per accedere all'ApplicationContext è
necessario farcelo iniettare nel nostro bean
Per questo dobbiamo implementare, nel nostro
bean, l'interface ApplicationContextAware
Questa interface prevede un metodo astratto
da implementare, setApplicationContext(),
che verrà usato da Spring per iniettare
l'application context
Accedere all'ApplicationContext
●
Vedi esempio
Spring i18n
●
●
●
Spring prevede la funzionalità di poter
separare le stringhe di testo in file esterni
(ResourceBundles)
L'acquisizione delle stringhe avviene tramite il
metodo getMessage() di ApplicationContext
Se vogliamo utilizzare una stringa presente in
un ResourceBundle, non dobbiamo far altro
che accedere ad
applicationContext.getMessage()
Spring i18n
●
●
●
I file properties usano la stessa convenzione
già vista in Struts
Per configurare Spring, dobbiamo definire un
bean con id “messageSource”:
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource
">
●
<property name="basename">
–
●
●
<value>traduzioni</value>
</property>
</bean>
Spring i18n
●
Nell'esempio Spring cercherà un file chiamato:
traduzioni.properties
Se è configurato il locale it, sarà:
●
●
traduzioni_it.properties
Il locale va passato al metodo getMessage(), i
cui parametri sono:
●
●
●
●
●
String key: la chiave di riferimento
Object[] args: i parametri che si devono
eventualmente sostituire nella stringa
Locale locale: un oggetto usato per
scegliere il nome del file
Spring i18n
●
Vedi esempio
Eventi Spring
●
●
●
●
Spring possiede un sistema di gestione di
eventi, utilizzato per disaccoppiare
ulteriormente le nostre applicazioni
Gli eventi hanno un funzionamento basato sul
pattern Observer
Un oggetto (subject) notifica ad altri
(observer) che è successo qualcosa (un evento)
Spring fornisce l'infrastruttura per questo tipo
di segnalazioni
Eventi Spring
Eventi Spring
●
●
●
●
Gli eventi devono estendere la classe
ApplicationEvent
Gli eventi vengono “lanciati” tramite
ApplicationContext.publishEvent()
Gli oggetti “osservatori” (nel gergo Spring i
“listener”) devono essere bean ed
implementare l'interface ApplicationListener
Questa interface prevede un metodo
[onApplicationEvent()] che viene richiamato
ogni volta che viene lanciato un evento
Eventi Spring
●
Vedi esempio
Spring e Web Application Context
●
●
●
Quando abbiamo a che fare con
un'applicazione Web, la configurazione
dell'applicationContext cambia
Invece di ottenere un oggetto
ApplicationContext tramite programmazione,
dobbiamo configurare il servlet container per
caricare anche Spring
Per far ciò ci serviamo di un tag <listener> in
web.xml
Spring e Web Application Context
●
In web.xml dobbiamo aggiungere:
●
●
●
<listener>
–
<listener-class>
–
org.springframework.web.context.ContextLoaderListener
–
</listener-class>
</listener>
Questa configurazione carica in automatico la
configurazione di Spring dal file:
●
WEB-INF/applicationContext.xml
Spring e Web Application Context
●
Per caricare la configurazione da altri file,
dovremo aggiungere un tag <context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:configurazione.xml
classpath:configurazione-avanzata.xml</param-value>
</context-param>
●
Questo ad esempio cerca due file
“configurazione.xml” e “configurazioneavanzata.xml” nel classpath
Spring e Web Application Context
●
Vedi esempio
Bean e Web
●
●
Abbiamo visto che un bean può essere creato
in due modalità, singleton o prototype
In ambito Web ci sono più possibili modalità,
impostate con l'attriburo “scope”:
request: viene creato un oggetto per ogni
request http
●
session: viene creato un oggetto per ogni
sessione
Queste due si aggiungono agli scope già visti
“singleton” e “prototype”
●
●
Spring MVC
Spring MVC
Spring MVC
●
●
●
La classe “cardine” di Spring MVC è
DispatcherServlet
Si occupa di ricevere la http request, e passa il
controllo via via agli altri componenti
Infine restituisce la http response
Spring MVC
Spring MVC configurazione servlet
●
In quanto Servlet va configurata in WEBINF/web.xml
<servlet>
<servlet-name>applicazione</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
Spring MVC configurazione servlet
●
Serve anche un servlet-mapping
<servlet-mapping>
<servlet-name>applicazione</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
●
Ricordiamo che questo serve ad associare le
url alla nostra servlet (in questo caso, ogni
richiesta che termina in .htm verrà gestita da
Spring MVC)
Modularizzazione Configurazione
●
Quando configuriamo la servlet Spring MVC, il
file di configurazione di default per Spring è
nome_servlet-servlet.xml
Per esempio, nel caso precedente si ha:
●
●
applicazione-servlet.xml
Per spezzare la configurazione in più file,
basta definire in web.xml anche un
ContextLoaderListener, come visto prima
●
●
Passi Base
1)Scrivere la classe Controller che effettua la
logica
2)Configurare il controller nel context di
DispatcherServlet
3)Configurare un View Resolver per legare il
controllet a un file JSP
4)Scrivere il file JSP che verrà usato come base
per la creazione della pagina da mostrare
all'utente
Passi Base: controller semplice
●
Vedi esempio
Spring MVC: URL Mapping
●
●
●
Sappiamo che un certo tipo di url verranno
gestite dalla nostra DispatcherServlet
Normalmente abbiamo più di un controller,
quindi dobbiamo definire quale url deve essere
gestita da quale controller
Dobbiamo realizzare, cioè, un mapping tra url
e controller
Mapping sul nome bean
●
Il comportamento di default è mappare la url
in base al nome del bean del controller
●
Se abbiamo <bean name=”/home.htm” … />
per il nostro controller, verrà mappato su
/home.html
Mapping diretto
<bean id=”simpleUrlMapping” class=
"org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<map>
<entry key="/admin/list" value-ref="adminUserListController"/>
<entry key="/admin/view/**" value-ref="adminUserViewController"/>
</map>
</property>
<property name="defaultHandler" ref="defaultHandler"/>
</bean>
Class Name Mapping
<bean id="urlMapping"
class="org.springframework.web.servlet.mvc.ControllerClassNameHandlerMap
ping"/>
●
Questo tipo di mapping associa in automatico
url con lo stesso nome del controller
●
Es.: se la richiesta è /home.htm, il
controller associato sarà HomeController
Usare più mapping
●
Tutte le classi usate per i mapping supportano
la property “order”:
●
●
<property name="order"><value>0</value></property>
Per associare una url ad un controller, Spring
procederà ad usare i mapping da quello con
order più basso (priorità maggiore) finché non
troverà un controller correttamente mappato
Controller
●
●
●
I Controller si occupano di gestire la logica
dell'applicazione, sono il “cervello” (quello
che per Struts2 sono le Action)
A differenza di Struts2, i controller sono di
diverso tipo e si comportano in modo diverso
tra loro
C'è un'intera gerarchia di classi
Gerarchia dei Controller
Simple Controller
●
●
●
●
Un controller basilare si realizza estendendo
AbstractController
Il metodo da riempire è
handleRequestInternal()
Questo metodo accetta in ingresso due
oggetti, HttpServletRequest e
HttpServletResponse
L'unica differenza con una servlet è che
restituisce un oggetto ModelAndView
(utilizzato da un view resolver per costruire la
pagina finale)
ModelAndView
●
●
●
●
ModelAndView è un concetto fondamentale in
Spring MVC
Incapsula al suo interno la view che verrà
utilizzata, e gli oggetti del model che le
verrano passati
Il formato di utilizzo è infatti
return new ModelAndView(“nome_view”,
“nome_logico_model”, oggetto_del_model);
Simple Controller
●
●
Il controller infine va inserito nella
configurazione del WebApplicationContext
Basta inserirlo come bean, senza
configurazioni particolari
Command Controller
●
●
●
Il controller di tipo Command rende più
semplice la gestione dei parametri Http
Infatti il suo metodo base handle() ha in input
(a parte HttpServletRequest/Response) un
oggetto command che incapsula i parametri
http
[questo è equivalente alle Action di Struts 2
gestite con getModel()]
Form Controller
●
Semplifica la gestione delle Form Html
Validation
●
●
Un form validator deve implementare
l'interface Validator
Questa interface prevede due metodi:
●
void validate(Object, Errors);
–
●
La funzione che dovremo implementare controllando il
primo oggetto (restituzione della form), in caso
generando oggetti Errors
boolean supports(Class);
–
Funzione “filtro”, sempre da implementare, che deve
restituire true se l'oggetto class passato rappresenta il
tipo delle classi form da validare
Validation
●
●
Spring in pratica prima controlla (tramite
support()) se il validator può validare un certo
oggetto
Se si, chiama la funzione validate()
Throwaway Controller
●
●
●
Il Throwaway Controller è il controller più
simile alle Action di Struts2
Possiede una funzione execute() da
implementare
I dati vengono passati creando opportuni
setter
●
Es. se esiste un parametro “username”,
Spring cerca un metodo setUsername() del
nostro controller
Throwaway Controller (2)
●
●
Il Throwaway Controller è differente dagli altri
Innanzitutto si deve definire, come bean, con
scope “prototype”
●
●
●
Infatti l'oggetto deve essere, in pratica, ricreato ogni volta,
per permettere di avere i parametri nuovi
Inoltre non eredita direttamente da Controller,
quindi dovremo configurare un bean “handler”
per permettere a DispatcherServlet di gestirlo
<bean id="throwawayHandler"
class="org.springframework.web.servlet.mvc.throwaway.Thro
wawayControllerHandlerAdapter"/>
ViewResolvers
●
●
Il ViewResolver si occupa di effettuare il
mapping tra i nomi logici delle view (che,
come abbiamo visto, si impostano creando
ModelAndView) e i corrispondenti file JSP
Un ViewResolver si configura definendo un
bean di un certo tipo, per esempio
UrlBasedViewResolver
ViewResolvers
●
<bean id="viewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver">
●
<property name="prefix" value="/WEB-INF/jsp/"/>
●
<property name="suffix" value=".jsp"/>
●
●
●
●
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView"/>
</bean>
“prefix” indica la parte iniziale del percorso,
mentre “suffix” la parte finale
“viewClass” indica quale motore di templating
stiamo utilizzando per la view (in questo caso,
JSP)
Spring Web Flow
●
●
●
Spring Web Flow permette di modellare il
comportamento di una applicazione web in
termini di stati e transizioni di stato
Si considera l'applicazione come una macchina
a stati finiti, che si può definire tramite State
Diagram
Spring Web Flow affianca Spring MVC, non lo
sostituisce
Spring Web Flow
Configurazione Web Flow
●
●
Per iniziare ad usare Web Flow basta definire
un bean:
<bean name="flowController"
class="org.springframework.webflow.executor.mvc.FlowController">
●
●
●
<property name="flowExecutor" ref="flowExecutor"/>
</bean>
Definito il bean, possiamo usarlo nella
configurazione dell'Url Mapping aggiungendo
●
<entry key="/admin/*" value-ref="flowController"/>
Configurazione Web Flow (2)
●
Bisogna configurare anche il Flow Executor:
●
<flow:executor id="flowExecutor" registry-ref="flowRegistry"/>
●
E il Flow Registry, che contiene riferimenti ai
file di configurazione degli stati
●
<flow:registry id="flowRegistry">
–
●
●
<flow:location path="classpath:**-flow.xml"/>
</flow:registry>
(gli asterischi indicano il flowId, identificativo
del flow)
Flow XML
●
●
Ogni file di configurazione definisce un flow
Il flow contiene, dentro un tag <flow>, la
definizione di:
●
●
●
●
Stato di partenza
Lista di stati
Definizione delle transizioni tra stati
Stato finale
Web Flow Actions
●
Le Actions sono metodi che vengono associati
ad uno stato o transizione
Ad esempio, possiamo associare una Action:
●
all'inizio del flow
●
all'ingresso in uno stato
●
All'uscita da uno stato
●
al verificarsi di una determinata transizione
●
..eccetera
Le Action vengono definite come bean e poi
richiamate dalla configurazione del flow
●
●
Spring Web Flow: esempio stati
●
<start-state idref="createUser"/>
●
<view-state id="createUser" view="admin/createUser">
●
<render-actions>
–
●
</render-actions>
●
<transition on="preview" to="previewUser">
–
●
<action bean="createUserAction" method="setupForm"/>
<action bean="createUserAction" method="bindAndValidate"/>
●
</transition>
●
<transition on="cancel" to="listUsers"/>
</view-state>
Spring Web Flow: esempio stati (2)
●
<view-state id="previewUser" view="admin/previewUser">
●
<transition on="edit" to="createUser"/>
●
<transition on="save" to="listUsers">
–
●
<action bean="createUserAction" method="save"/>
</transition>
●
</view-state>
●
<end-state id="listUsers" view="externalRedirect:/admin"/>
Spring Web Flow Plugin
●
●
Nello Spring IDE plugin per Eclipse c'è una
parte che permette di gestire i Web Flow
Funzionalità aggiunte:
●
●
Possibilità di validare le configurazioni dei
flow
Possibilità di visualizzare e creare in modo
visuale un flow
Spring Tag Library
●
●
●
Anche Spring possiede tag library JSP per
semplificare la scrittura di elementi HTML
well-formed
Del namespace spring: ricordiamo il tag
<spring:message> usato per accedere ai
ResourceBundles
Il namespace form: invece contiene elementi
molto simili a quelli già visti in Struts2
Spring e Struts2
●
●
●
L'uso di Spring con Struts2 ci permette di
sfruttare le funzionalità IoC e AOP del primo
Usandole per disaccoppiare i vari elementi del
nostro Model, oppure per iniettare oggetti DAO
Per integrare Spring con Struts2 è necessario
utilizzare un plugin (incluso nella distribuzione
ufficiale di Struts2) che permette di usare
Spring per definire oggetti interni del
framework Struts2
Spring e Struts2
●
●
●
●
Struts2 usa, internamente, un oggetto
chiamato ObjectFactory
Questo oggetto è usato per la creazione degli
elementi come Action, Interceptor, Result
Lo Spring Plugin affianca l'ObjectFactory
interno con uno proprio, basato su
BeanFactory
La cosa importante da notare è che
l'ObjectFactory interno è usato per default
Spring e Struts2
●
Al web.xml, già usato per configurare Struts2,
dobbiamo aggiungere il listener di Spring
ContextLoaderListener
Come già detto, il file di configurazione di
default in questo caso è
●
●
●
WEB-INF/applicationContext.xml
Spring e Struts2: Actions
●
●
Per poter iniettare bean all'interno di una
Action, è necessario che la Action stessa sia
definita come bean in applicationContext.xml
Una volta definita come bean (con scope
prototype), per poter utilizzare
l'ObjectFactory di Spring basta modificare
l'attributo “class” del tag <action> della
configurazione Struts
●
Normalmente, l'attributo indica la classe da
creare; se specifichiamo un bean id verrà
utilizzato Spring e non la creazione diretta
Spring, Struts2 e Hibernate
●
●
A questo punto è immediato il passo
successivo: possiamo usare Spring per iniettare
anche gli oggetti di Hibernate, come
SessionFactory
Oppure usare le annotazioni come
@Transactional per semplificare la scrittura
delle transazioni

Documenti analoghi

dependency injection di Spring

dependency injection di Spring Chiara separazione ruoli: controller, validator, oggetti command/form/ model, DispatcherServlet, handler mapping, view resolver, ... Adattabilità e riutilizzabilità: possibilità di utilizzare quals...

Dettagli

Programmazione Java Avanzata

Programmazione Java Avanzata Struts 2 è un framework per lo sviluppo di applicazioni web in Java Implementa il pattern MVC È stato completamente riscritto nella versione 2, a partire da un framework chiamato

Dettagli

Seam - JBoss.org Documentation

Seam - JBoss.org Documentation 4.2.1. Bean di sessione stateless .................................................................... 4.2.2. Bean di sessione stateful ................................................................

Dettagli