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:
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:
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()); }
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