Ejemplo Tutorial de Spring AOP – Aspecto, Consejo, Punto de Corte, Punto de Unión, Anotaciones, Configuración XML

El Marco de Primavera se desarrolla sobre dos conceptos principales: Inyección de Dependencias y Programación Orientada a Aspectos (Spring AOP).

Spring AOP

Ya hemos visto cómo funciona la Inyección de Dependencias de Spring, hoy veremos los conceptos principales de la Programación Orientada a Aspectos y cómo podemos implementarla usando el Marco de Primavera.

Resumen de Spring AOP

La mayoría de las aplicaciones empresariales tienen algunas preocupaciones comunes que son aplicables a diferentes tipos de objetos y módulos. Algunas de las preocupaciones comunes son el registro, la gestión de transacciones, la validación de datos, etc. En la Programación Orientada a Objetos, la modularidad de la aplicación se logra mediante clases, mientras que en la Programación Orientada a Aspectos, la modularidad de la aplicación se logra mediante Aspectos y están configurados para cortar a través de diferentes clases. Spring AOP elimina la dependencia directa de tareas transversales de las clases que no podemos lograr a través del modelo de programación orientada a objetos normal. Por ejemplo, podemos tener una clase separada para el registro, pero nuevamente, las clases funcionales tendrán que llamar a estos métodos para lograr el registro en toda la aplicación.

Conceptos Básicos de la Programación Orientada a Aspectos

Antes de adentrarnos en la implementación de la implementación de Spring AOP, debemos entender los conceptos básicos de AOP.

  1. Aspecto: Un aspecto es una clase que implementa preocupaciones de aplicación empresarial que atraviesan múltiples clases, como la gestión de transacciones. Los aspectos pueden ser una clase normal configurada a través de la configuración XML de Spring o podemos usar la integración de Spring AspectJ para definir una clase como Aspecto usando la anotación @Aspect.
  2. Punto de unión: Un punto de unión es un punto específico en la aplicación, como la ejecución de un método, el manejo de excepciones, el cambio de valores de variables de objeto, etc. En Spring AOP, un punto de unión siempre es la ejecución de un método.
  3. Consejo: Los consejos son acciones tomadas para un punto de unión particular. En términos de programación, son métodos que se ejecutan cuando se alcanza un cierto punto de unión con un corte de punto coincidente en la aplicación. Puedes pensar en los Consejos como interceptores de Struts2 o Filtros Servlet.
  4. Corte de punto: El corte de punto son expresiones que coinciden con los puntos de unión para determinar si se debe ejecutar o no un consejo. El corte de punto utiliza diferentes tipos de expresiones que coinciden con los puntos de unión y el marco de Spring utiliza el lenguaje de expresión de corte de punto de AspectJ.
  5. Objeto objetivo: Son los objetos en los que se aplican los consejos. Spring AOP se implementa utilizando proxies en tiempo de ejecución, por lo que este objeto siempre es un objeto proxificado. Lo que significa es que se crea una subclase en tiempo de ejecución donde se anula el método objetivo y se incluyen los consejos en función de su configuración.
  6. Proxy AOP: La implementación de Spring AOP utiliza el proxy dinámico JDK para crear las clases Proxy con las clases objetivo y las invocaciones de consejos, a las que se llaman clases de proxy AOP. También podemos utilizar el proxy CGLIB agregándolo como dependencia en el proyecto Spring AOP.
  7. Tejido (Weaving): Es el proceso de vincular aspectos con otros objetos para crear objetos de proxy asesorados. Esto se puede hacer en tiempo de compilación, tiempo de carga o en tiempo de ejecución. Spring AOP realiza el tejido en tiempo de ejecución.

Tipos de Consejos AOP

Según la estrategia de ejecución del consejo, son de los siguientes tipos.

  1. Consejo Antes (Before Advice): Estos consejos se ejecutan antes de la ejecución de los métodos del punto de unión. Podemos utilizar la anotación @Before para marcar un tipo de consejo como consejo antes.
  2. Consejo Después (Finally) (After Advice): Un consejo que se ejecuta después de que el método del punto de unión termine de ejecutarse, ya sea de forma normal o al lanzar una excepción. Podemos crear un consejo después utilizando la anotación @After.
  3. Consejo Después de Devolver (After Returning Advice): A veces queremos que los métodos de consejo se ejecuten solo si el método del punto de unión se ejecuta normalmente. Podemos utilizar la anotación @AfterReturning para marcar un método como consejo después de devolver.
  4. Después de Dar Consejos: Este consejo se ejecuta solo cuando el método del punto de unión lanza una excepción, podemos usarlo para revertir la transacción de manera declarativa. Utilizamos la anotación @AfterThrowing para este tipo de consejo.
  5. Consejo alrededor: Este es el consejo más importante y poderoso. Este consejo rodea el método del punto de unión y también podemos elegir si ejecutar o no el método del punto de unión. Podemos escribir código de consejo que se ejecuta antes y después de la ejecución del método del punto de unión. Es responsabilidad del consejo alrededor invocar el método del punto de unión y devolver valores si el método está devolviendo algo. Utilizamos la anotación @Around para crear métodos de consejo alrededor.

