Uso de @Before, @After, @Around, @AfterReturning y @AfterThrowing en un solo proyecto Spring AOP

Spring AOP ha creado diferentes anotaciones, a saber, @Before, @After, @Around, @AfterReturning y @AfterThrowing. Por lo general, se producen confusiones entre los desarrolladores al escribir el código apto entre estas anotaciones. En este artículo, veámoslos usando una aplicación de muestra. 

@Alrededor

Este es el consejo más eficaz entre todos los demás consejos. El primer parámetro es de tipo ProceedingJoinPoint. El código debe contener proceder() en ProceedingJoinPoint y hace que se ejecuten las líneas de código subyacentes. Contiene el código que debe ejecutarse antes y después de que el método coincida con el punto de corte.

Fragmento de muestra

Lo más importante es que incluso si hay anotaciones @Before, @Around se invocará primero con la invocación before y luego solo @Before se llamará.

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.examples.service.GeneralService.*(..))")
private void
logDisplay()
{
}
 
// 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 = "logDisplay()")
public void aroundAdvice(ProceedingJoinPoint jp)
    throws Throwable
{
    System.out.println(
        "The method aroundAdvice() before invokation of the method "
        + jp.getSignature().getName() + " method");
    try {
        jp.proceed();
    }
    finally {
    }
    System.out.println(
        "The method aroundAdvice() after invokation of the method "
        + jp.getSignature().getName() + " method");
}

Por lo tanto, este siempre es un lugar ideal y bueno para realizar la lógica comercial y ayudará a informar lo que sucede antes y después de la aplicación cuando se ejecuta el código.

Posibles casos de uso

  • Cuando el usuario está descargando una imagen en una aplicación
  • Cuando un empleado inicia sesión en la oficina diariamente.
  • Cuando una aplicación externa como una impresora/escáner comenzó su trabajo en el tiempo especificado, etc.

En todos estos escenarios, el asesoramiento @Around ayuda a proporcionar los registros antes y después, y también el servicio funciona perfectamente.

@Antes 

Este consejo se ejecutará como un primer paso si no hay un consejo @Around. Si @Around está ahí, se ejecutará después de la parte inicial de @Around.

Fragmento de muestra

Java

// If there is no @Around advice, @Before will be called
// first, otherwise @Around Before Invocation is called
@Before(
    "execution(* com.examples.service.GeneralService.*(..))")
public void
logBefore()
{
 
    System.out.println(
        ".............I WILL EXECUTE BEFORE EACH AND EVERY METHOD.............");
}

Posible escenario para tener @Before:

Por ejemplo, si hay dos tablas (principal y secundaria) y cuando se produce una inserción en la tabla secundaria, debemos comprobar si ya existe o no el padre correspondiente. Se puede comprobar con @Before 

@Después

Este consejo se ejecutará como un paso después del consejo @Antes si no hay un consejo @Alrededor. Si @Around está ahí, se ejecutará después de la parte final de @Around.

Fragmento 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.examples.service.GeneralService.*(..))")
public void
logAfter()
{
    System.out.println(
        ".............I WILL EXECUTE AFTER EACH AND EVERY METHOD.............");
}

Posible escenario para tener @After:

Por ejemplo, si se descarga una imagen o un mp4, debemos informar a los usuarios finales sobre el proceso de descarga completo. Todos se manejan en @After 

@Después de regresar

Este consejo se ejecutará como un paso después del consejo @After. Por lo general, este es el lugar donde necesitamos informar sobre el resultado exitoso del método. Si el método SuccessSnippetfully regresa, @AfterReturning tendrá un lugar para indicar los resultados.

Fragmento 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.examples.service.GeneralService.*(..))",
    returning = "account")
public void
afterReturningAdvice(JoinPoint joinPoint)
{
    System.out.println("After Returning method:"
                       + joinPoint.getSignature());
}

Posible escenario para tener @AfterReturning:

El resultado del método, ya sea éxito/fracaso, sus causas fundamentales y el resultado final, se pueden mencionar aquí.

@después de lanzar

Este paso se ejecutará siempre que haya una excepción en el código. Necesitamos manejar eso colocando bloques try-catch y siempre es una buena práctica manejar excepciones. En AOP, por medio de @AfterThrowing, se maneja.

Fragmento 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.examples.service.GeneralService.*(..))",
    throwing = "ex")
public void
afterThrowingAdvice(JoinPoint joinPoint, Exception ex)
{
    System.out.println("After Throwing exception in method:"
                       + joinPoint.getSignature());
    System.out.println("Exception is:" + ex.getMessage());
}

Posible escenario para tener @AfterThrowing:

Siempre que haya excepciones verificadas como IOExceptions (archivo no encontrado al intentar descargar) o excepciones no verificadas como excepción aritmética (división por cero debido a algunos cálculos), este es el lugar donde ingresará el código y realizará los pasos que están presentes aquí.

