Aspectos con Spring Boot (AOP)
Aspect Oriented Programming
Aspectos con Spring Boot (AOP)
Aspect Oriented Programming
Muchas veces cuando estamos desarrollando una aplicación con Spring Boot, nos encontramos con código que puede repetirse mucho en varias clases de nuestro proyecto, lo cual puede llegar a ser tedioso estar creando, otro problema con el que podemos estar lidiando es con el cruce de funcionalidades, esto quiere decir que dentro de nuestras clases estamos agregando código que realiza para otras operaciones que no están relacionadas con la funcionalidad principal de dicha clase, algunos ejemplos pueden ser el loggin de nuestra aplicación o parte de la seguridad, como roles de usuario y ambos casos podrían estar dentro de una misma clase, a esto es a lo que se le llama cruce de funcionalidades.
Algo que debemos aclarar es que Spring AOP, no lo debemos confundir con AspectJ, aun que son para la misma funcionalidad ambos tienen diferencias significativas, de las cuales no hablaremos en este artículo.
Ahora imaginemos que tenemos tres componentes y esto implementan una interfase y queremos que impriman un mensaje cuando entren en cada uno de sus métodos, lo podríamos resolver de diferentes formas, pero lo más simple que haríamos sería lo siguiente:
- Declarar nuestro loggin en la clase.
- Poner el mensaje en cada uno de los métodos.
interface ClassService{
fun methodA():Unit
}
@Service
class ClassServiceA:ClassService{
var log: Logger = LogManager.getLogger(ClassServiceA::class)
override fun methodA(){
log.info("methodA")
}
}
@Service
class ClassServiceB:ClassService{
var log: Logger = LogManager.getLogger(ClassServiceB::class)
override fun methodA(){
log.info("methodA")
}
}
@Service
class ClassServiceC:ClassService{
var log: Logger = LogManager.getLogger(ClassServiceC::class)
override fun methodA(){
log.info("methodA")
}
}
Como vemos nuestro código se vuelve repetitivo y mezclamos funcionalidades, es decir el loggin con la funcionalidad de lo que haría cada un de nuestros métodos en cada una de sus clases.
Para resolver estos problemas tenemos los Aspectos en Spring, los cuales nos ayudaran a separar estas funcionalidades de nuestras clases de negocio.
Pero ¿Cómo funcionan estos aspectos?, ¿Cómo los implementamos?, antes de saber todo esto debemos aprender ciertos conceptos que nos ayudaran a entender mejor los aspectos.
a) PointCut: Es una expresión la cual nos ayudara a seleccionar uno o varios Join Points.
b)Join Point: Es un punto en la ejecución de un programa, como la llamada a un método o el lanzamiento de una excepción.
c)Advice: Es el código que se va a ejecutar cada vez que se seleccione un Join Point.
d)Aspect: Es un módulo que encapsula los PointCuts y Advice.
e)Weaving: Es la técnica con la cual son combinados los aspectos con el código principal.
¿Cómo funciona?
Bien una vez sabiendo esto, veamos como funcionan los aspectos, básicamente lo que hacen es encapsular en un AOP proxy nuestras clases o mejor dicho las interfaces que implementan nuestros componentes y todos los métodos que están en dicha interfase podrán ser seleccionados por nuestros Pointcuts.

Los métodos de nuestros componentes son llamados y si estos coinciden con un pointcut son enrutados a través del method interceptor, este método ejecuta el advice del aspecto y ejecuta el target de nuestro proxy, el target representa nuestra interfaz, y en esta se llama al metodo que se va a ejecutar, esta puede regresar una respuesta correcta o una exepción.
Podrán encontrar más información sobre como funciona los aspectos en: spring.io
¿Como implementarlo?
Para implementar un aspecto podemos crear una o varias clases de configuración, esto puede variar, con respecto a las necesidades de nuestro proyecto, como buena práctica podemos separar nuestros aspectos por funcionalidad.