Los puntos mencionados anteriormente pueden sonar confusos, pero cuando veamos la implementación de Spring AOP, las cosas estarán más claras. Comencemos creando un proyecto Spring simple con implementaciones de AOP. Spring proporciona soporte para utilizar anotaciones de AspectJ para crear aspectos y lo utilizaremos por simplicidad. Todas las anotaciones AOP mencionadas anteriormente están definidas en el paquete org.aspectj.lang.annotation. Spring Tool Suite proporciona información útil sobre los aspectos, así que te sugeriría que lo uses. Si no estás familiarizado con STS, te recomendaría que eches un vistazo al Tutorial de Spring MVC donde he explicado cómo usarlo.

Ejemplo de Spring AOP

Crear un nuevo proyecto Simple Spring Maven para que todas las bibliotecas principales de Spring estén incluidas en los archivos pom.xml y no sea necesario incluirlas explícitamente. Nuestro proyecto final se verá como en la imagen a continuación. Exploraremos en detalle los componentes principales de Spring y las implementaciones de Aspecto.

Dependencias de Spring AOP AspectJ

El framework Spring proporciona soporte AOP de forma predeterminada, pero como estamos utilizando anotaciones AspectJ para configurar aspectos y consejos, necesitaríamos incluirlos en el archivo 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>

Observa que he agregado las dependencias aspectjrt y aspectjtools (versión 1.7.4) en el proyecto. También he actualizado la versión del framework Spring a la última disponible hasta la fecha, es decir, 4.0.2.RELEASE.

Clase de Modelo

Creemos un simple bean de Java que utilizaremos para nuestro ejemplo con algunos métodos adicionales. Código de 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");
	}	
}

¿Notaste que el método setName() está anotado con la anotación Loggable? Es una anotación Java personalizada definida por nosotros en el proyecto. Investigaremos su uso más adelante.

Clase de Servicio

Creemos una clase de servicio para trabajar con el bean Employee. Código de 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.

Configuración de Bean Spring con AOP

Si estás utilizando STS, tienes la opción de crear un “Archivo de Configuración de Bean Spring” y elegir el espacio de nombres del esquema AOP, pero si estás utilizando otro IDE, simplemente puedes agregarlo en el archivo de configuración de bean spring. Mi archivo de configuración de bean de proyecto se ve así. 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 AOP de Spring en beans de Spring, necesitamos hacer lo siguiente:

  1. Declarar el espacio de nombres de AOP como xmlns:aop=“https://www.springframework.org/schema/aop
  2. Agregar el elemento aop:aspectj-autoproxy para habilitar el soporte de AspectJ de Spring con proxy automático en tiempo de ejecución
  3. Configurar las clases de aspecto como otros beans de Spring

Puede ver que tengo muchos aspectos definidos en el archivo de configuración de beans de Spring, es hora de analizarlos uno por uno.

Ejemplo de Aspecto Before de 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");
	}
}

Puntos importantes en la clase de aspecto anterior:

  • Se requiere que las clases Aspect tengan la anotación @Aspect.
  • La anotación @Before se utiliza para crear un consejo Before
  • El parámetro de cadena pasado en la anotación @Before es la expresión Pointcut
  • El consejo getNameAdvice() se ejecutará para cualquier método de bean de Spring con la firma public String getName(). Este es un punto muy importante a recordar, si creamos el bean Employee utilizando el operador new, los consejos no se aplicarán. Solo cuando usemos ApplicationContext para obtener el bean, se aplicarán los consejos.
  • Podemos usar el asterisco (*) como comodín en las expresiones Pointcut, getAllAdvice() se aplicará a todas las clases en el paquete com.journaldev.spring.service cuyo nombre comienza con get y no toma ningún argumento.

Veremos el consejo en acción en una clase de prueba después de haber analizado todos los diferentes tipos de consejos.

Métodos de Punto de Corte y Reutilización en Spring AOP

