SeamFramework.orgCommunity Documentation
Non vedi l'ora di iniziare a scrivere il primo Web Beans? O forse sei un pò scettico e ti domandi quali virtuosismi ti farà fare la specifica Web Beans! La buona notizia è che probabilmente hai già scritto e usato centinaia, forse migliaia di Web Beans. Potresti addirittura non ricordare il primo Web Bean scritto.
Con alcune eccezioni molto particolari, ogni classe Java con un costruttore che non accetta parametri è un Web Bean. Questo include ogni JavaBean. Inoltre, ogni session bean di stile EJB3 è un Web Bean. Sicuramente i JavaBean e gli EJB3 che si sono sempre scritti non erano in grado di sfruttare i nuovi servizi definiti dalla specifica Web Beans, ma si sarà presto in grado di usare ciascuno di essi come Web Bean iniettandoli in altri Web Beans, configurandoli tramite strumenti di configurazione XML Web Bean, e perfino aggiungendo a loro interceptor e decoratori senza toccare il codice esistente.
Si supponga di avere due classi Java esistenti, usate da anni in varie applicazioni. La prima classe esegue il parsing di una stringa in una lista di frasi:
public class SentenceParser {
public List<String
> parse(String text) { ... }
}
La seconda classe è un session bean stateless front-end per un sistema esterno capace di tradurre le frasi da una lingua ad un altra:
@Stateless
public class SentenceTranslator implements Translator {
public String translate(String sentence) { ... }
}
Dove Translator
è l'interfaccia locale:
@Local
public interface Translator {
public String translate(String sentence);
}
Sfortunatamente non ci sono classi preesistenti che traducano l'intero testo dei documenti. Quindi occorre scrivere un Web Bean che faccia questo lavoro:
public class TextTranslator {
private SentenceParser sentenceParser;
private Translator sentenceTranslator;
@Initializer
TextTranslator(SentenceParser sentenceParser, Translator sentenceTranslator) {
this.sentenceParser = sentenceParser;
this.sentenceTranslator = sentenceTranslator;
}
public String translate(String text) {
StringBuilder sb = new StringBuilder();
for (String sentence: sentenceParser.parse(text)) {
sb.append(sentenceTranslator.translate(sentence));
}
return sb.toString();
}
}
Si può ottenere un'istanza di TextTranslator
iniettandolo in un Web Bean, Servlet o EJB:
@Initializer
public setTextTranslator(TextTranslator textTranslator) {
this.textTranslator = textTranslator;
}
In alternativa si può ottenere un'istanza chiamando direttamente un metodo del manager Web Bean:
TextTranslator tt = manager.getInstanceByType(TextTranslator.class);
Ma TextTranslator
non ha un costruttore con nessun parametro! E' ancora un Web Bean? Una classe che non ha un costruttore senza parametri può essere un Web Bean se il suo costruttore è annotato con @Initializer
.
Come hai indovinato, l'annotazione @Initializer
ha qualcosa che fare con la dependency injection! @Initializer
può essere applicato ad un costruttore od un metodo di un Web Bean, e dice al manager Web Bean di chiamare quel costruttore o metodo quando si istanzia il Web Bean. Il manager Web Bean inietterà altri Web Bean nei parametri del costruttore o del metodo.
In fase di inizializzazione del sistema, il manager Web Bean deve convalidare che esattamente un solo Web Bean esista e soddisfi ciascun punto di iniezione. Nell'esempio, se nessuna implementazione di Translator
fosse disponibile se l'EJB SentenceTranslator
non venisse deployato il manager Web Bean lancerebbe una UnsatisfiedDependencyException
. Se più di un'implementazione di Translator
fosse disponibile, il manager Web Bean lancerebbe una AmbiguousDependencyException
.
Ma cosa è esattamente un Web Bean?
Un Web Bean è una classe di un'applicazione che contiene della logica di business. Può essere chiamato direttamente da codice Java, o può essere invocato via Unified EL. Un Web Bean può accedere a risorse transazionali. Le dipendenze tra Web Beans sono gestite automaticamente dal manager Web Bean. La maggior parte dei Web Beans sono stateful e contestuali. Il ciclo di vita di un Web Bean è sempre gestito da un manager Web Bean.
Torniamo indietro un attimo. Cosa significa veramente essere "contestuale"? Poiché Web Beans può essere stateful, è importante quale istanza di bean si ha. Diversamente da un modello a componenti stateless (per esempio, i session bean stateless) o un modello a componenti singleton (come i servlet o i bean singleton) i client di un Web Bean vedono il Web Bean in stati differenti. Lo stato del client visibile dipende dall'istanza del Web Bean alla quale il client ha il riferimento.
Comunque, in modo simile ad un modello stateless o singleton, ma non come i session bean stateful, il client non ha il controllo sul ciclo di vita dell'istanza, creandola e distruggendola esplicitamente. Invece, lo scope del Web Bean determina:
il ciclo di vita di ogni istanza del Web Bean e
quali client condividono una referenza con una particolare istanza del Web Bean.
Per un dato thread in un'applicazione Web Beans, ci può essere un contesto attivo associato allo scope del Web Bean. Questo contesto può essere univoco nel thread (per esempio, se il Web Bean è con scope di richiesta), o può essere condiviso con alcuni altri thread (per esempio, se il Web Bean è con scope di sessione) od anche tutti gli altri thread (se è scope di applicazione).
I client (per esempio, altri Web Beans) che sono in esecuzione nello stesso contesto vedranno la stessa istanza del Web Bean. Ma i client in un contesto diverso vedranno un istanza diversa.
Un grande vantaggio del modello contestuale è che consente ai Web Beans stateful di essere trattati come servizi! Il client non deve preoccuparsi di gestire il ciclo di vita del Web Bean che sta utilizzando, e neppure deve sapere quale sia il ciclo di vita. Web Beans interagisce passando i messaggi, e le implementazioni Web Bean definiscono il ciclo di vita del proprio stato. I Web Beans sono debolmente disaccoppiati (loosely coupled) poiché:
interagiscono tramite delle API pubblica ben-definita
il loro ciclo di vita è completamente disaccoppiato
Si può sostituire un Web Bean con un diverso Web Bean che implementa la stessa API ed ha un diverso ciclo di vita (un diverso scope) senza alterare l'implementazione dell'altro Web Bean. Infatti Web Beans definisce un meccanismo sofisticato per fare l'override delle implementazioni Web Bean al momento del deploy, come visto in Sezione 4.2, «Tipi di deploy».
Si noti che non tutti i client dei un Web Bean sono Web Bean. Altri oggetti come Servlet o Message-Driven Beans che sono per natura non iniettabili, oggetti contestuali possono pure ottenere riferimenti a Web Bean tramite iniezione.
Più formalmente, secondo la specifica:
Un Web Bean comprende:
Un set (non vuoto) di tipi di API
Un set (non vuoto) di tipi di annotazione di binding
Uno scope
Un tipo di deploy
Opzionalmente un nome Web Bean
Un set di tipi di interceptor binding
Un implementazione Web Bean
Vediamo cosa significano alcuni di questi termini per lo sviluppatore Web Bean.
I Web Bean solitamente acquisiscono riferimenti ad altri Web Bean tramite la dependency injection. Ogni attributo iniettato specifica un "contratto" che deve essere soddisfatto dal Web Bean per essere iniettato. Il contratto è:
un tipo di API, assieme a
un set di tipi di binding
Un API è una classe o interfaccia definita dall'utente. (Se il Web Bean è un session bean EJB, il tipo di API è l'interfaccia @Local
o la vista locale della classe-bean). Un tipo di binding rappresenta un semantica del client che è soddisfatta da certe implementazioni dell'API e non da altre.
I tipi di binding sono rappresentati da annotazioni definite dall'utente che sono loro stesse annotate con @BindingType
. Per esempio, il seguente punto di iniezione ha un tipo di API PaymentProcessor
ed un tipo di binding @CreditCard
:
@CreditCard PaymentProcessor paymentProcessor
Se nessun tipo di binding viene specificato in modo esplicito ad un punto di iniezione, il tipo di binding di default si assume essere @Current
.
Per ogni punto di iniezione, il manager Web Bean cerca un Web Bean che soddisfi il contratto (che implementi la API, e che abbia tutti i tipi di binding), ed inietta tale Web Bean.
Il seguente Web Bean ha il tipo binding @CreditCard
e implementa il tipo API PaymentProcessor
. Può quindi essere iniettato nel punto di iniezione d'esempio:
@CreditCard
public class CreditCardPaymentProcessor
implements PaymentProcessor { ... }
Se un Web Bean non specifica esplicitamente un set di tipi di binding, ha esattamente un solo tipo di binding: il tipo di binding di default @Current
.
Web Beans definisce un algoritmo di risoluzione sofisticato ma intuitivo che aiuta il container a decidere cosa fare se più di un Web Bean soddisfa un particolare contratto. Vedremo i dettagli in Capitolo 4, Dependency injection.
I tipi di deploy consentono di classificare i Web Bean secondo uno scenario di deploy. Un tipo di deploy è un'annotazione che rappresenta un particolare scenario di deploy, per esempio @Mock
, @Staging
oppure @AustralianTaxLaw
. Si applica l'annotazione ai Web Bean che dovrebbero essere deployati in tale scenario. Un tipo di deploy consente ad un intero set di Web Bean di essere deployati in modo condizionato, con una sola linea di configurazione.
Molti Web Bean usano soltanto il tipo di deploy di default @Production
, ed in questo caso non occorre specificare esplicitamente nessun tipo di deploy. Tutti e tre i Web Bean d'esempio hanno ul tipo di deploy @Production
.
In un ambiente di test è possibile sostituire il Web Bean SentenceTranslator
con un "oggetto mock":
@Mock
public class MockSentenceTranslator implements Translator {
public String translate(String sentence) {
return "Lorem ipsum dolor sit amet";
}
}
In ambiente di test si dovrebbe abilitare il tipo di deploy @Mock
per indicare che l'uso di MockSentenceTranslator
ed ogni altro Web Bean annotato con @Mock
.
Si discuterà questa potente funzionalità con maggior dettaglio in Sezione 4.2, «Tipi di deploy»."
Lo scope definisce il ciclo di vita e la visibilità delle istanze di Web Bean. Il modello di contesto Web Bean è estensibile e facilita gli scope arbitrari. Comunque alcuni importanti scope sono predefiniti all'internodella specifica e vengono forniti dal manager Web Bean. Uno scope è rapresentato da un tipo di annotazione.
Per esempio un'applicazione web può avere Web Bean con scope di sessione
@SessionScoped
public class ShoppingCart { ... }
Un'istanza di un Web Bean con scope sessione è legato ad una sessione utente ed è condivisa da tutte le richieste che si eseguono nel contesto di tale sessione.
Di default i Web Bean appartengono ad uno speciale scope chiamato pseudo-scope dipendente. Web Bean con questo scope sono oggetti puri dipendenti dall'oggetto nel quale vengono iniettati ed il loro ciclo di vita è legato al ciclo di vita di tale oggetto.
Approfondiremo gli scope in Capitolo 5, Scope e contesti.
Un Web Bean può avere un nome che gli consente di essere usato in un'espressione Unified EL. E' facile specificare il nome del Web Bean:
@SessionScoped @Named("cart")
public class ShoppingCart { ... }
Ora si può facilmente utilizzare il Web Bean in ogni pagina JSF o JSP:
<h:dataTable value="#{cart.lineItems}" var="item"> .... </h:dataTable >
Si può anche lasciare assegnare al manager Web Bean il nome di default:
@SessionScoped @Named
public class ShoppingCart { ... }
In questo caso il nome di default è shoppingCart
il nome della classe non qualificata, con il primo carattere messo in minuscolo.
Web Beans supporta la funzionalità di interceptor definita da EJB 3, non solo per i bean EJB, ma anche per classi Java semplici (plain). In aggiunta, Web Beans fornisce un nuovo approccio al binding di interceptor nei confronti di bean EJB e di altri Web Beans.
Rimane la possibilità di specificare direttamente la classe interceptor tramite l'uso dell'annotazione @Interceptors
.
@SessionScoped
@Interceptors(TransactionInterceptor.class)
public class ShoppingCart { ... }
Comunque è più elegante ed è considerata una pratica migliore quella di giungere indirettamente ad un interceptor binding tramite un tipo di interceptor binding:
@SessionScoped @Transactional
public class ShoppingCart { ... }
Si discuteranno gli interceptor e i decoratori di Web BEans in Capitolo 7, Gli interceptor e Capitolo 8, Decoratori.
Si è già visto che JavaBeans, EJB ed altri tipi di classi Java possono essere Web Bean. Ma esattamente quali tipi di oggetti sono Web Beans?
La specifica Web Beans dice che una classe concreta Java è un Web Bean semplice se:
Non è un componente gestito da un container EE, come EJB, un Servlet o un entity JPA,
non è una classe interna statica/non statica,
non è un tipo parametrizzato, e
ha un costruttore senza parametro, o un costruttore annotato con @Initializer
.
Quindi quasi ogni JavaBean è un Web Bean semplice.
Ogni interfaccia implementata direttamente o indirettamente da un Web Bean semplice è un tipo di API di un Web Bean semplice. La classe e le sue superclassi sono anch'essere tipi di API.
La specifica dice che tutti i bean di sessione stile EJB3 e quelli singleton sono Web Bean enterprise. I bean message driven non sono Web Bean poiché non sono intesi per essere iniettati in altri oggetti ma possono sfruttare la maggior parte della funzionalità dei Web Bean, inclusi dependency injection e interceptor.
Ogni interfaccia locale di un Web Bean enterprise che non ha un parametro tipo wildcard o variabile tipo, e ciascuna delle sue superinterfacce, è un tipo di API del Web Bean enterprise. Se il bean EJB ha una vista locale di classe bean, la classe bean e ogni sua superclasse è anch'essa un tipo di API.
I session bean stateful dovrebbero dichiarare un metodo remoto senza parametri od un metodo annotato con @Destructor
. Il manager Web Bean chiama questo metodo per distruggere l'istanza del session bean statefull alla fine del suo ciclo di vita. Questo metodo è chiamato metodo distruttore del Web Bean enterprise.
@Stateful @SessionScoped
public class ShoppingCart {
...
@Remove
public void destroy() {}
}
Ma allora quando occorre usare un Web Bean enterprise invece di un Web Bean semplice? Quando occorrono servizi enterprise avanzati offerti da EJB, quali:
gestione delle transazioni a livello di metodo e sicurezza,
gestione della concorrenza,
passivazione a livello di istanza per session bean stateful e pooling di istanze per session bean stateless,
invocazione remota e web service, e
timer e metodi asincroni,
si dovrebbe usare un Web Bean enterprise. Quando non occorrono queste cose, va bene utilizzare un Web Bean semplice.
Molti Web Bean (inclusi Web Bean con scope di sessione o applicazione) sono disponibili per accessi concorrenti. Quindi la gestione della concorrenza fornita da EJB3.1 è molto utile. La maggior parte dei Web Bean con scope sessione e applicazione dovrebbero essere EJB.
Web Bean che mantengono riferimenti alle risorse pesanti o mantengono molti benefici dello stato interno dal ciclo di vita avanzato, gestito dal container, definito dal modello EJB @Stateless
/@Stateful
/@Singleton
", con supporto alla passivazione e pooling delle istanze.
Infine è ovvio quando occorre usare la gestione delle transazioni a livello di metodo, la sicurezza a livello di metoto, i timer, i metodi remoti o i metodi asincroni.
E' facile iniziare con un Web Bean semplice e poi volgere a EJB semplicemente aggiungendo l'annotazione: @Stateless
, @Stateful
o @Singleton
.
Un metodo produttore è un metodo che viene chiamato dal manager Web Bean per ottenere un'istanza di un Web Bean quando non esiste alcuna istanza nel contesto corrente. Un metodo produttore lascia all'applicazione il pieno controllo del processo di istanziamento, invece di lasciare l'istanziamento al manager Web Bean. Per esempio:
@ApplicationScoped
public class Generator {
private Random random = new Random( System.currentTimeMillis() );
@Produces @Random int next() {
return random.nextInt(100);
}
}
Il risultato del metodo produttore è iniettato come qualsiasi altro Web Bean.
@Random int randomNumber
Il tipo di ritorno del metodo e tutte le interfacce che estende/implementa direttamente o indirettamente sono tipi di API del metodo produttore. Se il tipo di ritorno è una classe, tutte le superclassi sono anch'esse tipi di API.
Alcuni metodi produttori restituiscono oggetti che richiedono una distruzione esplicita:
@Produces @RequestScoped Connection connect(User user) {
return createConnection( user.getId(), user.getPassword() );
}
Questi metodi produttori possono definire corrispondenti metodi distruttori:"
void close(@Disposes Connection connection) {
connection.close();
}
Il metodo distruttore è chiamato direttamente dal manager Web Bean alla fine della richiesta.
Si parlerà in maggior dettaglio dei metodi produttori in Capitolo 6, Metodi produttori.
Infine una coda od un topic JMS possono essere Web Bean. Web Beans solleva lo sviluppatore dalla noia della gestione dei cicli di vita di tutti i vari oggetti JMS richiesto per inviare messaggi a code o topic. Si discuteranno gli endpoint JMS in Sezione 13.4, «Endpoint JMS».