ago

11

Spring Framework Parte 2 -> Aspect Oriented Programming

By Felipe Saab

Considerações iniciais: se vc veio ler este post e ainda não leu o post anterior (Spring Framework Parte 1 -> Dependency Injection), por favor o faça! Caso contrário você pode não entender os códigos utilizados aqui.

Beleza, daqui pra frente vou assumir que você já sabe como linkar os beans no Spring utilizando o arquivo de configuração (pelo menos o básico, como visto na Parte 1 desta série).

Vamos ao nosso post então: AOP: Aspect Oriented Programming (POA: Programação Orientada a Aspectos). E como sempre, pra fugir um pouco dos termos técnicos, vamos a uma história:

Imagine que você está desenvolvendo um e-commerce, atualmente está desenvolvendo o checkout do teu sistema, ou seja, o cliente já escolheu tudo o que ele queria, e agora vai fazer a transferência eletrônica do pagamento (vamos imaginar que você está desenvolvendo tudo isso). Olhando com um nível de abstração muito alto o seu código poderia ser algo assim:

public class CheckoutService {
    //...
    public void efetuarPagamento(Carrinho carrinho){
        LOGGER.log(new Date().toString() + " - Executando efetuarPagamento(Carrinho carrinho)");
        if (carrinho.getCliente().temPermissao()){
             DAO.iniciarTransacao();
             try {
                 carrinho.getCliente().debitar(carrinho.getValorTotal());
                 carrinho.finalizarCompra();
                 DAO.commit();
                 LOGGER.log(new Date().toString() + " - Pagamento efetuado!");
             } catch (Exception e) {
                 DAO.rollback();
                 LOGGER.log(new Date().toString() + " - " + e.toString());
             }
        } else {
            LOGGER.log(new Date().toString() + "Cliente não está autenticado");
        }
    }
    //...
}

Dê uma boa olhada no código… O que tem a ver mesmo com o checkout são apenas as linhas:

carrinho.getCliente().debitar(carrinho.getValorTotal());
carrinho.finalizarCompra();

O resto é código responsável por logging, controle de transação, permissão de acessos, coisas que são necessárias porém que ocupam muito espaço, “sujam” o código. A idéia da orientação a objetos é encapsular em um objeto todas as coisas referentes a ele, os serviços que ele vai prestar, e não ficar poluindo ele com outros códigos que não tem nada a ver com o seu propósito.

Mas esses códigos são necessários para a aplicação funcionar corretamente! O que fazer então? Orientação a Aspectos!!!

A Orientação a Aspectos surgiu exatamente para eliminar os cross-cutting concerns (código que é necessário, porém foge das preocupações principais do objeto, como no caso acima, o checkout tem que se preocupar com o logging, transação e permissão).

OBS: Existem vários termos técnicos, sem graça, e difíceis de entender envolvidos nesse “paradigma” de programação. Nós vamos discutindo-os conforme o tutorial for andando.

Então como eu faço para separar esse código chato dos meus objetos? A Orientação a Aspectos funciona assim:

-> Primeiro a gente separa o código que não tem nada a ver com o nosso objeto (logging, transação, permissão e tals) em um lugar separado. Esse lugar separado se chama advice (é o o que).

-> Ainda dentro do advice, é definido onde o programa tem que dar uma “paradinha” para que o código que foi separado possa ser executado. Todo lugar que o programa pode dar essa “paradinha” é chamado de join point. Um join point pode ser por exemplo: uma chamada de método, a execução de um método, instanciação de objetos, execução de construtores, e mais alugns (é o quando).

-> Uma vez que nós já sabemos o que vai ser feito (advice) e quando vai ser feito (join point), falta definir onde vai ser feito. Para isso servem os pointcuts. Geralmente são definidos utilizando os nomes das classes e métodos que queremos interceptar, mas também podem ser definidos através de expressões regulares (logo mais veremos um exemplo na prática).

-> Por fim, o conjunto de tudo isso (o que vai ser feito, quando vai ser feito e onde vai ser feito) é chamado de aspect (aspecto).

