¿Cómo implementar AOP en la aplicación Spring Boot?

AOP (Programación Orientada a Aspectos) divide el programa completo en diferentes unidades más pequeñas. En numerosas situaciones, necesitamos registrar y auditar los detalles, así como también debemos prestar atención a las transacciones declarativas, la seguridad, el almacenamiento en caché, etc. Veamos las terminologías clave de AOP

  1. Aspecto: Cuenta con un conjunto de APIs para requerimientos transversales. El módulo de registro es un ejemplo del aspecto AOP del registro.
  2. Punto de unión: enchufe de aspecto AOP en su lugar
  3. Consejo: a través de esto, se cuida la implementación real del código para el enfoque AOP. Puede ser antes/después/después de la devolución/después del lanzamiento. En este artículo, veamos los ejemplos relacionados con esto.
  4. Pointcut: Conjunto de uno o más puntos de unión donde se necesita ejecutar un consejo. 
  5. Introducción: Este es el lugar donde podemos agregar nuevos métodos o atributos a las clases existentes.
  6. Objeto de destino: uno o más aspectos estarán allí para brindar asesoramiento para el objeto de destino.
  7. Tejer: vincular aspectos con otros tipos de aplicaciones u objetos. Se puede hacer en tiempo de compilación/tiempo de ejecución/tiempo de carga.

En este artículo, veremos brevemente los diferentes tipos de consejos disponibles.

@Alrededor

Este es el consejo más efectivo y deseable entre todos los demás consejos. El primer parámetro es de tipo ProceedingJoinPoint. El código debe tener proceder() en ProceedingJoinPoint. Cuando se proporciona esto, el código debe ejecutarse antes y después de que el método coincida con el punto de corte.

Fragmento de código de muestra:

En cuanto a la prioridad, siempre se invocará @Around incluso si hay anotaciones @Before, 

Java

// Displays all the available methods i.e. the advice will
// be called for all the methods The method declaration is
// called the pointcut signature. It provides a name that can
// be used by advice annotations to refer to that pointcut.
@Pointcut(
    value
    = "execution(* com.gfg.examples.service.ServiceExample.*(..))")
private void
printLogs()
{
}
 
// Declares the around advice that is applied before and
// after the method matching with a pointcut expression Even
// there are @Before annotations, @Around will be invoked
// first with the before invokation and then only @Before
// will be called
@Around(value = "printLogs()")
public void
logsAroundAdvice(ProceedingJoinPoint proJoinPoint)
    throws Throwable
{
    System.out.println(
        "The method aroundAdvice() before invokation of the method "
        + proJoinPoint.getSignature().getName()
        + " method");
    try {
        proJoinPoint.proceed();
    }
    finally {
    }
    System.out.println(
        "The method aroundAdvice() after invokation of the method "
        + proJoinPoint.getSignature().getName()
        + " method");
}

Como siempre, este es el punto de partida, la lógica empresarial debe procesarse aquí y es ideal.

Posibles casos de uso:

  • Cuando se ha iniciado el cálculo de la nómina de empleados.
  • Cuando un estudiante comenzó a escribir el examen.
  • Cuando se inicia una aplicación externa como una impresora/escáner en el período de tiempo mencionado.

Dado que los consejos de @Around informarán sobre todas y cada una de las actividades antes y después del progreso, serán un punto de referencia y también los errores se pueden diagnosticar fácilmente a través de este enfoque.

@Antes

En cuanto a la prioridad, puede ser el primer paso si no hay un consejo @Around. Si @Around está ahí, siempre @Around tendrá la prioridad más alta y @Before se ejecutará después de la parte inicial de @Around.

Fragmento de código de muestra:

Java

// If there is no @Around advice, @Before will be called
// first, otherwise @Around Before Invocation is called
@Before(
    "execution(* com.gfg.examples.service.ServiceExample.*(..))")
public void
printLogStatementsBefore()
{
 
    System.out.println(
        ".............Looking for @Around advice, if none is there, @Before will be called first. My role is to execute before each and every method.............");
}

Escenario apto para tener @Antes:

Por lo general, en la relación maestro-detalle, el registro maestro debe existir y luego solo se pueden insertar los datos secundarios correspondientes de manera similar durante la eliminación, los datos secundarios deben eliminarse y luego solo se pueden eliminar los datos maestros. @Before ayudará a superar estos escenarios de dependencias desde el principio.

@Después

En cuanto a la prioridad, siempre se ejecutará @Around, si está disponible y, después de eso, solo se ejecutará @After. Este será un buen lugar para concluir el final de una actividad.

Fragmento de código de muestra:

Java

// If there is no @Around advice, @After will be called
// after @Before(if available) first, otherwise @Around After
// Invocation is called
@After(
    "execution(* com.gfg.examples.service.ServiceExample.*(..))")
public void
printLogStatementsAfter()
{
    System.out.println(
        ".............Looking for @Around advice, if none is there, @After will be called after @Before(if available). My role is to execute after each and every method.............");
}

