@Log public void aMethod(){...}
CDI Interceptors
Vamos escrever uma aplicação simples que nos permite comprar entradas para um filme. Como toda aplicação, log é uma das questões transversais que nós temos.
Alem disso, ha alguns métodos na nossa aplicação que só podem ser acessados no horário de trabalho. Se acessados fora do horário nos vamos lançar um AccessDeniedException
.
Como podemos marcar quais métodos devem ser interceptados? Não seria conveniente anotar um método desta forma:
ou
@TimeRestricted public void bMethod(){...}
Vamos criar essas anotações que vão "marcar" um método para interceptação.
@InterceptorBinding @Target({ TYPE, METHOD }) @Retention(RUNTIME) public @interface Log { }
e
@InterceptorBinding @Target({ TYPE, METHOD }) @Retention(RUNTIME) public @interface TimeRestricted { }
Garanta que voce não esqueceu a anotação @InterceptorBinding
acima! Agora nossas anotações personalizadas estão criadas, vamos anexa-las (ou vincula-las) aos interceptores.
Aqui esta nosso interceptor de log. Um método @AroundInvoke
e estamos quase terminando.
@Interceptor @Log //Vinculando com o interceptador aqui. Agora todo método anotado com @Log ira ser interceptado pelo logMethodEntry public class BookForAShowLoggingInterceptor implements Serializable { private static final long serialVersionUID = 8139854519874743530L; private Logger logger = Logger.getLogger("BookForAShowApplicationLogger"); @AroundInvoke public Object logMethodEntry(InvocationContext ctx) throws Exception { logger.info("Before entering method:" + ctx.getMethod().getName()); InterceptionOrderTracker.getMethodsInterceptedList().add(ctx.getMethod().getName()); InterceptionOrderTracker.getInterceptedByList().add(this.getClass().getSimpleName()); return ctx.proceed(); } }
Agora a anotação @Log
que nos criamos esta vinculada a esse interceptador. (Da mesma forma nos vinculamos @TimeRestrict
para TimeBasedRestrictingInterceptor
. Veja no código)
Tudo pronto, vamos anotar a nível de classe ou método e se divertir interceptando!
@Log @Stateful public class BookForAShowOneInterceptorApplied implements Serializable { private static final long serialVersionUID = 6350400892234496909L; public List<String> getMoviesList() { List<String> moviesAvailable = new ArrayList<String>(); moviesAvailable.add("12 Angry Men"); moviesAvailable.add("Kings speech"); return moviesAvailable; } public Integer getDiscountedPrice(int ticketPrice) { return ticketPrice - 50; } // Suponha que existam mais métodos }
A anotação @Log
aplicada em nível de classe denota que todos os métodos serão interceptados com BookForAShowLoggingInterceptor
.
Antes de dizer-mos "tudo pronto" tem apenas uma coisa que precisamos fazer! Habilitar os interceptadores!
Vamos criar rapidamente um arquivo beans.xml
no caminho src/main/resources/META-INF/beans.xml
:
<beans> <interceptors> <class>org.superbiz.cdi.bookshow.interceptors.BookForAShowLoggingInterceptor </class> <class>org.superbiz.cdi.bookshow.interceptors.TimeBasedRestrictingInterceptor </class> </interceptors> </beans>
Por padrão, um arquivo de bean não tem interceptadores habilitados; Um interceptador deve ser explicitamente habilitado para "escutar" sua classe no arquivo beans.xml
.
A ordem da anotação que intercepta não importa.
Exemplo:
@TimeRestrict @Log void cMethod(){}
Existe o elemento interceptors
no bean.xml
não apenas para "habilitar" os interceptadores, mas também define a "ordem de execução" deles. Nesse caso BookForAShowLoggingInterceptor
vai ser aplicado primeiro e depois TimeBasedRestrictingInterceptor
Agora você já sabe que a ordem somente é determinada pelo elemento interceptors
no beans.xml
. A regra é clara, interceptadores que aparecem antes na lista são chamados primeiro.
Perceba também que um método pode ser marcado para interceptação utilizando vários interceptors apenas adicionando a anotação acima.
Isso traz outra questão. No caso acima nós temos dois interceptadores aplicados juntos. Mas e se nos quisermos algo como 4 interceptadores? Isso vai ir longe…. Ter tantas anotações não deixa meu código feio?
Não se preocupe! Apenas crie uma anotação que herda das outras
@Inherited @InterceptorBinding @Target({ TYPE, METHOD }) @Retention(RUNTIME) @Log @TimeRestricted public @interface TimeRestrictAndLog { }
Este interceptador faz herança.
O código abaixo demonstra a maioria dos casos que nos discutimos.
Não esqueça que forma antiga de binding com @Interceptors(WhicheverInterceptor.class)
também é suportada. De uma olhada em BookForAShowOldStyleInterceptorBinding
onde os comentários explicam como a maneira mais nova (que conversamos acima) é melhor.
O código
BookForAShowOneInterceptorApplied
BookForAShowOneInterceptorApplied
mostra apenas um simples interceptador de @Log
sendo aplicado.
package org.superbiz.cdi.bookshow.beans;
import org.superbiz.cdi.bookshow.interceptorbinding.Log;
import jakarta.ejb.Stateful;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@Log
@Stateful
public class BookForAShowOneInterceptorApplied implements Serializable {
private static final long serialVersionUID = 6350400892234496909L;
public List<String> getMoviesList() {
List<String> moviesAvailable = new ArrayList<String>();
moviesAvailable.add("12 Angry Men");
moviesAvailable.add("Kings speech");
return moviesAvailable;
}
public Integer getDiscountedPrice(int ticketPrice) {
return ticketPrice - 50;
}
}
BookForAShowTwoInterceptorsApplied
BookForAShowTwoInterceptorsApplied
mostra ambos @Log
e @TimeRestricted
sendo aplicados.
package org.superbiz.cdi.bookshow.beans;
import org.superbiz.cdi.bookshow.interceptorbinding.Log;
import org.superbiz.cdi.bookshow.interceptorbinding.TimeRestricted;
import jakarta.ejb.Stateful;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@Log
@Stateful
public class BookForAShowTwoInterceptorsApplied implements Serializable {
private static final long serialVersionUID = 6350400892234496909L;
public List<String> getMoviesList() {
List<String> moviesAvailable = new ArrayList<String>();
moviesAvailable.add("12 Angry Men");
moviesAvailable.add("Kings speech");
return moviesAvailable;
}
@TimeRestricted
public Integer getDiscountedPrice(int ticketPrice) {
return ticketPrice - 50;
}
}
BookShowInterceptorBindingInheritanceExplored
BookShowInterceptorBindingInheritanceExplored
mostra como @TimeRestrictAndLog
(interceptor-binding-inheritance) pode ser usado como uma alternativa em vez de anotar o método com muitas anotações explicitamente.
package org.superbiz.cdi.bookshow.beans;
import org.superbiz.cdi.bookshow.interceptorbinding.TimeRestrictAndLog;
import jakarta.ejb.Stateful;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@Stateful
public class BookShowInterceptorBindingInheritanceExplored implements Serializable {
private static final long serialVersionUID = 6350400892234496909L;
public List<String> getMoviesList() {
List<String> moviesAvailable = new ArrayList<String>();
moviesAvailable.add("12 Angry Men");
moviesAvailable.add("Kings speech");
return moviesAvailable;
}
@TimeRestrictAndLog
public Integer getDiscountedPrice(int ticketPrice) {
return ticketPrice - 50;
}
}