AspectJ - Programação Orientada a Aspectos em Java

Propaganda
AspectJ - Programação
Orientada a Aspectos em Java
Sérgio Soares
Centro de Informática
Universidade Federal de Pernambuco
Programação Orientada a
Objetos
„
„
Lida com conceitos mais intuitivos
Permite ganhos
• Reuso
• Manutenção
• Adaptação
„
Padrões de projetos
• Auxiliam a POO
Exemplo:
Sistema Disque Saúde
„
Um sistema de informação
• Registra e encaminha queixas para o sistema de
saúde
• Exibe informações sobre unidades de saúde e
suas especialidades
„
Requisitos não-funcionais
• Distribuído
• Acesso concorrente
• Extensível
—Armazenamento de dados
—Tecnologia de distribuição
Exemplo:
Sistema Disque Saúde
„
Implementado em Java
• Servlets implementam a GUI
• Utiliza vários padrões de projetos para
contemplar requisitos não-funcionais
„
Como implementar a distribuição no
sistema?
Disque Saúde local
Disque Saúde distribuído com
RMI
public class Complaint implements java.io.Serializable {
public class HealthWatcherFacade implements IFacade {
private String description;
public class ServletUpdateComplaintData extends HttpServlet {
public void update(Complaint complaint)
private Person complainer; ...
private IFacade facade;
throws TransactionException, RepositoryException,
public Complaint(String description, Person complainer,
...) {
public void init(ServletConfig config) throws ServletException {
ObjectNotFoundException, ObjectNotValidException {
try {
...
...
facade = (IFacade) java.rmi.Naming.lookup("//HealthWatcher");
}
}
}
public static void main(String[] args) {
public String getDescription() {
catch (java.rmi.RemoteException rmiEx) {...}
try {
return this.description;
catch (java.rmi.NotBoundException rmiEx) {...}
HealthWatcherFacade facade = HealthWatcherFacade.getInstance();
}
catch (java.net.MalformedURLException rmiEx) {...}
System.out.println("Creating RMI server...");
public Person getComplainer() {
}
UnicastRemoteObject.exportObject(facade);
return this.complainer;
java.rmi.Naming.rebind("/HealthWatcher");
}
System.out.println("Server created and ready.");
public void setDescription(String desc) {
this.description = desc;
}
public void setComplainer(Person complainer) {
throws ServletException, IOException {
catch (RemoteException rmiEx) {... }
...
catch (MalformedURLException rmiEx) { ...}
facade.update(complaint);
catch(Exception ex) {... }
this.complainer = complainer;
} ...
public void doPost(HttpServletRequest request, HttpServletResponse response)
}
...
}
} ...
}
}
}
public class Person implements java.io.Serializable {
public interface IFacade extends java.rmi.Remote {
private String nome; ...
public Person(String nome, …) {
public void updateComplaint complaint)
this.nome = nome; …
throws TransactionException, RepositoryException,
}
ObjectNotFoundException, ObjectNotValidException,
public String getNome() {
RemoteException;
return nome;
}…
...
}
}
Código RMI é vermelho…
Implementação OO: problemas!
„
Tangled code (código entrelaçado)
• código de distribuição misturado com
código de negócio e de GUI
„
Spread code (código espalhado)
• código de distribuição em várias classes
• distribuição é um crosscutting concern
„
Difícil de manter e reusar
• mudanças no protocolo de distribuirão
(RMI, CORBA, EJB ) são invasivas
AOP — Aspect-oriented
programming
„
Melhora a modularidade de
crosscutting concerns
• distribuição, gerenciamento de dados,
controle de concorrência, tratamento de
exceções, logging, debugging, …
„
Auxilia separation of concerns
• Aumenta extensibilidade e reuso
Disque Saúde com AOP
public class Complaint {
private String description;
private Person complainer; ...
public class ServletUpdateComplaintData extends HttpServlet {
public Complaint(String description, Person complainer,
...) {
aspect DistributionAspect {
private HealthWatcherFacade facade;
declare parents: HealthWatcherFacade implements IFacade;
public void init(ServletConfig config) throws ServletException {
declare parents: Complaint || Person implements java.io.Serializable;
...
try {
}
public static void HealthWatcherFacade.main(String[] args) {
facade = HealthWatcherFacade.getInstance();
public String getDescription() {
return this.description;
}
try {
}
HealthWatcherFacade facade = HealthWatcherFacade.getInstance();
catch (Exception ex) {...}
System.out.println("Creating RMI server...");
}
public Person getComplainer() {
return this.complainer;
UnicastRemoteObject.exportObject(facade);
public void doPost(HttpServletRequest request, HttpServletResponse
response)
}
java.rmi.Naming.rebind("/HealthWatcher");
System.out.println("Server created and ready.");
throws ServletException, IOException {
public void setDescription(String desc) {
...
this.description = desc;
}
} catch (RemoteException rmiEx) {...}
catch (MalformedURLException rmiEx) {...}
} ...
catch(Exception ex) {...}
}
}
public void setComplainer(Person complainer) {
this.complainer = complainer;
}
}
public class Person {
private String nome; ...
public Person(String nome, ...) {
this.matricula = matricula; ...
}
public String getNome() {
return nome;
private IFacade remoteFacade;
Sistema local
pointcut facadeMethodsExecution():
within(HttpServlet+) && execution(* HealthWatcherFacade.*(..)) &&
this(HealthWatcherFacade);
before(): facadeMethodsExecution() { prepareFacade();}
private synchronized void prepareFacade() {
Aspectos de
Distribuição para RMI
if (healthWatcher == null) {
try { remoteFacade = (IFacade) java.rmi.Naming.lookup("//HealthWatcher");
} catch (java.rmi.RemoteException rmiEx) {...}
catch (java.rmi.NotBoundException rmiEx) {...}
catch (java.net.MalformedURLException rmiEx) {...}
}
} ...
void around(Complaint complaint) throws TransactionException, RepositoryException
}
ObjectNotFoundException,ObjectNotValidException:
public interface IFacade extends java.rmi.Remote {
facadeRemoteExecutions() && args(complaint) &&
public class HealthWatcherFacade {
}
RemoteException;
...
...
}
remoteFacade.update(complaint);
} catch (RemoteException rmiEx) {...}
ObjectNotFoundException, ObjectNotValidException,
ObjectNotFoundException,
ObjectNotValidException {
}
try {
throws TransactionException, RepositoryException,
throws TransactionException, RepositoryException,
}
call(void update(Complaint)) {
public void updateComplaint complaint)
public void update(Complaint complaint)
}
Disque Saúde com AOP
Implementação com AOP
„
Aumento em modularidade, reuso e
extensibidade
• Mais unidades de código
• Mudanças no sistema local podem causar
impacto nos aspectos de distribuição
„
Separation of concerns
• Relação entre os aspectos e o resto do
sistema nem sempre é clara
„
Normalmente menos linhas de código
Passo 1: Identificando
concerns
Fonte: JavaWorld
Concerns do Disque Saúde
Tratamento de Exceções
Interface com o Usuário
Armazenamento de dados
Distribuição
Regras do Negócio
Controle de Concorrência
Passo 2: Implementar o
sistema, separando os concerns
„
Alguns concerns são bem modelados
como objetos (núcleo do sistema)
• GUI
• Regras de negócio
„
Outros (crosscutting concerns) como
aspectos
• Distribuição, Controle de Concorrência,
Tratamento de Exceções,
Armazenamento de Dados
Passo 3: Recompor o sistema
Fonte: JavaWorld
Weaving é usado para …
„
Compor o “núcleo” do sistema com os
aspectos
Sistema original
chamadas locais entre A e B
A
Processo de
recomposição
Sistema distribuído
chamadas remotas entre A e B
Aspectos de
distribuição
B
Weaver
A
B
Protocolo de distribução
Composição nos join
points
a method is called
and returns or
throws
object A
a method is
called
and returns
or throws
dispatch
object B
a method executes
and returns or throws
Comportamento pode ser
alterado nos join points…
dispatch
a method executes
and returns or throws
Fonte: AspectJ Tutorial
aspectj.org
Pointcuts especificam join
points
„
Identificam join points de um sistema
• chamadas e execuções de métodos (e
construtores)
• acessos a atributos
• tratamento de exceções
• inicialização estática e dinâmica
• expõe o contexto nos join points
—argumentos de métodos, objetos alvo, atributos
„
Composição de join points
• &&, || e !
AspectJ: identificando
chamadas de métodos da
fachada (servidor)
identifica código
dentro da classe ...
nome do
pointcut
pointcut facadeMethodsCall():
within(HttpServlet+) &&
call(* IFacade+.*(..));
identifica
chamadas de …
qualquer
método
com quaisquer
argumentos
Advice especifica
comportamento extra nos
join points
„
Define código adicional que deve ser
executado…
• before
• after
—after returning
—after throwing
• ou around
join points
AspectJ: antes (before) de
chamar métodos da fachada
private IFacade remoteFacade;
before(): facadeMethodsCall() {
getRemoteInstance();
}
synchronized void getRemoteInstance() {...
remoteFacade =
(IFacade) java.rmi.Naming.lookup(...);
...}
AspectJ: transformando
chamadas locais em remotas
void around(Complaint c) throws Ex1,…:
facadeMethodsCall() && args(c) &&
call(void update(Complaint))
{
try { remoteFacade.update(c);
obtendo e utilizando
argumento de método
em um join point
} catch (RemoteException rmiEx) {...}
}
Além de dynamic
crosscutting com advice…
„
AspectJ suporta
crosscutting
static
• alterar relação de subtipo
• adicionar membros a classes
introductions
AspectJ: static crosscutting
declare parents:
HealthWatcherFacade implements IFacade;
declare parents: Complaint || Person
implements java.io.Serializable;
public static void
HealthWatcherFacade.main(String[] args){
try {...
java.rmi.Naming.rebind("/HW");
} catch
}
...
Adicionando
o método
main na classe
fachada
Alterando a hierarquia de tipos
Mais construtores de AspectJ
„
target(<nome do tipo>)
• Identifica join points onde o objeto alvo é uma
instância de <nome do tipo>
„
this(<nome do tipo>)
• Identifica join points cujo objeto em execução é
uma instância de <nome do tipo>
„
withincode(<assinatura de método>)
• Identifica join points cujo código em execução
pertence ao corpo do método ou construtor
especificado por <assinatura de método>
Mais construtores de AspectJ
„
cflow(<pointcut>)
• Identifica join points que estejam no fluxo de
execução identificado por <pointcut>
„
get(<assinatura>) / set(<assinatura>)
• Leitura/atribuição a atributos
„
declare soft: <nome do tipo>:<pointcut>;
• A exceção <nome do tipo> será encapsulada em
uma exceção não checada em tempo de
compilação (runtime) em qualquer join point
definido por <pointcut>
Distribution
AspectJ
aspect in
public aspect DistributionAspect {
declare parents: ...
private IFacade remoteFacade;
public static void
HealthWatcherFacade.main(String[] as)...
pointcut facadeMethodsCall(): ...
before(): facadeMethodsCall() ...
private synchronized void
getRemoteInstance() ...
void around(Complaint complaint) ...
}
Aspectos de desenvolvimento:
debugging simples com AspectJ
public aspect DatabaseDebugging {
pointcut queryExecution(String sql):
call(* Statement.*(String)) &&
args(sql);
before(String sql): queryExecution(sql) {
System.out.println(sql);
}
}
AspectJ: pontos positivos
„
„
„
„
„
Modularidade, reuso, e
extensibilidade de software
Obliviousness
Suporte a desenvolvimento com IDEs
Produtividade
Permite implementação e testes
progressivos
AspectJ: pontos negativos
„
Novo paradigma
• relação entre classes e aspectos deve ser
minimizada
• obliviousness
„
Projeto da linguagem
• tratamento de exceções
• conflitos entre aspectos
„
„
„
Requer suporte de ferramentas
Ambiente de desenvolvimento
Static weaving
AOP ou um bom projeto OO?
Padrão de
projeto Adapter
Com adaptadores…
„
„
Escrevemos mais código
A ligação entre o adaptador e o
objeto adaptado
• é explicita e invasiva
• não altera o comportamento de chamadas
internas para o objeto adaptado
• não tem acesso ao objeto fonte (source)
• pode ser modificado dinamicamente
Referências
„
„
„
„
„
Gregor Kiczales et. al. Aspect-Oriented
Programming. European Conference on
Object-Oriented Programming,
ECOOP'97
JavaWorld, “I want my AOP”.
http://www.aosd.net/
http://www.eclipse.org/aspectj
http://groups.yahoo.com/group/asoc-br/
Exercícios – PARTE 1
„
Considere a classe Conta a seguir
package contas;
public class Conta {
private String numero;
private double saldo;
public Conta(String numero, double saldo)...
public Conta(String numero) ...
public String getNumero() ...
public double getSaldo() ...
public void creditar(double valor) ...
public void debitar(double valor)
throws SaldoInsuficienteException ...
}
Exercício 1
„
„
Defina o aspecto Teste com um
pointcut para identificar todas as
chamadas ao método creditar de
objetos do tipo Conta.
Agora, no mesmo aspecto, defina um
advice para imprimir a mensagem "Vou
creditar" antes das chamadas ao
método creditar de objetos do tipo
Conta.
Exercício 2
„
„
„
Crie outro aspecto (Teste2) e defina um
pointcut para identificar todas as
execuções do método creditar de um
objeto do tipo Conta expondo o objeto do
qual o método vai ser executado.
Defina um advice para imprimir o saldo da
conta após a execução do método creditar.
O que deve acontecer caso ambos os
aspectos (Teste e Teste2) forem
compostos com o sistema?
Exercícios - PARTE 2
„
Agora considere a seguinte classe
CadastroContas
package contas;
public class CadastroContas {
private RepositorioContas contas;
...
public void transferir(String nDe, String nPara,
double valor)
throws ContaNaoEncontradaException,
SaldoInsuficienteException {
Conta de = contas.procurar(nDe);
Conta para = contas.procurar(nPara);
de.debitar(valor);
para.creditar(valor);
}
}
Exercício 3
„
Mais uma vez usando o projeto de
aplicação bancária defina o aspecto
TrocaOrdemArgumentos que troca
a ordem dos números das contas na
chamada do método transferir da
classe CadastroContas. Use o
advice around.
Exercício 4
„
Agora defina o aspecto
TransacaoTransferencia. Este
aspecto deve imprimir uma mensagem
após a execução com sucesso do
método creditar e do método debitar
de uma conta, mas somente se os
mesmos forem chamados devido a
execução de uma transferência. Use o
designator cflow.
Download