Escenario apto para tener @After:

Para informar a los usuarios sobre la finalización de una operación, como la finalización del proceso de descarga de un archivo o una imagen o el trabajo de impresión completado, etc.,

@Después de regresar

Aunque el consejo @After está ahí, el consejo @AfterReturning indicará el éxito de una operación. Por lo tanto, los pasos de éxito se indicarán aquí.

Fragmento de código de muestra:

Java

// implementing after returning advice
// This is generally used to indicate the output after
// successful return of the method, will be called at last
// i.e. after @Around
@AfterReturning(
    value
    = "execution(* com.gfg.examples.service.ServiceExample.*(..))",
    returning = "account")
public void
logsAfterReturningDisplay(JoinPoint joinPoint)
{
    System.out.println("After Returing method:"
                       + joinPoint.getSignature());
}

Escenario apto para tener @AfterReturning:

Cuando hay actividades como trabajos de impresión, necesitamos conocer el estado de finalización de los trabajos de impresión. Una vez finalizado con éxito, es necesario iniciar otras actividades posteriores. Por lo tanto, las operaciones relacionadas con el éxito se pueden proporcionar en @AfterReturning

@después de lanzar

Hay muchos escenarios de falla de una actividad, por ejemplo, cuando no hay energía, el trabajo de impresión no se puede completar. Durante esos tiempos, es necesario informar sobre la causa de la falla y las medidas de recuperación posteriores. @AfterThrowing es el lugar adecuado para ello.

Fragmento de código de muestra:

Java

// implementing after throwing advice
// This is generally used to indicate the exception in case
// of exception , will be called whenever exception occurs
@AfterThrowing(
    value
    = "execution(* com.gfg.examples.service.ServiceExample.*(..))",
    throwing = "ex")
public void
logsAfterThrowingDisplay(JoinPoint jPoint, Exception ex)
{
    System.out.println("After Throwing exception in method:"
                       + jPoint.getSignature());
    System.out.println("Exception is:" + ex.getMessage());
}

Escenario apto para @AfterThrowing:

Debido a un error de E/S, excepciones aritméticas, excepciones de SQL, etc., debemos proporcionar medidas correctivas.

Combinemos todo y verifiquemos como un proyecto maven de muestra.

Proyecto de ejemplo

Estructura del proyecto:

Project Structure

 

pom.xml

XML

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                             https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
        <relativePath/>
    </parent>
    <groupId>com.gfg.examples</groupId>
    <artifactId>aop-example</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>aop-example</name>
    <description>Demo project for Spring Boot</description>
 
    <properties>
        <java.version>1.8</java.version>
    </properties>
 
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
 
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
 
</project>

Archivos importantes clave

EjemploServicio.java

Java

import org.springframework.stereotype.Service;
 
@Service
public class ServiceExample {
    public void
    getAccountBalance(String employeeAccountNumber)
    {
        System.out.println("Inside getBalance() method");
        // To mention about for a certain
        // employeeAccountNumber value
        if (employeeAccountNumber.equals("Emp1212")) {
            System.out.println("Total balance: ......");
        }
        else {
            System.out.println(
                "Sorry! wrong account number. Please give correct account number to verify");
        }
    }
 
    public String employeeStatus(String employeeNumber)
    {
        System.out.println(
            "Inside checkEmployeeExistence() method");
        String status = null;
        if (employeeNumber.equals("emp12345")) {
            System.out.println(employeeNumber
                               + " is currently active");
            status = "active";
        }
        else {
            System.out.println(employeeNumber
                               + " is currently inactive");
            status = "Inactive";
        }
        return status;
    }
 
    public String
    eligibilityForPromotion(int promotionExamMarks)
    {
        System.out.println(
            "Inside eligibilityForPromotion() method");
        String status = null;
        if (promotionExamMarks >= 650) {
            System.out.println("Eligible for promotion..");
            status = "eligible";
        }
        else {
            System.out.println(
                "Not eligible for promotion..");
            status = "not eligible";
        }
        return status;
    }
}

ImplementaciónDeDiferentesAspectos.java

Java

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
 
// Enables the spring AOP functionality in an application
@Aspect
@Component
public class ImplementationOfDifferentAspect {
    // Displays all the available methods i.e. the advice
    // will be called for all the methods The method
    // declaration is called the pointcut signature. It
    // provides a name that can be used by advice annotations
    // to refer to that pointcut.
    @Pointcut(
        value
        = "execution(* com.gfg.examples.service.ServiceExample.*(..))")
    private void
    printLogs()
    {
    }
    // If there is no @Around advice, @Before will be called
    // first, otherwise @Around Before Invocation is called
    @Before(
        "execution(* com.gfg.examples.service.ServiceExample.*(..))")
    public void
    printLogStatementsBefore()
    {
 
        System.out.println(
            ".............Looking for @Around advice, if none is there, @Before will be called first. My role is to execute before each and every method.............");
    }
    // If there is no @Around advice, @After will be called
    // after @Before(if available) first, otherwise @Around
    // After Invocation is called
    @After(
        "execution(* com.gfg.examples.service.ServiceExample.*(..))")
    public void
    printLogStatementsAfter()
    {
        System.out.println(
            ".............Looking for @Around advice, if none is there, @After will be called after @Before(if available). My role is to execute after each and every method.............");
    }
 
