Exemplo de Tutorial Spring AOP – Aspecto, Conselho, Ponto de Corte, JoinPoint, Anotações, Configuração XML

Spring Framework é desenvolvido com base em dois conceitos principais – Dependency Injection e Programação Orientada a Aspectos (Spring AOP).

Spring AOP

Já vimos como funciona a Injeção de Dependência do Spring, hoje vamos analisar os conceitos principais da Programação Orientada a Aspectos e como podemos implementá-la usando o Spring Framework.

Visão Geral do Spring AOP

A maioria das aplicações empresariais possui algumas preocupações transversais comuns que são aplicáveis a diferentes tipos de objetos e módulos. Algumas das preocupações transversais comuns incluem registro, gerenciamento de transações, validação de dados, etc. Na Programação Orientada a Objetos, a modularidade da aplicação é alcançada por meio de classes, enquanto na Programação Orientada a Aspectos, a modularidade da aplicação é alcançada por Aspectos e eles são configurados para atravessar diferentes classes. O Spring AOP retira a dependência direta de tarefas transversais das classes, o que não podemos alcançar por meio do modelo normal de programação orientada a objetos. Por exemplo, podemos ter uma classe separada para registro, mas novamente as classes funcionais terão que chamar esses métodos para realizar o registro em toda a aplicação.

Conceitos Principais da Programação Orientada a Aspectos

Antes de nos aprofundarmos na implementação do Spring AOP, devemos entender os conceitos principais da POA.

  1. Aspecto: Um aspecto é uma classe que implementa preocupações da aplicação empresarial que atravessam várias classes, como o gerenciamento de transações. Os aspectos podem ser uma classe normal configurada por meio da configuração XML do Spring, ou podemos usar a integração Spring AspectJ para definir uma classe como Aspecto usando a anotação @Aspect.
  2. Ponto de Junção: Um ponto de junção é um ponto específico na aplicação, como a execução de método, tratamento de exceções, mudança de valores de variáveis de objeto, etc. No Spring AOP, um ponto de junção é sempre a execução de um método.
  3. Conselho: Conselhos são ações tomadas para um ponto de junção específico. Em termos de programação, são métodos que são executados quando um determinado ponto de junção com um ponto de corte correspondente é alcançado na aplicação. Você pode pensar em Conselhos como interceptadores do Struts2 ou Filtros do Servlet.
  4. Ponto de Corte: Ponto de corte são expressões que são correspondidas com pontos de junção para determinar se o conselho precisa ser executado ou não. Ponto de corte utiliza diferentes tipos de expressões que são correspondidas com os pontos de junção e o framework Spring usa a linguagem de expressão de ponto de corte do AspectJ.
  5. Objeto Alvo: São os objetos nos quais os conselhos são aplicados. O Spring AOP é implementado usando proxies em tempo de execução, então esse objeto é sempre um objeto proxy. Isso significa que uma subclasse é criada em tempo de execução onde o método alvo é substituído e os conselhos são incluídos com base em sua configuração.
  6. Proxy AOP: A implementação do Spring AOP utiliza o proxy dinâmico do JDK para criar classes Proxy com classes alvo e invocações de conselhos, essas classes são chamadas de classes de proxy AOP. Também podemos usar o proxy CGLIB adicionando-o como dependência no projeto Spring AOP.
  7. Weaving: É o processo de vincular aspectos a outros objetos para criar objetos de proxy aconselhados. Isso pode ser feito em tempo de compilação, tempo de carregamento ou em tempo de execução. O Spring AOP realiza o weaving em tempo de execução.

Tipos de Conselhos AOP

Com base na estratégia de execução do conselho, eles são dos seguintes tipos.

  1. Conselho Antes: Esses conselhos são executados antes da execução dos métodos do ponto de junção. Podemos usar a anotação @Before para marcar um tipo de conselho como conselho antes.
  2. Conselho Após (Finalmente): Um conselho que é executado após o término da execução do método do ponto de junção, seja normalmente ou lançando uma exceção. Podemos criar um conselho após usando a anotação @After.
  3. Conselho Após o Retorno: Às vezes, queremos que os métodos de aconselhamento sejam executados apenas se o método do ponto de junção for executado normalmente. Podemos usar a anotação @AfterReturning para marcar um método como conselho após o retorno.
  4. Depois do Conselho de Lançamento: Este conselho é executado apenas quando o método do ponto de junção lança uma exceção, podemos usá-lo para reverter a transação de forma declarativa. Usamos a anotação @AfterThrowing para esse tipo de conselho.
  5. Conselho Envoltório: Este é o conselho mais importante e poderoso. Este conselho envolve o método do ponto de junção e também podemos escolher se queremos executar o método do ponto de junção ou não. Podemos escrever código de conselho que é executado antes e depois da execução do método do ponto de junção. É responsabilidade do conselho envoltório invocar o método do ponto de junção e retornar valores se o método estiver retornando algo. Usamos a anotação @Around para criar métodos de conselho envoltório.

