SeamFramework.orgCommunity Documentation
Injeção de dependência possibilita baixo acoplamento permitindo que a implementação do bean type injetado alterne, seja durante a implantação ou em tempo de execução. Eventos vão mais a frente, permitem que beans interajam absolutamente com nenhuma dependência em tempo de compilação. Os produtores disparam eventos que são entregues a observadores de evento pelo contêiner.
Este esquema básico pode soar como o conhecido padrão observer/observable, mas existem algumas diferenças:
não só os produtores são desacoplados dos observadores; os observadores são totalmente desacoplados dos produtores,
os observadores podem especificar uma combinação de "seletores" para reduzir o conjunto de notificações de evento que irão receber, e
os observadores podem ser notificados imediatamente, ou podem especificar que a entrega do evento deve esperar o fim da transação corrente.
A funcionalidade de notificação de evento do CDI utiliza mais ou menos a mesma abordagem typesafe que já vimos no serviço de injeção de dependência.
O objeto de evento carrega estado do produtor para o consumidor. O objeto do evento é nada mais que uma instância de uma classe Java concreta. (A única restrição é que as classes de evento não podem conter variáveis de tipo). Um evento pode ter qualificadores atribuídos, permitindo que observadores o diferencie de outros eventos do mesmo tipo. Os qualificadores funcionam como seletores de tópico, possibilitando que um observador reduza o conjunto de eventos a observar.
Um qualificador de evento é apenas um qualificador normal, definido com @Qualifier
. Aqui vai um exemplo:
@Qualifier
@Target({FIELD, PARAMETER})
@Retention(RUNTIME)
public @interface Updated {}
Um método observador (observer method) é um método de um bean com um parâmetro anotado com @Observes
.
public void onAnyDocumentEvent(@Observes Document document) { ... }
O parâmetro anotado é chamado de parâmetro de evento. O tipo do parâmetro de evento é a classe de evento observada, neste caso Document
. O parâmetro de evento também pode especificar qualificadores.
public void afterDocumentUpdate(@Observes @Updated Document document) { ... }
Um método observador não precisa especificar qualificadores de evento—neste caso ele estará interessado em todos eventos de um tipo específico. Se ele especificar qualificadores, somente estará interessado em eventos que possuem estes qualificadores.
O método observador pode ter parâmetros adicionais, os quais são pontos de injeção:
public void afterDocumentUpdate(@Observes @Updated Document document, User user) { ... }
Os produtores disparam eventos utilizando uma instância da interface Event
parametrizada. Uma instância desta interface é obtida por injeção:
@Inject @Any Event<Document
> documentEvent;
Um produtor lança eventos chamando o método fire()
da interface Event
, passando o objeto de evento:
documentEvent.fire(document);
Este evento específico será entregue a todo método observador que:
tenha um parâmetro de evento em que o objeto de evento (o Document
) é especificado, e
não especifique qualquer qualificador.
O contêiner simplesmente chama todos os métodos observadores, passando o objeto de evento como valor do parâmetro de evento. Se algum método observador lançar uma exceção, o contêiner para de chamar os métodos observadores, e a exceção é relançada pelo método fire()
.
Os qualificadores podem ser aplicados a um evento em uma das seguintes formas:
anotando o ponto de injeção Event
, ou
passando qualificadores para o método select()
de Event
.
Especificar os qualificadores no ponto de injeção é muito mais simples:
@Inject @Updated Event<Document
> documentUpdatedEvent;
Em seguida, todos os eventos disparados por essa instância de Event
tem o qualificador de evento @Updated
. O evento será entregue a cada método observador que:
tenha um parâmetro evento em que o evento objeto é atribuído, e
não possua qualquer qualificador de evento exceto para os qualificadores de evento que coincidam com os especificados no ponto de injeção de Event
.
A desvantagem de anotar o ponto de injeção é que não podemos especificar o qualificador dinamicamente. CDI nos deixa obter uma instância de qualificador ao estender a classe auxiliadora AnnotationLiteral
. Deste modo podemos passar o qualificador para o método select()
de Event
.
documentEvent.select(new AnnotationLiteral<Updated
>(){}).fire(document);
Eventos podem ter vários qualificadores de evento, agregados usando qualquer combinação de anotações no ponto de injeção de Event
e passando instâncias qualificadoras ao método select()
.
Por padrão, se nenhuma instância de um observador existir no contexto atual, o contêiner instanciará o observador para entregar um evento a ele. Este comportamento não é sempre desejável. Podemos querer entregar eventos somente para instâncias do observador que já existam nos contextos atuais.
Um observador condicional é especificado ao adicionar receive = IF_EXISTS
na anotação @Observes
.
public void refreshOnDocumentUpdate(@Observes(receive = IF_EXISTS) @Updated Document d) { ... }
Um bean com escopo @Dependent
não pode ser um observador condicional, uma vez que ele nunca deveria ser chamado!
Um tipo qualificador de evento pode possuir anotações com membros:
@Qualifier
@Target({PARAMETER, FIELD})
@Retention(RUNTIME)
public @interface Role {
RoleType value();
}
O valor do membro é utilizado para reduzir as mensagens entregues ao observador:
public void adminLoggedIn(@Observes @Role(ADMIN) LoggedIn event) { ... }
Os membros do tipo qualificador de evento podem ser especificados estaticamente pelo produtor de evento, por meio de anotações no ponto de injeção do notificador de evento:
@Inject @Role(ADMIN) Event<LoggedIn
> loggedInEvent;
Alternativamente, o valor do membro do tipo qualificador de evento pode ser determinado dinamicamente pelo produtor de evento. Vamos começar escrevendo uma subclasse abstrata de AnnotationLiteral
:
abstract class RoleBinding
extends AnnotationLiteral<Role
>
implements Role {}
O produtor de evento passa uma instância desta classe para select()
:
documentEvent.select(new RoleBinding() {
public void value() { return user.getRole(); }
}).fire(document);
Os tipos qualificadores de evento podem ser combinados, por exemplo:
@Inject @Blog Event<Document
> blogEvent;
...
if (document.isBlog()) blogEvent.select(new AnnotationLiteral<Updated
>(){}).fire(document);
Os observadores devem satisfazer completamente o tipo do qualificador final do evento. Assuma os seguintes observadores neste exemplo:
public void afterBlogUpdate(@Observes @Updated @Blog Document document) { ... }
public void afterDocumentUpdate(@Observes @Updated Document document) { ... }
public void onAnyBlogEvent(@Observes @Blog Document document) { ... }
public void onAnyDocumentEvent(@Observes Document document) { ... }}}
O único observador a ser notificado será:
public void afterBlogUpdate(@Observes @Updated @Blog Document document) { ... }
No entanto, se houver também um observador:
public void afterBlogUpdate(@Observes @Any Document document) { ... }
Este também deveria ser notificado, uma vez que @Any
atua como um sinônimo para qualquer de todos qualificadores.
Observadores transacionais recebem notificações de eventos durante, antes ou após a conclusão da transação em que o evento foi disparado. Por exemplo: o seguinte método observador necessita atualizar um conjunto de resultados de uma consulta que está armazenada no contexto da aplicação, mas apenas quando as transações que atualizam a àrvore de Category
forem concluídas com sucesso:
public void refreshCategoryTree(@Observes(during = AFTER_SUCCESS) CategoryUpdateEvent event) { ... }
Existem cinco tipos de observadores transacionais:
observadores IN_PROGESS
são chamados imediatamente (padrão)
observadores AFTER_SUCCESS
são chamados durante a fase posterior à conclusão da transação, mas somente se a transação for concluída com sucesso
observadores AFTER_FAILURE
são chamados durante a fase posterior à conclusão da transação, mas somente se a transação não for concluída com sucesso
observadores AFTER_COMPLETION
são chamados durante a fase posterior à conclusão da transação
observadores BEFORE_COMPLETION
são chamados durante a fase anterior à conclusão da transação
Os observadores transacionais são muito importantes em um modelo de objetos stateful, porque o estado é muitas vezes mantido por mais de uma única transação atômica.
Imagine que fizemos cache do conjunto de resultados de uma consulta JPA no escopo de aplicação:
@ApplicationScoped @Singleton
public class Catalog {
@PersistenceContext EntityManager em;
List<Product
> products;
@Produces @Catalog
List<Product
> getCatalog() {
if (products==null) {
products = em.createQuery("select p from Product p where p.deleted = false")
.getResultList();
}
return products;
}
}
De tempos em tempos, um Product
é criado ou excluído. Quando isso ocorre, precisamos atualizar o catálogo de Product
. Mas devemos esperar até depois da transação ser concluída com sucesso antes de realizar essa atualização!
O bean que cria e remove Product
s pode lançar eventos, por exemplo:
@Stateless
public class ProductManager {
@PersistenceContext EntityManager em;
@Inject @Any Event<Product
> productEvent;
public void delete(Product product) {
em.delete(product);
productEvent.select(new AnnotationLiteral<Deleted
>(){}).fire(product);
}
public void persist(Product product) {
em.persist(product);
productEvent.select(new AnnotationLiteral<Created
>(){}).fire(product);
}
...
}
E agora Catalog
pode observar os eventos após a conclusão com sucesso da transação:
@ApplicationScoped @Singleton
public class Catalog {
...
void addProduct(@Observes(during = AFTER_SUCCESS) @Created Product product) {
products.add(product);
}
void removeProduct(@Observes(during = AFTER_SUCCESS) @Deleted Product product) {
products.remove(product);
}
}