Al combinar todos los consejos anteriores, construyamos un proyecto maven de muestra para contener todos los consejos. 

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.examples</groupId>
    <artifactId>aop-different-advice-example</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>aop-around-advice-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>
       
          <!-- This is the much required dependency to have various advices -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
       
        <!-- For testing -->
        <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>

Veamos la parte de Java

DiferenteAdviceExampleApplication.java

Java

import com.examples.service.GeneralService;
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 DifferentAdviceExampleApplication {
    public static void main(String[] args)
    {
        ConfigurableApplicationContext context
            = SpringApplication.run(
                DifferentAdviceExampleApplication.class,
                args);
        // Fetching the  object from the application
        // context.
        GeneralService service
            = context.getBean(GeneralService.class);
        // checking for an employee available in the
        // organization
        String employeeNumber = "A123";
        try {
            service.checkEmployeeExistence(employeeNumber);
        }
        catch (Exception ex) {
            System.out.println("Exception occurred.."
                               + ex.getMessage());
        }
        // Displaying balance in the account.
        String accnumber = "10000";
        try {
            service.getBalance(accnumber);
        }
        catch (Exception ex) {
            System.out.println("Exception occurred.."
                               + ex.getMessage());
        }
        // Closing the context object.
        context.close();
    }
}

DiferenteAspecto.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 DifferentAspect {
    // 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.examples.service.GeneralService.*(..))")
    private void
    logDisplay()
    {
    }
    // If there is no @Around advice, @Before will be called
    // first, otherwise @Around Before Invocation is called
    @Before(
        "execution(* com.examples.service.GeneralService.*(..))")
    public void
    logBefore()
    {
 
        System.out.println(
            ".............I WILL 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.examples.service.GeneralService.*(..))")
    public void
    logAfter()
    {
        System.out.println(
            ".............I WILL 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
    @AfterReturning(
        value
        = "execution(* com.examples.service.GeneralService.*(..))",
        returning = "account")
    public void
    afterReturningAdvice(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.examples.service.GeneralService.*(..))",
        throwing = "ex")
    public void
    afterThrowingAdvice(JoinPoint joinPoint, Exception ex)
    {
        System.out.println(
            "After Throwing exception in method:"
            + joinPoint.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 = "logDisplay()")
    public void
    aroundAdvice(ProceedingJoinPoint proceedingJoinPoint)
        throws Throwable
    {
        System.out.println(
            "The method aroundAdvice() before invokation of the method "
            + proceedingJoinPoint.getSignature().getName()
            + " method");
        try {
            proceedingJoinPoint.proceed();
        }
        finally {
        }
        System.out.println(
            "The method aroundAdvice() after invokation of the method "
            + proceedingJoinPoint.getSignature().getName()
            + " method");
    }
}

ServicioGeneral.java

Java

import org.springframework.stereotype.Service;
 
@Service
public class GeneralService {
    public void getBalance(String accNum)
    {
        System.out.println("Inside getBalance() method");
        if (accNum.equals("12345")) {
            System.out.println("Total balance: ......");
        }
        else {
            System.out.println(
                "Sorry! wrong account number.");
        }
    }
 
    // second method
    // we can write any methods and when called, all methods
    // follow the same flow
    public String
    checkEmployeeExistence(String employeeNumber)
    {
        System.out.println(
            "Inside checkEmployeeExistence() method");
        String status = null;
        if (employeeNumber.equals("A123")) {
            System.out.println(employeeNumber
                               + " is currently active");
            status = "active";
        }
        else {
            System.out.println(employeeNumber
                               + " is currently inactive");
            status = "Inactive";
        }
        return status;
    }
}

Ejecución y salida del programa:

 

Producción:

Output

 

Explicación:

1. checkEmployeeExistence method
// @around advice begins
The method aroundAdvice() before invokation of the method checkEmployeeExistence method

// @before advice 
.............I WILL EXECUTE BEFORE EACH AND EVERY METHOD.............

// execution steps
Inside checkEmployeeExistence() method
A123 is currently active

// @around advice ends
The method aroundAdvice() after invokation of the method checkEmployeeExistence method

// @after advvice 
.............I WILL EXECUTE AFTER EACH AND EVERY METHOD.............
@afterreturning
After Returing method:String com.examples.service.GeneralService.checkEmployeeExistence(String)

// Similarly it will be done for getBalance

Veamos cuándo se lanza una excepción para el método getBalance simplemente pasando el número de cuenta a nulo

Java

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

 

Explicación:

// @around advice begins
The method aroundAdvice() before invokation of the method getBalance method
@before advice
.............I WILL EXECUTE BEFORE EACH AND EVERY METHOD.............
// execution steps
Inside getBalance() method

// An exception is occurred and hence it will come out and print data from @after advice 
.............I WILL EXECUTE AFTER EACH AND EVERY METHOD.............

@AfterThrowing advice
After Throwing exception in method:void com.examples.service.GeneralService.getBalance(String)
Exception is:Cannot invoke "String.equals(Object)" because "accNum" is null
Exception occurred..Cannot invoke "String.equals(Object)" because "accNum" is null

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 *