Os pontos mencionados acima podem parecer confusos, mas quando olharmos para a implementação do Spring AOP, as coisas ficarão mais claras. Vamos começar a criar um projeto Spring simples com implementações AOP. O Spring fornece suporte para usar anotações AspectJ para criar aspectos e vamos usar isso para simplicidade. Todas as anotações AOP mencionadas acima estão definidas no pacote org.aspectj.lang.annotation. Spring Tool Suite fornece informações úteis sobre os aspectos, então eu sugeriria que você o use. Se você não está familiarizado com o STS, eu recomendaria que você dê uma olhada no Tutorial do Spring MVC onde eu expliquei como usá-lo.

Exemplo de Spring AOP

Crie um novo projeto Maven Spring simples para que todas as bibliotecas principais do Spring estejam incluídas nos arquivos pom.xml e não precisemos incluí-las explicitamente. Nosso projeto final ficará como a imagem abaixo, vamos examinar os componentes principais do Spring e as implementações de Aspecto em detalhes.

Dependências do AspectJ do Spring AOP

O framework Spring fornece suporte para AOP por padrão, mas como estamos usando anotações AspectJ para configurar aspectos e conselhos, precisaríamos incluí-los no arquivo pom.xml.

<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>org.springframework.samples</groupId>
	<artifactId>SpringAOPExample</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<properties>

		<!-- Generic properties -->
		<java.version>1.6</java.version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

		<!-- Spring -->
		<spring-framework.version>4.0.2.RELEASE</spring-framework.version>

		<!-- Logging -->
		<logback.version>1.0.13</logback.version>
		<slf4j.version>1.7.5</slf4j.version>

		<!-- Test -->
		<junit.version>4.11</junit.version>

		<!-- AspectJ -->
		<aspectj.version>1.7.4</aspectj.version>

	</properties>

	<dependencies>
		<!-- Spring and Transactions -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>

		<!-- Logging with SLF4J & LogBack -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>${slf4j.version}</version>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>${logback.version}</version>
			<scope>runtime</scope>
		</dependency>

		<!-- AspectJ dependencies -->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>${aspectj.version}</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjtools</artifactId>
			<version>${aspectj.version}</version>
		</dependency>
	</dependencies>
</project>

Observe que adicionei as dependências aspectjrt e aspectjtools (versão 1.7.4) no projeto. Também atualizei a versão do framework Spring para a mais recente disponível até a data, ou seja, 4.0.2.RELEASE.

Classe de Modelo

Vamos criar um simples bean Java que usaremos como exemplo com alguns métodos adicionais. Código Employee.java:

package com.journaldev.spring.model;

import com.journaldev.spring.aspect.Loggable;

public class Employee {

	private String name;
	
	public String getName() {
		return name;
	}

	@Loggable
	public void setName(String nm) {
		this.name=nm;
	}
	
	public void throwException(){
		throw new RuntimeException("Dummy Exception");
	}	
}

Você percebeu que o método setName() está anotado com a anotação Loggable. É uma anotação Java personalizada definida por nós no projeto. Vamos analisar o seu uso mais tarde.

Classe de Serviço

Vamos criar uma classe de serviço para trabalhar com o bean Employee. Código EmployeeService.java:

package com.journaldev.spring.service;

import com.journaldev.spring.model.Employee;

public class EmployeeService {

	private Employee employee;
	
	public Employee getEmployee(){
		return this.employee;
	}
	
	public void setEmployee(Employee e){
		this.employee=e;
	}
}

I could have used Spring annotations to configure it as a Spring Component, but we will use XML based configuration in this project. EmployeeService class is very standard and just provides us an access point for Employee beans.

Configuração do Bean Spring com AOP

Se estiver usando o STS, você tem a opção de criar um “Arquivo de Configuração de Bean Spring” e escolher o namespace do esquema AOP. Mas se estiver usando outro IDE, você pode adicioná-lo simplesmente ao arquivo de configuração de bean Spring. Meu arquivo de configuração de bean do projeto parece o seguinte. spring.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
	xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="https://www.springframework.org/schema/aop"
	xsi:schemaLocation="https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-4.0.xsd
		https://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