    // implementing after returning advice
    // This is generally used to indicate the output after
    // successful return of the method, will be called at
    // last i.e. after @Around
    // AOP aspect plug in place is JointPoint
    @AfterReturning(
        value
        = "execution(* com.gfg.examples.service.ServiceExample.*(..))",
        returning = "account")
    public void
    logsAfterReturningDisplay(JoinPoint joinPoint)
    {
        System.out.println("After Returing method:"
                           + joinPoint.getSignature());
        // System.out.println(account);
    }
 
    // implementing after throwing advice
    // This is generally used to indicate the exception in
    // case of exception , will be called whenever exception
    // occurs
    @AfterThrowing(
        value
        = "execution(* com.gfg.examples.service.ServiceExample.*(..))",
        throwing = "ex")
    public void
    logsAfterThrowingDisplay(JoinPoint jPoint, Exception ex)
    {
        System.out.println(
            "After Throwing exception in method:"
            + jPoint.getSignature());
        System.out.println("Exception is:"
                           + ex.getMessage());
    }
 
    // Declares the around advice that is applied before and
    // after the method matching with a pointcut expression
    // Even there are @Before annotations, @Around will be
    // invoked first with the before invokation and then only
    // @Before will be called
    @Around(value = "printLogs()")
    public void
    logsAroundAdvice(ProceedingJoinPoint proJoinPoint)
        throws Throwable
    {
        System.out.println(
            "The method aroundAdvice() before invokation of the method "
            + proJoinPoint.getSignature().getName()
            + " method");
        try {
            proJoinPoint.proceed();
        }
        finally {
        }
        System.out.println(
            "The method aroundAdvice() after invokation of the method "
            + proJoinPoint.getSignature().getName()
            + " method");
    }
}

ImplementaciónDeDiferentesConsejos.java

Java

import com.gfg.examples.service.ServiceExample;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
 
@SpringBootApplication
// @EnableAspectJAutoProxy annotation enables support for
// handling the components marked with @Aspect annotation.
@EnableAspectJAutoProxy
public class ImplementationOfDifferentAdvice {
    public static void main(String[] args)
    {
        ConfigurableApplicationContext context
            = SpringApplication.run(
                ImplementationOfDifferentAdvice.class,
                args);
        // Fetching the  object from the application
        // context.
        ServiceExample service
            = context.getBean(ServiceExample.class);
        // checking for an employee available in the
        // organization
        String employeeNumber = "emp12345";
        try {
            service.employeeStatus(employeeNumber);
        }
        catch (Exception ex) {
            System.out.println("Exception occurred.."
                               + ex.getMessage());
        }
        // Displaying balance in the account.
        String employeeAccountNumber = "Emp1212";
        try {
            service.getAccountBalance(
                employeeAccountNumber);
        }
        catch (Exception ex) {
            System.out.println("Exception occurred.."
                               + ex.getMessage());
        }
 
        // Employee has undergone some exams for promotion.
        // Let us check that
        int promotionExamMarks = 650;
        try {
            service.eligibilityForPromotion(
                promotionExamMarks);
        }
        catch (Exception ex) {
            System.out.println("Exception occurred.."
                               + ex.getMessage());
        }
        // Closing the context object.
        context.close();
    }
}

Producción:

Output

 

Explicación:

1. employeeStatus method
// @around advice begins
The method aroundAdvice() before invocation of the method employeeStatus method

// @before advice 
.............Looking for @Around advice, if none is there, @Before will be called first. My role is to execute before each and every method.............

// execution steps
Inside ....() method
emp12345 is currently active

// @around advice ends
The method aroundAdvice() after invocation of the method employeeStatus method

// @after advvice 
.............Looking for @Around advice, if none is there, @After will be called after @Before(if available). My role is to execute after each and every method.............
@afterreturning
After Returing method:String com.gfg.examples.service.ServiceExample.employeeStatus(String)

// Similarly it will be done for other methods

En caso de que hayamos ejecutado el método con entradas que causan excepciones, tal vez en el siguiente ejemplo podamos probar

Java

// Displaying balance in the account.
String employeeAccountNumber = null;
try {
    service.getAccountBalance(employeeAccountNumber);
}
catch (Exception ex) {
    System.out.println("Exception occurred.."
                       + ex.getMessage());
}

Producción:

Output

 

A lo largo de las muestras, si observamos, hemos utilizado una forma adecuada de proporcionar consejos @Around, @Before, @After, @AfterReturning y @AfterThrowing y, de esta manera, podemos implementar la funcionalidad AOP fácilmente.

Publicación traducida automáticamente

Artículo escrito por priyarajtt y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *