Spring Framework Parte 2 -> Aspect Oriented Programming
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!
).
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 (
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!
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
Samuel
novembro 26th, 2010
17:15
Parabéns ! Excelênte didática.
José Ricardo
janeiro 14th, 2011
17:20
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.
José Ricardo
janeiro 14th, 2011
17:26
No comentário a cima, eu coloquei ‘atireio pau’, mas no código está correto, conforme você informou no tutorial. ‘atireiopau’.
Saab
janeiro 14th, 2011
20:29
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…
Daniel
fevereiro 14th, 2011
15:45
Parabens cara!! ta me ajudando mto esse tutorial!! sempre quis aprender Spring!! Continue assim!!
Valeu!!
Danilo
fevereiro 19th, 2011
9:42
Parabens! show de bola!
denner
fevereiro 20th, 2011
1:38
Muito Bom!
Gabriel
março 29th, 2011
11:57
Cara! Voce deveria ser professor.
Gabriel
março 29th, 2011
11:58
Se já não for professor, recomendo que o faça logo!
Saab
março 29th, 2011
20:35
iuahiauhaiuhaiuahiau
Obrigado pelo feedback.
Fico mto feliz de que tenham gostado.
BRomero
março 31st, 2011
16:45
Cara , parabens … seus posts são os melhores
Thiago
abril 28th, 2011
16:36
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!!
felipe
maio 6th, 2011
1:13
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
Daniel
maio 24th, 2011
19:36
Parabéns muito bem explicado e muito fácil de entender. Primeiro tutorial que entendo como funciona isso hehehe.Valeu!!!
Vinicius
agosto 3rd, 2011
11:23
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!
Thaís
agosto 16th, 2011
17:38
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!
Saab
agosto 16th, 2011
19:30
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
Laydy
agosto 19th, 2011
20:02
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
Edson
outubro 8th, 2011
17:48
Legal. Parabéns. Consegui pegar a questão do Aspecto e notei uma semelhança com as anotações dos testes do JUnit.
Gilluan
outubro 11th, 2011
9:07
Cara, muito bom mesmo. Você está levando mesmo a sério o nome do site.
Parabéns.
Leave a comment