<!-- Enable AspectJ style of Spring AOP -->
<aop:aspectj-autoproxy />

<!-- Configure Employee Bean and initialize it -->
<bean name="employee" class="com.journaldev.spring.model.Employee">
	<property name="name" value="Dummy Name"></property>
</bean>

<!-- Configure EmployeeService bean -->
<bean name="employeeService" class="com.journaldev.spring.service.EmployeeService">
	<property name="employee" ref="employee"></property>
</bean>

<!-- Configure Aspect Beans, without this Aspects advices wont execute -->
<bean name="employeeAspect" class="com.journaldev.spring.aspect.EmployeeAspect" />
<bean name="employeeAspectPointcut" class="com.journaldev.spring.aspect.EmployeeAspectPointcut" />
<bean name="employeeAspectJoinPoint" class="com.journaldev.spring.aspect.EmployeeAspectJoinPoint" />
<bean name="employeeAfterAspect" class="com.journaldev.spring.aspect.EmployeeAfterAspect" />
<bean name="employeeAroundAspect" class="com.journaldev.spring.aspect.EmployeeAroundAspect" />
<bean name="employeeAnnotationAspect" class="com.journaldev.spring.aspect.EmployeeAnnotationAspect" />

</beans>

Para usar o AOP do Spring nos beans do Spring, precisamos fazer o seguinte:

  1. Declare o namespace do AOP como xmlns:aop=“https://www.springframework.org/schema/aop
  2. Adicione o elemento aop:aspectj-autoproxy para habilitar o suporte ao AspectJ do Spring com proxy automático em tempo de execução
  3. Configure as classes de Aspecto como outros beans do Spring

Você pode ver que tenho muitos aspectos definidos no arquivo de configuração do feijão Spring, é hora de analisá-los um por um.

Exemplo de Aspecto Before do Spring AOP

Código EmployeeAspect.java:

package com.journaldev.spring.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class EmployeeAspect {

	@Before("execution(public String getName())")
	public void getNameAdvice(){
		System.out.println("Executing Advice on getName()");
	}
	
	@Before("execution(* com.journaldev.spring.service.*.get*())")
	public void getAllAdvice(){
		System.out.println("Service method getter called");
	}
}

Pontos importantes na classe de aspecto acima são:

  • Classes de Aspecto são necessárias para ter a anotação @Aspect.
  • A anotação @Before é usada para criar um conselho Before
  • O parâmetro de string passado na anotação @Before é a expressão Pointcut
  • O conselho getNameAdvice() será executado para qualquer método Spring Bean com a assinatura public String getName(). Este é um ponto muito importante a lembrar, se criarmos um bean Employee usando o operador new, os conselhos não serão aplicados. Somente quando usarmos ApplicationContext para obter o bean, os conselhos serão aplicados.
  • Podemos usar o asterisco (*) como caractere curinga em expressões Pointcut, getAllAdvice() será aplicado para todas as classes no pacote com.journaldev.spring.service cujo nome começa com get e não recebe argumentos.

Vamos ver o conselho em ação em uma classe de teste depois de termos examinado todos os diferentes tipos de conselhos.

Métodos de Ponto de Corte e Reutilização do AOP do Spring

Às vezes, temos que usar a mesma expressão de ponto de corte em vários lugares, podemos criar um método vazio com a anotação @Pointcut e depois usá-lo como uma expressão nos avisos. Código EmployeeAspectPointcut.java:

package com.journaldev.spring.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class EmployeeAspectPointcut {

	@Before("getNamePointcut()")
	public void loggingAdvice(){
		System.out.println("Executing loggingAdvice on getName()");
	}
	
	@Before("getNamePointcut()")
	public void secondAdvice(){
		System.out.println("Executing secondAdvice on getName()");
	}
	
	@Pointcut("execution(public String getName())")
	public void getNamePointcut(){}
	
	@Before("allMethodsPointcut()")
	public void allServiceMethodsAdvice(){
		System.out.println("Before executing service method");
	}
	
	// Ponto de corte para executar em todos os métodos de classes em um pacote
	@Pointcut("within(com.journaldev.spring.service.*)")
	public void allMethodsPointcut(){}
	
}

O exemplo acima é muito claro, em vez de expressão, estamos usando o nome do método no argumento da anotação de aviso.

Ponto de Junção e Argumentos de Aviso do AOP do Spring

Podemos usar JoinPoint como parâmetro nos métodos de aviso e usar isso para obter a assinatura do método ou o objeto de destino. Podemos usar a expressão args() no ponto de corte para ser aplicada a qualquer método que corresponda ao padrão de argumento. Se usarmos isso, então precisamos usar o mesmo nome no método de aviso de onde o tipo de argumento é determinado. Podemos usar também objetos genéricos nos argumentos de aviso. Código EmployeeAspectJoinPoint.java:

package com.journaldev.spring.aspect;

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class EmployeeAspectJoinPoint {
	
	@Before("execution(public void com.journaldev.spring.model..set*(*))")
	public void loggingAdvice(JoinPoint joinPoint){
		System.out.println("Before running loggingAdvice on method="+joinPoint.toString());
		
		System.out.println("Agruments Passed=" + Arrays.toString(joinPoint.getArgs()));

	}
	
	// Argumentos do aviso, serão aplicados aos métodos de bean com um único argumento de String
	@Before("args(name)")
	public void logStringArguments(String name){
		System.out.println("String argument passed="+name);
	}
}

Exemplo de Conselho After do Spring AOP

Vamos dar uma olhada em uma classe de aspecto simples com um exemplo de conselho After, After Throwing e After Returning. Código EmployeeAfterAspect.java:

package com.journaldev.spring.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class EmployeeAfterAspect {

	@After("args(name)")
	public void logStringArguments(String name){
		System.out.println("Running After Advice. String argument passed="+name);
	}
	
	@AfterThrowing("within(com.journaldev.spring.model.Employee)")
	public void logExceptions(JoinPoint joinPoint){
		System.out.println("Exception thrown in Employee Method="+joinPoint.toString());
	}
	
	@AfterReturning(pointcut="execution(* getName())", returning="returnString")
	public void getNameReturningAdvice(String returnString){
		System.out.println("getNameReturningAdvice executed. Returned String="+returnString);
	}
	
}

Podemos usar within na expressão do ponto de corte para aplicar o conselho a todos os métodos na classe. Podemos usar o conselho @AfterReturning para obter o objeto retornado pelo método aconselhado. Temos o método throwException() no bean Employee para demonstrar o uso do conselho After Throwing.

Exemplo de Aspecto Around do Spring AOP

Como explicado anteriormente, podemos usar o aspecto Around para cortar a execução do método antes e depois. Podemos usá-lo para controlar se o método aconselhado irá ou não executar. Também podemos inspecionar o valor retornado e alterá-lo. Este é o conselho mais poderoso e precisa ser aplicado corretamente. Código EmployeeAroundAspect.java:

package com.journaldev.spring.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class EmployeeAroundAspect {

	@Around("execution(* com.journaldev.spring.model.Employee.getName())")
	public Object employeeAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
		System.out.println("Before invoking getName() method");
		Object value = null;
		try {
			value = proceedingJoinPoint.proceed();
		} catch (Throwable e) {
			e.printStackTrace();
		}
		System.out.println("After invoking getName() method. Return value="+value);
		return value;
	}
}

Em torno do conselho, é sempre necessário ter ProceedingJoinPoint como argumento e devemos usar o método proceed() para invocar o método do objeto alvo aconselhado. Se o método aconselhado estiver retornando algo, é responsabilidade do conselho devolvê-lo ao programa chamador. Para métodos void, o método de conselho pode retornar null. Como o conselho de volta corta o método aconselhado, podemos controlar a entrada e saída do método, bem como o seu comportamento de execução.

Conselho do Spring com Ponto de Corte de Anotação Personalizada

Se olharmos para todas as expressões de corte de ponto de conselho acima, há chances de que elas sejam aplicadas a alguns outros beans onde não é pretendido. Por exemplo, alguém pode definir um novo bean de primavera com o método getName() e o conselho começará a ser aplicado a ele, mesmo que não fosse pretendido. É por isso que devemos manter o escopo da expressão de corte de ponto o mais estreito possível. Uma abordagem alternativa é criar uma anotação personalizada e anotar os métodos onde queremos que o conselho seja aplicado. Este é o propósito de ter o método Employee \texttt{setName()} anotado com a anotação \texttt{@Loggable}. A anotação \texttt{@Transactional} do Spring Framework é um ótimo exemplo dessa abordagem para \texttt{Gerenciamento de Transações do Spring}. Código Loggable.java:

package com.journaldev.spring.aspect;

public @interface Loggable {

}

Código EmployeeAnnotationAspect.java:

package com.journaldev.spring.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class EmployeeAnnotationAspect {

	@Before("@annotation(com.journaldev.spring.aspect.Loggable)")
	public void myAdvice(){
		System.out.println("Executing myAdvice!!");
	}
}

O método \texttt{myAdvice()} aconselhará apenas o método \texttt{setName()}. Esta é uma abordagem muito segura e sempre que quisermos aplicar o conselho a qualquer método, tudo o que precisamos fazer é anotá-lo com a anotação \texttt{Loggable}.

Configuração XML do AOP do Spring

I always prefer annotation but we also have the option to configure aspects in the spring configuration file. For example, let’s say we have a class as below. EmployeeXMLConfigAspect.java code:

package com.journaldev.spring.aspect;

import org.aspectj.lang.ProceedingJoinPoint;

public class EmployeeXMLConfigAspect {

	public Object employeeAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
		System.out.println("EmployeeXMLConfigAspect:: Before invoking getName() method");
		Object value = null;
		try {
			value = proceedingJoinPoint.proceed();
		} catch (Throwable e) {
			e.printStackTrace();
		}
		System.out.println("EmployeeXMLConfigAspect:: After invoking getName() method. Return value="+value);
		return value;
	}
}

Podemos configurá-lo incluindo a seguinte configuração no arquivo de configuração do Spring Bean.

<bean name="employeeXMLConfigAspect" class="com.journaldev.spring.aspect.EmployeeXMLConfigAspect" />

<!-- Spring AOP XML Configuration -->
<aop:config>
	<aop:aspect ref="employeeXMLConfigAspect" id="employeeXMLConfigAspectID" order="1">
		<aop:pointcut expression="execution(* com.journaldev.spring.model.Employee.getName())" id="getNamePointcut"/>
		<aop:around method="employeeAroundAdvice" pointcut-ref="getNamePointcut" arg-names="proceedingJoinPoint"/>
	</aop:aspect>
</aop:config>

Os elementos de configuração AOP xml têm seu propósito claro a partir de seus nomes, então não vou entrar em muitos detalhes sobre isso.

Exemplo de AOP do Spring

Vamos ter um programa Spring simples e ver como todos esses aspectos atravessam os métodos do bean. Código SpringMain.java:

package com.journaldev.spring.main;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.journaldev.spring.service.EmployeeService;

public class SpringMain {

	public static void main(String[] args) {
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
		EmployeeService employeeService = ctx.getBean("employeeService", EmployeeService.class);
		
		System.out.println(employeeService.getEmployee().getName());
		
		employeeService.getEmployee().setName("Pankaj");
		
		employeeService.getEmployee().throwException();
		
		ctx.close();
	}
}

Agora, quando executamos o programa acima, obtemos a seguinte saída.

Mar 20, 2014 8:50:09 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@4b9af9a9: startup date [Thu Mar 20 20:50:09 PDT 2014]; root of context hierarchy
Mar 20, 2014 8:50:09 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring.xml]
Service method getter called
Before executing service method
EmployeeXMLConfigAspect:: Before invoking getName() method
Executing Advice on getName()
Executing loggingAdvice on getName()
Executing secondAdvice on getName()
Before invoking getName() method
After invoking getName() method. Return value=Dummy Name
getNameReturningAdvice executed. Returned String=Dummy Name
EmployeeXMLConfigAspect:: After invoking getName() method. Return value=Dummy Name
Dummy Name
Service method getter called
Before executing service method
String argument passed=Pankaj
Before running loggingAdvice on method=execution(void com.journaldev.spring.model.Employee.setName(String))
Agruments Passed=[Pankaj]
Executing myAdvice!!
Running After Advice. String argument passed=Pankaj
Service method getter called
Before executing service method
Exception thrown in Employee Method=execution(void com.journaldev.spring.model.Employee.throwException())
Exception in thread "main" java.lang.RuntimeException: Dummy Exception
	at com.journaldev.spring.model.Employee.throwException(Employee.java:19)
	at com.journaldev.spring.model.Employee$$FastClassBySpringCGLIB$$da2dc051.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:711)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
	at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:58)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:644)
	at com.journaldev.spring.model.Employee$$EnhancerBySpringCGLIB$$3f881964.throwException(<generated>)
	at com.journaldev.spring.main.SpringMain.main(SpringMain.java:17)

Você pode ver que os conselhos estão sendo executados um por um com base em suas configurações de pointcut. Você deve configurá-los um por um para evitar confusões. Isso é tudo para o Tutorial de Exemplo de AOP do Spring, espero que você tenha aprendido o básico de AOP com o Spring e possa aprender mais com exemplos. Baixe o projeto de exemplo no link abaixo e brinque com ele.

Baixar Projeto Spring AOP

Source:
https://www.digitalocean.com/community/tutorials/spring-aop-example-tutorial-aspect-advice-pointcut-joinpoint-annotations