Una vez creada nuestra clase de configuración agregamos la anotación @Aspect para habilitar los aspectos en spring.
Bien una vez echo esto podemos comenzar a crear nuestros pointcuts, los pointcut sabemos que son expresiones, estas se componen de la siguiente forma:
execution(<<method pattern>>)
Podemos utilizar operadores lógicos dentro de nuestra expresión para poder crear expresiones más complejas.
a) && (AND)
b) || (OR)
c) ! (NOT)
execution(<pattern1>) || execution(<pattern2>)
La estructura de nuestros patterns debe ser la siguiente:
[Modifiers]ReturnType[ClassType]MethodName(Arguments)[thows ExceptionType]

-
- : Hace match con solo un resultado.
- .. : Puede hacer match cuando se encuentra un valor de zero a muchos.
Tipos de Advice
Existen diferentes advices que nos ayudan a configurar nuestros aspectos para diferentes casos, se pueden utilizar diferentes advices para un mismo método.
Para ejemplificar las notaciones que veremos utilizaremos el ejemplo descrito en la parte de arriba, donde tenemos tres componentes que implementan una misma interfase, la cual se modificó un poco para poder mostrar como funciona cada advice.
El código está escrito en el lenguaje Kotlin, pero es exactamente la misma implementación para java.
interface ClassService{
fun methodA(a:String,b:String):Int
}
@Service
class ClassServiceA:ClassService{
override fun methodA(a:String,b:String):Int{
return a.toInt()+b.toInt()
}
}
@Service
class ClassServiceB:ClassService{
override fun methodA(a:String,b:String):Int{
return a.toInt()-b.toInt()
}
}
@Service
class ClassServiceC:ClassService{
override fun methodA(a:String,b:String):Int{
return a.toInt()*b.toInt()
}
}
- @Before : Una vez seleccionado un join point se ejecuta el código de este advice y luego se ejecuta el target, es decir el código del método que fue llamado.
@Aspect
@Component
class ConfigAspects {
private val logger: Logger = Logger.getLogger(ConfigAspects::class.java.toString())
@Before("execution(* methodA(..))")
fun loggingController(jp: JoinPoint) {
logger.info("Se ejecuto el metodo ${jp.signature.name}")
}
}
Cuando se ejecuta el método “methodA(a:String,b:String)”, este entrar en nuestro AOP Proxy, se ejecuta nuestro advice y en seguida se ejecuta el código del método que se llamó.

Result @Before
Podemos obtener el joinpoint y sacar información de él cómo el método que se ejecutó, la clase del objeto, etc.
- @AfterReturning: Este es la inversa de @Before y la diferencia es que podemos obtener el objeto de respuesta para poderlo usar en nuestro Advice, solo si obtener una respuesta correcta del target.
@AfterReturning(value = "execution(* methodA(..))", returning = "result")
fun loggingUsers(jp: JoinPoint, result: Int) {
logger.info("${jp.signature.declaringType} Resutl $result")
}
Una vez que se ejecuta el metodo “methodA()” obtenemos la clase que se con el metodo que se ejecuto y el resultado de este.

Result @AfterReturning
- @AfterThrowing: @AfterReturning funciona igual que @AfterThrowing, la diferencia radique en que @AfterThrowing solo se ejecuta cuando ocurre una excepción en el método, la cual podemos cachar, sin que se interrumpa la línea de este.
@AfterThrowing(value="execution(* methodA(..))",throwing = "e")
fun loggingThrowing(jp: JoinPoint, e: NumberFormatException){
logger.info("Mensaje exception ${e.message}")
}
En este caso el methodA le pasamos un string la cual fue una letra y al intentarlo castear a Integer nos regresa la excepción NumberFormatException, con el cual solo imprimimos el mensaje de la excepción.