Em muito poucas palavras isso é a base da orientação a aspectos. Vamos ver como a gente pode utilizar isso no Spring.

O Spring oferece diversos tipos de suporte a Orientação a Aspectos (eles foram evoluindo através das versões), dentre eles o suporte para a orientação a aspectos declarativa (no XML de configuração) e anotativa (utilizando annotations (se não sabe o que são annotations clique aqui)).

Eu estudei as duas e devo dizer que me identifiquei muito mais com a orientação a aspectos utilizando annotations, portanto é ela que eu irei mostrar aqui (caso queira aprender sobre o outro tipo eu aconselho fortemente o livro Spring in Action 2 (Manning, 2008), capítulo 4).

OBS: Eu irei reaproveitar o código da Parte 1, então caso você não tenha feito o tutorial anterior eu aconselho a pelo menos pegar os código de lá. Tudo o que eu fiz foi renomear o projeto para “OrientacaoAspectos”, o pacote principal para “orientacaoaspectos” e todas as definições dos beans no arquivo de configuração para bater com o novo nome do pacote.
Como o suporte de orientação a aspectos do Spring consegue usar a poderosa linguagem do AspectJ (para definir pointcuts) nós vamos usá-la aqui, para tanto é necessário o download da biblioteca do AspectJ e a inclusão dela no classpath. O arquivo pode ser baixado no site do projeto.
Só mais um detalhe: o arquivo que vc baixa do site eh um .jar chamado: aspectj-x.x.x.jar, dentro dele é que estão as bibliotecas que o Spring precisa, então abra este arquivo com o WinRAR (ou outro do gênero) navegue até a pasta /lib, extraia os .jar que estão lá e os adicione ao classpath.

Voltando ao escopo da nossa aplicação: o show de talentos Ídolos Spring, vamos adicionar um requisito muito importante para qualquer programa de entrenimento: a platéia!

Sem utilizar a orientação a aspectos nós teríamos os seguintes códigos:

public class Plateia {
 
    public Plateia() { }
 
    public void sentar(){
        System.out.println("A platéia está sentando");
    }
 
    public void desligarCelulares(){
        System.out.println("A platéia está desligando os celulares");
    }
 
    public void aplaudir(){
        System.out.println("CLAP CLAP CLAP UHULL CLAP CLAP");
    }
 
    public void vaiar(){
        System.out.println("UUUUUU!! E FORA! E FORA! UUUUU!! ");
    }
}
public class Cantor implements Competidor {
 
    private Musica musica;
    private Plateia plateia;
 
    public Cantor() {
    }
 
    public void setMusica(Musica musica) {
        this.musica = musica;
    }
 
    public void setPlateia(Plateia plateia) {
        this.plateia = plateia;
    }
 
    public void apresentar() {
        plateia.sentar();
        plateia.desligarCelulares();
 
        try {
            musica.cantar();
            plateia.aplaudir();
        } catch (Exception e) {
            plateia.vaiar();
        }
    }
}

OBS: A platéia estaria sendo injetada na classe Cantor pelo container assim como está acontecendo com a musica.

Sem a orientação a aspectos, toda classe que implemente a interface Competidor tem que ficar se preocupando com a platéia. Não é certo o competidor ficar mostrando os assentos para a platéia, pedir para elas desligarem os celulares, se apresentar e por fim pedir para eles aplaudirem ou vaiarem. Isso é coisa que a platéia tem que fazer sozinha!

Vamos utilizar todo o poder da orientação a aspectos para que a platéia consiga fazer isso sozinha. A classe Cantor volta a ser o que era antes, se preocupando somente em cantar a sua musica:

public class Cantor implements Competidor {
 
    private Musica musica;
 
    public Cantor() {
    }
 
    public void setMusica(Musica musica) {
        this.musica = musica;
    }
 
    public void apresentar() {
        musica.cantar();
    }
}

E a classe Plateia vai ser “anotada” para utilizar as funcionalidades da orientação a aspectos:

@Aspect
public class Plateia {
 
    public Plateia() { }
 
    @Pointcut("execution(* *.apresentar(..))")
    public void apresentacao() {}
 
    @Before("apresentacao()")
    public void sentar(){
        System.out.println("A platéia está sentando");
    }
 
    @Before("apresentacao()")
    public void desligarCelulares(){
        System.out.println("A platéia está desligando os celulares");
    }
 
    @AfterReturning("apresentacao()")
    public void aplaudir(){
        System.out.println("CLAP CLAP CLAP UHULL CLAP CLAP");
    }
 
    @AfterThrowing("apresentacao()")
    public void vaiar(){
        System.out.println("UUUUUU!! E FORA! E FORA! UUUUU!! ");
    }
}

Aconteceram algumas coisas aqui:

-> Primeiro de tudo, a classe foi anotada com a anotação @Aspect indicando o óbvio: esta classe será um aspecto;

-> Depois foi criado um método que não faz absolutamente nada chamado apresentacao() cujo único propósito deste método é ser anotado para se “tornar” um pointcut (“tornar” entre aspas porque apresentar() continua sendo um método que não faz nada, apenas contém uma anotação que é um pointcut).
O parâmetro passado para a anotação @Pointcut é uma frase na linguagem de descrição de pointcuts do AspectJ. Segue uma pequena explicação dela (que eu acabei de fazer no Paint!! hehehe):

-> Depois que o pointcut está definido, basta anotar os métodos desejados especificando quando e onde eles devem ser executados. Para especificar onde os métodos serão executados é utilizado o nome do método que está anotado com o pointcut (no nosso exemplo é a string “apresentacao()”) e para especificar quando existem algumas outras anotações. Creio que as mais utilizadas são: @Before, @AfterReturning e @AfterThrowing (caso queira saber quais são as outras, fique a vontade para fuçar na documentação).

E voilá, nosso aspecto está pronto! Para ele funcionar só falta mais uma coisinha, que não é tão pequena assim (pelo menos na teoria, na prática é sim! :D ).

Para a orientação a aspectos funcionar devem ser criadas cópias dos objetos que queremos interceptar. Tais cópias contém os advices (códigos das cross-cut concerns) e esse objeto é que recebe as requisições, executa os advices e depois passa a requisição para o método do objeto original. Essas cópias de objetos que contém os advices são chamados de Proxy (no plural: Proxies).

E até algumas versões atrás os proxies tinham que ser criados na mão!!! Imagine que trabalho que dava… Porém o Spring provê um mecanismo automático para a criação dos proxies (ufa!).

Segue o arquivo de configuração para podermos fazer nosso exemplo funcionar:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
 
       <!-- competidores -->
       <bean id="atireiopau" class="orientacaoaspectos.AtireiOPauNoGato" />
       <bean id="jose" class="orientacaoaspectos.Cantor">
           <property name="musica" ref="atireiopau" />
       </bean>
 
       <bean id="ciranda" class="orientacaoaspectos.CirandaCirandinha" />
       <bean id="gaita" class="orientacaoaspectos.Gaita" />
 
       <bean id="piano" class="orientacaoaspectos.Piano" />
       <bean id="carlos" class="orientacaoaspectos.CantorTocador">
           <property name="musica" ref="atireiopau" />
           <property name="instrumento" ref="piano" />
       </bean>
 
       <!-- platéia -->
       <bean class="orientacaoaspectos.Plateia" />
       <aop:aspectj-autoproxy />
</beans>

Três coisas novas apareceram no arquivo de configuração:

1 – Na root tag () foi definido mais um namespace (xmlns:aop) e na localização dos esquemas estão os seus respectivos links. Isso é necessário para o arquivo de configuração saber quais tags podem e quais não podem quando o assunto é AOP;

2 – Foi declarado o bean correspondente à platéia. Ele não tem o atributo id definido porque provavelmente nós nunca precisaremos de uma referência à platéia, eles fazem tudo sozinhos! Não tem porque darmos um id para a platéia sendo que nunca iremos utilizá-la;

3 – A simples tag “< aop:aspectj-autoproxy />” já diz para o Spring fazer todo o trabalho duro de ficar criando os proxies para nós.

E é isso caro leitor(a) (será que mulher le isso? to brincando!! hehehe), agora é só compilar (in the java-way of course), pedir pra tia VM executar e ver a platéia trabalhando sozinha, que povo educado! :D

Saindo da historinha e voltando ao mundo real, é extremamente fácil prover um mecanismo de log simples e que pode sofrer modificações facilmente utilizando a programação orientada a aspectos. Suponha que sua aplicação utilize o mecanismo padrão de log do Java e queira mudar para o Log4J: simples! Basta ir no seu aspecto responsável pelo log, e alterar os códigos. Você só tem trabalho uma vez (e nem é tanto trabalho assim)!

Nos próximos posts veremos os mecanismos que o Spring provê para banco de dados, transações, segurança, e mais alguns. Tão fácil quanto um System.out.println(“Hello World”).

Quem quiser o código do projeto, está aqui.

[]s e até a próxima,
Saab.

20 Responses so far

Parabéns ! Excelênte didática.

Boa tarde, muito bom o tutorial, consegui fazer a parte 1, mas na parte 2 está dando o erro: Error creating bean with name ‘atireio pau’ defined in class path resource [conf/idolos.xml]: Initialization of bean failed; nested exception is java.lang.IllegalStateException: Must set property ‘expression’ before attempting to match.

Se puder me ajudar, eu agradeço.

No comentário a cima, eu coloquei ‘atireio pau’, mas no código está correto, conforme você informou no tutorial. ‘atireiopau’.

José, quando o assunto é Orientação a Aspectos você tem que triplicar o cuidado quando escreve as expressões. Dê uma conferida letra por letra no pointcut e nas demais anotações, creio que esse deva ser teu problema…

Parabens cara!! ta me ajudando mto esse tutorial!! sempre quis aprender Spring!! Continue assim!!

Valeu!!

Parabens! show de bola!

Muito Bom!

Cara! Voce deveria ser professor.

Se já não for professor, recomendo que o faça logo!

iuahiauhaiuhaiuahiau
Obrigado pelo feedback.
Fico mto feliz de que tenham gostado. :)

Cara , parabens … seus posts são os melhores

Parabéns pela didática do seu texto. Estou produzindo 2 livros com a mesma didática que você usa para o pessoal que depende de um bom livro de Estatística e Calculo. Continue assim!! Abraços!!

Acabei de ler o primeiro e agora li o segundo post, eu não fazia idéia de como funcionava orientação a aspecto, obrigadão!!!
Leitura muito agradavel

Parabéns muito bem explicado e muito fácil de entender. Primeiro tutorial que entendo como funciona isso hehehe.Valeu!!!

Gostei do post, deu pra entender a base da coisa… Agora da pra pelo menos praticar e pegar melhor o entendimento do assunto.
Valeu, abraço!

Muito bem explicado!
Eu nunca tinha conseguido uma explicação tão boa para orientação a aspectos!
Seu post foi um achado! Estava procurando sobre Hibernate Template e vim parar aqui..
Parabéns!
PS- Mulher também lê isso, viu? rsss
Abraços!

Obrigado pessoal, fico muito feliz em saber que o post está ajudando vocês a entenderem o assunto =]
Fico feliz em saber que mulher lê isso também Thaís.. hahahahaha

Só pra constar, mais uma mulher!!

Muito fácil de entender, parabéns e obrigada por compartilhar o conhecimento de uma forma tão bem aplicada!

[]‘s

Legal. Parabéns. Consegui pegar a questão do Aspecto e notei uma semelhança com as anotações dos testes do JUnit.

Cara, muito bom mesmo. Você está levando mesmo a sério o nome do site.
Parabéns.

Leave a comment