A linguagem AspectJ é uma extensão da linguagem Java para apoiar a programação orientada a aspectos e é, portanto, uma linguagem de propósitos gerais. A linguagem foi desenvolvida no centro de Pesquisa da Xerox (Kiczales et al., 1997, 2001a), em Palo Alto, e posteriormente foi agregada ao projeto Eclipse da IBM.
Na programação orientada a aspectos é introduzido o conceito de aspectos, que implemen- tam os interesses transversais do sistema e adicionam comportamento ao código-base (classes, por exemplo) do sistema. Para permitir a implementação de aspectos, o AspectJ introduz novas construções, tais como: os aspectos (aspect); os conjuntos de pontos de junção (pointcut) que iden- tificam pontos de junção (join points); e os adendos (advices) que implementam o comportamento a ser adicionado ao ponto de junção. Além disso, pode-se definir atributos e métodos que alteram a estrutura estática das classes afetadas pelo aspecto, e por isso chamadas de declarações inter-tipos (intertype declarations). AspectJ também permite a alteração da hierarquia das classes afetadas pelo aspecto e a declaração de hierarquias entre os aspectos, por meio de aspectos especializados.
Um exemplo da implementação de um aspecto de rastreamento pode ser visto na Figura 3.3. As construções introduzidas pelo AspectJ são detalhadas e exemplificadas nos tópicos a seguir, bem como a descrição sucinta de como os aspectos e classes são combinados para formar o programa final.
public aspect Rastreamento {
public pointcut metodos(): execution (* *.*(..)) && !execution (* *.set*(..)) && !execution (* *.get*(..)); before():metodos()
{
System.out.println("Entrou no método: " + thisJoinPoint.toString()); }
after():metodos() {
System.out.println("Saiu do método: " + thisJoinPoint.toString()); }
}
Figura 3.3: Implementação de Rastreamento em AspectJ
3.2.1.1 - Conjunto de Pontos de Junção
Os conjuntos de ponto de junção são utilizados para identificar um conjunto de pontos bem definidos no fluxo de execução de um programa, em que os comportamentos adicionais inseridos
pelos aspectos serão executados. Esses pontos bem definidos no fluxo de execução do programa são chamados de pontos de junção e podem ser construtores de classes, chamadas e execução de métodos, acesso a atributos, etc. Na Tabela 3.1 são mostrados os tipos de pontos de junção do AspectJ e suas respectivas sintaxes.
Tipo Sintaxe
Execução de método execution(AssinaturaDeMétodo)
Chamada a método call(AssinaturaDeMétodo)
Execução de construtor execution(AssinaturaDeConstrutor) Chamada a construtor call(AssinaturaDeConstrutor)
Iniciação de classe staticinicialization(AssinaturaDeTipo) Acesso de leitura de atributo get(AssinaturaDeAtributo) Acesso de modificação de atributo set(AssinaturaDeAtributo) Execução de tratador de exceção handler(AssinaturaDeTipo)
Iniciação de objeto initialization(AssinaturaDeConstrutor) Pré-iniciação de objeto preinitialization(AssinaturaDeConstrutor)
Execução de advices adviceexecution()
Tabela 3.1: Tipos de pontos de junção do AspectJ e suas respectivas sintaxes (adaptado de Laddad (2003))
Os conjuntos de pontos de junção podem identificar um único ponto de junção ou a composição de vários deles, usando operadores lógicos como && (e), || (ou), além de operadores unários como o ! (negação). Os conjuntos de junção podem ser identificados na própria declaração dos adendos ou serem identificados por um nome e posteriormente podem ser referidos por esse mesmo nome na declaração dos adendos.
No código mostrado na Figura 3.3, o conjunto de pontos de junção recebe o nome de metodos e identifica os pontos de junção a serem entrecortados. O ponto de junção execution(* *.*(..)) determina que quando qualquer método, com qualquer parâmetro, de qualquer classe e de qualquer tipo for executado (execution), esses métodos serão entrecortados e um com- portamento adicional será executado. Os pontos de junção !execution(* *.set*(..)) e !execution(* *.get*(..)) definem que os métodos que começam com get e set de qualquer tipo, de qualquer classe e de qualquer parâmetro não serão entrecortados. Mais deta- lhes sobre como designar os conjuntos de ponto de junção podem ser encontrados nos manuais do AspectJ (Team, 2003).
3.2.1.2 - Adendos
Os adendos são implementações de comportamento que são executados quando o aspecto en- trecortar a classe nos pontos de junção determinados pelo designador dos conjuntos de ponto de junção. Os adendos podem ser de três tipos básicos: anteriores (before), que são executados antes do ponto de junção ser executado; os posteriores (after), que são executados depois do ponto de
junção ser executado; e os de contorno (around), que são executados no lugar do ponto de junção, sendo que após serem executados podem ou não chamar a execução do ponto de junção por um método denominado de proceed().
Os adendos são construções semelhantes aos métodos, entretanto não podem ser chamados diretamente pela aplicação base e nem pelo próprio aspecto, pois sua execução é feita automatica- mente após o entrecorte no ponto de junção. Uma outra diferença em relação aos métodos é que os adendos não possuem nome, não têm especificadores de acesso (public, private, etc) e tem acesso a variáveis especiais das execuções dos pontos de junção, como assinatura dos métodos entrecortados, etc.
No código do exemplo da Figura 3.3, os adendos before e after simplesmente implemen- tam uma linha de código que escreve uma indicação de que o fluxo de execução do programa está iniciando ou finalizando a execução do método entrecortado. Os adendos adicionam comporta- mento aos pontos de junção identificados em metodos(), pois o conjunto de ponto de junção foi nomeado e está referenciado na declaração do adendo. Outra maneira de se identificar os pon- tos de junção que serão entrecortados é mostrado na Figura 3.4, em que os pontos de junção são identificados na própria declaração do adendo.
before(): {
System.out.println("Entrou no método: " + thisJoinPoint.toString()); }
execution (* *.*(..)) && !execution (* *.set*(..)) && !execution (* *.get*(..));
Figura 3.4: Implementação de um adendo identificando os pontos de junção a entrecortar
3.2.1.3 - Declarações inter-tipos
As declarações inter-tipo permitem a introdução de novos atributos e métodos nas classes bási- cas do programa. Isso é feito simplesmente pela declaração de atributos e métodos no próprio aspecto, com esses novos métodos podendo ser acessados diretamente pelas classes afetadas. O aspecto mostrado na Figura 3.5, introduz na classe Servidor o atributo enable que é ativado e desativado dependendo do método executado em Servidor..
3.2.1.4 - Processo de Combinação
No processo de combinação entre aspectos e classes, o compilador AspectJ transforma os as- pectos em implementações semelhantes às classes, com os adendos se tornando semelhantes aos métodos e os aspectos às classes. Diante disso, o combinador identifica os possíveis pontos de junção das classes afetadas pelos aspectos e insere uma chamada ao método do aspecto (adendo)
aspect controleServidor {
static boolean Servidor.enable=true;
after(): execution(void Servidor.close(..)) {
enable = false; }
before(): execution(void Servidor.open(..)) {
enable = true; }
}
Figura 3.5: Exemplo de declaração inter-tipos
antes, depois ou no lugar do ponto de junção, de acordo com o adendo. Esse processo é feito diretamente no bytecode.