Result @AfterThrowing
- @Around :Este advice nos es útil porque nuestro método no se ejecuta hasta que nosotros ejecutamos el método proceed() de nuestro joint point, esto nos permite realizar acciones antes y después, e incluso si nuestro método tiene alguna excepción la podemos cachar.
@Around("execution(* methodA(..))")
fun loggingAround(jp: ProceedingJoinPoint): Any? {
logger.info("No se ha ejecutado el metodos aún")
var value = jp.proceed()
logger.info("Se ejcuto el metodo : ${jp.signature.name} y el resultado fue : $value")
return value
}
En este caso solo imprimimos un mensaje antes de ejecutar nuestro método y después imprimimos el método que se ejecutó y su resultado.

Result @Around
Como hemos visto los aspectos son una parte muy buena que nos proporciona spring la cual nos ayuda a mantener y evitar que el código sea repetitivo y mezcle las funcionalidades en nuestras clases.
@Poincut
Otra parte importante de los Aspectos son Poincuts, estos nos permite darle un nombre a nuestras expresiones y utilizarlas en nuestros Advice, con lo cual podemos reutilizarlas y no tener que escribirlas de nuevo si la utilizamos en diferentes aspectos, estas pueden estar dentro de nuestras clases de configuración de aspectos o incluso fuera de ellas como clases separadas.
El código que está abajo muestra como podemos crear nuestros poincuts.
class PoinCutsNames{
@Pointcut("execution(* methodB(..))")
fun logginMethodBClassService(){}
}
@Aspect
@Component
class ConfigAspects {
private val logger: Logger = Logger.getLogger(ConfigAspects::class.java.toString())
@Before("logginMethodClassService() || com.example.userservice.PoinCutsNames.logginMethodBClassService()")
fun loggingController(jp: JoinPoint) {
logger.info("Se ejecuto el metodo ${jp.signature.name} de la clase : ${jp.target}")
}
@AfterReturning(value = "logginMethodClassService() || com.example.userservice.PoinCutsNames.logginMethodBClassService()", returning = "result")
fun loggingUsers(jp: JoinPoint, result: Int) {
logger.info("${jp.signature.declaringType} Resutl $result")
}
@AfterThrowing(value="logginMethodClassService() || com.example.userservice.PoinCutsNames.logginMethodBClassService()",throwing = "e")
fun loggingThrowing(jp: JoinPoint, e: NumberFormatException){
logger.info("Mensaje exception ${e.message}")
}
@Around("logginMethodClassService() || com.example.userservice.PoinCutsNames.logginMethodBClassService()")
fun loggingAround(jp: ProceedingJoinPoint): Any? {
logger.info("No se ha ejecutado el metodos aún")
var value = jp.proceed()
logger.info("Se ejcuto el metodo : ${jp.signature.name} y el resultado fue : $value")
return value
}
@Pointcut("execution(* methodA(..))")
fun logginMethodClassService(){}
}
Ventajas
- Nos permiten crear expresiones muy complejas y colocarlas de forma más fácil en nuestros aspectos.
- Nos ayuda a no estar reescribiendo las expresiones.
- Podemos tener por separado nuestros aspectos y expresiones.
- Podemos reutilizar los pointcut
Conclusión
Los aspectos nos ayudan a separar y organizar de mejor forma nuestro código, evitando que nuestras clases estén llenas de funcionalidades que no tienen que ver nada con la propia de la clase, lo que hace que nuestro código sea más legible para cualquier persona que lo tenga que leer.
Los aspectos son un tema muy extenso por lo que en este artículo lo resumimos para encontrar más información puedes consultar la documentación de spring.
메타데이터
- post_id
- fd90cc0c77d2
- slug
- aspectos-con-spring-boot-aop-fd90cc0c77d2
- url
- https://medium.com/@alexmark140/aspectos-con-spring-boot-aop-fd90cc0c77d2
- canonical_url
- https://medium.com/@alexmark140/aspectos-con-spring-boot-aop-fd90cc0c77d2
- author_url
- https://medium.com/@alexmark140
- status
- ok
- fetched_at
- 2026-08-26 22:14:32