Este artigo demonstra como configurar a Programação Orientada a Aspectos (AOP) no Spring Framwork utilizando exclusivamente configuração XML.
Considere o seguinte cenário para exemplos:
Interafce e Implementação do DAO
Primeiro, definimos uma interface para o DAO de Usuário e sua implementação concreta.
package com.exemplo.persistencia;
import com.exemplo.modelo.Usuario;
public interface UsuarioDAO {
void registrar(Usuario usuario);
}
package com.exemplo.persistencia.impl;
import org.springframework.stereotype.Component;
import com.exemplo.persistencia.UsuarioDAO;
import com.exemplo.modelo.Usuario;
@Component
public class UsuarioDAOImpl implements UsuarioDAO {
@Override
public void registrar(Usuario usuario) {
// Lógica de persistência (ex: Hibernate, JDBC)
System.out.println("Usuário registrado com sucesso!");
}
}
Classe Aspecto (Aspect)
Criamos uma classe que conterá a lógica transversal (cross-cutting concern), como logging.
package com.exemplo.aspectos;
public class InterceptadorLog {
public void aconselharAntes() {
System.out.println("[Log] Antes da execução do método.");
}
}
Serviço de Aplicação
A classe de serviço utiliza a injeção de dependência para consumir o DAO.
package com.exemplo.servico;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.exemplo.persistencia.UsuarioDAO;
import com.exemplo.modelo.Usuario;
@Component("servicoUsuario")
public class ServicoUsuario {
private UsuarioDAO usuarioDAO;
@Autowired
public void setUsuarioDAO(UsuarioDAO usuarioDAO) {
this.usuarioDAO = usuarioDAO;
}
public void adicionar(Usuario usuario) {
usuarioDAO.registrar(usuario);
}
}
Configuração XML (Exemplo 1: Pointcut Global)
No arquivo applicationContext.xml, declaramos um pointcut reutilizável dentro do bloco <aop:config> e referenciamos-o no conselho (advice).
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.exemplo"/>
<!-- Registra a classe de aspecto como um bean -->
<bean id="logAspecto" class="com.exemplo.aspectos.InterceptadorLog"/>
<aop:config>
<!-- Define um pointcut global chamado 'pontoServico' -->
<aop:pointcut id="pontoServico"
expression="execution(public * com.exemplo.servico..*.adicionar(..))"/>
<!-- Associa o aspecto ao pointcut -->
<aop:aspect id="aspectoLog" ref="logAspecto">
<aop:before method="aconselharAntes" pointcut-ref="pontoServico"/>
</aop:aspect>
</aop:config>
</beans>
Configuração XML (Exemplo 2: Pointcut Inline)
Alternativamente, o pointcut pode ser definido diretamente dentro do elemento do conselho, tornando-o local àquele aspecto.
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.exemplo"/>
<bean id="logAspecto" class="com.exemplo.aspectos.InterceptadorLog"/>
<aop:config>
<aop:aspect id="aspectoLog" ref="logAspecto">
<!-- Pointcut inline, definido apenas para este conselho -->
<aop:before method="aconselharAntes"
pointcut="execution(public * com.exemplo.servico..*.adicionar(..))"/>
</aop:aspect>
</aop:config>
</beans>
Teste
Um teste de integração verifica o comportamento da aplicação.
package com.exemplo;
import com.exemplo.modelo.Usuario;
import com.exemplo.servico.ServicoUsuario;
import org.junit.jupiter.api.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
class AplicacaoTest {
@Test
void deveExecutarLogAntesDoServico() {
ClassPathXmlApplicationContext contexto =
new ClassPathXmlApplicationContext("applicationContext.xml");
ServicoUsuario servico = contexto.getBean(ServicoUsuario.class);
servico.adicionar(new Usuario());
contexto.close();
}
}
Ao executar o teste, o console exibirá a mensagem do log ([Log] Antes da execução do método.) antes da mensagem de registro do DAO.
Nota: A configuração de <aop:advisor> é frequentmeente utilizada em conjunto com a transação declarativa do Spring, geralmente configurada via namespace tx.