A veces tenemos que usar la misma expresión de punto de corte en varios lugares, podemos crear un método vacío con la anotación @Pointcut y luego usarlo como expresión en los consejos. 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");
	}
	
	//Punto de corte para ejecutar en todos los métodos de clases en un paquete
	@Pointcut("within(com.journaldev.spring.service.*)")
	public void allMethodsPointcut(){}
	
}

El ejemplo anterior es muy claro, en lugar de una expresión, estamos usando el nombre del método en el argumento de la anotación del consejo.

JoinPoint y Argumentos del Consejo en Spring AOP

Podemos usar JoinPoint como parámetro en los métodos de consejo y usarlo para obtener la firma del método o el objeto objetivo. Podemos usar la expresión args() en el punto de corte para aplicarla a cualquier método que coincida con el patrón de argumentos. Si usamos esto, entonces necesitamos usar el mismo nombre en el método de consejo desde donde se determina el tipo de argumento. También podemos usar objetos genéricos en los argumentos del consejo. 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 del consejo, se aplicarán a métodos de bean con un solo argumento de tipo String
	@Before("args(name)")
	public void logStringArguments(String name){
		System.out.println("String argument passed="+name);
	}
}

Ejemplo de consejo After de Spring AOP

Echemos un vistazo a una clase de aspecto simple con un ejemplo de consejos After, After Throwing y 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 en la expresión del punto de corte para aplicar el consejo a todos los métodos de la clase. Podemos usar el consejo @AfterReturning para obtener el objeto devuelto por el método asesorado. Tenemos el método throwException() en el bean Employee para mostrar el uso del consejo After Throwing.

Ejemplo de aspecto Around de Spring AOP

Como se explicó anteriormente, podemos usar el aspecto Around para cortar la ejecución del método antes y después. Podemos usarlo para controlar si el método asesorado se ejecutará o no. También podemos inspeccionar el valor devuelto y cambiarlo. Este es el consejo más poderoso y debe aplicarse correctamente. 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;
	}
}

Alrededor del consejo siempre se requiere tener ProceedingJoinPoint como argumento y debemos usar su método proceed() para invocar el método objetivo aconsejado del objeto. Si el método aconsejado devuelve algo, es responsabilidad del consejo devolverlo al programa llamador. Para métodos void, el método de consejo puede devolver null. Dado que el consejo alrededor corta alrededor del método aconsejado, podemos controlar la entrada y salida del método, así como su comportamiento de ejecución.

Consejo de Primavera con Punto de Corte de Anotación Personalizada

Si observas todas las expresiones de corte de consejos mencionadas anteriormente, existe la posibilidad de que se apliquen a otros beans donde no está previsto. Por ejemplo, alguien puede definir un nuevo bean de primavera con el método getName() y el consejo comenzará a aplicarse incluso si no estaba previsto. Por eso debemos mantener el alcance de la expresión de corte de consejos lo más estrecho posible. Un enfoque alternativo es crear una anotación personalizada y anotar los métodos donde queremos que se aplique el consejo. Este es el propósito de tener el método setName() del Empleado anotado con la anotación @Loggable. La anotación @Transactional del Marco de Primavera es un gran ejemplo de este enfoque para la Administración de Transacciones de Spring. Código de Loggable.java:

package com.journaldev.spring.aspect;

public @interface Loggable {

}

Código de 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!!");
	}
}

El método myAdvice() asesorará solo al método setName(). Este es un enfoque muy seguro y siempre que queramos aplicar el consejo a cualquier método, todo lo que necesitamos es anotarlo con la anotación Loggable.

Configuración XML de AOP de 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 configurarlo incluyendo la siguiente configuración en el archivo de configuración de beans de Spring.

<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>

Los elementos de configuración AOP en XML tienen su propósito claro por sus nombres, así que no entraré en muchos detalles al respecto.

Ejemplo de Spring AOP

Vamos a tener un programa simple de Spring y ver cómo todos estos aspectos atraviesan los métodos del 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();
	}
}

Ahora, al ejecutar el programa anterior, obtenemos la siguiente salida.

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)

Puedes ver que los consejos se ejecutan uno por uno según sus configuraciones de pointcut. Debes configurarlos uno por uno para evitar confusiones. Eso es todo para Tutorial de Ejemplo de Spring AOP. Espero que hayas aprendido los conceptos básicos de AOP con Spring y puedas aprender más a través de ejemplos. Descarga el proyecto de muestra desde el siguiente enlace y juega con él.

Descargar Proyecto Spring AOP

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