JavaRush /Blog Java /Random-ES /¿Qué es AOP? Conceptos básicos de la programación orienta...

¿Qué es AOP? Conceptos básicos de la programación orientada a aspectos

Publicado en el grupo Random-ES
¡Hola, chicos! Sin comprender los conceptos básicos, es bastante difícil profundizar en los marcos y enfoques para crear funcionalidad. Así que hoy hablaremos de uno de estos conceptos: AOP o programación orientada a aspectos . ¿Qué es AOP?  Fundamentos de la programación orientada a aspectos - 1Este no es un tema fácil y no se suele utilizar directamente, pero muchos marcos y tecnologías lo utilizan de forma oculta. Y, por supuesto, a veces, durante las entrevistas, es posible que le pidan que le diga en términos generales qué tipo de animal es y dónde se puede utilizar. Entonces, veamos los conceptos básicos y algunos ejemplos simples de AOP en Java . ¿Qué es el AOP?  Fundamentos de la programación orientada a aspectos - 2Entonces, AOP ( programación orientada a aspectos ) es un paradigma destinado a aumentar la modularidad de varias partes de una aplicación mediante la separación de preocupaciones transversales. Para hacer esto, se agrega un comportamiento adicional al código existente, sin cambiar el código original. En otras palabras, parece que agregamos funcionalidad adicional a los métodos y clases sin realizar modificaciones al código modificado. ¿Por qué es esto necesario? Tarde o temprano llegamos a la conclusión de que el enfoque habitual orientado a objetos no siempre puede resolver eficazmente determinados problemas. En ese momento, AOP viene al rescate y nos brinda herramientas adicionales para construir la aplicación. Y las herramientas adicionales significan una mayor flexibilidad en el desarrollo, gracias a lo cual hay más opciones para resolver un problema en particular.

Aplicación de POA

La programación orientada a aspectos está diseñada para resolver problemas transversales, que pueden ser cualquier código que se repite muchas veces de diferentes maneras, que no se puede estructurar completamente en un módulo separado. En consecuencia, con AOP podemos dejar esto fuera del código principal y definirlo verticalmente. Un ejemplo es la aplicación de una política de seguridad en una aplicación. Normalmente, la seguridad afecta a muchos elementos de una aplicación. Además, la política de seguridad de la aplicación debe aplicarse por igual a todas las partes nuevas y existentes de la aplicación. Al mismo tiempo, la política de seguridad utilizada puede evolucionar. Aquí es donde el uso de AOP puede resultar útil . También otro ejemplo es el registro . Existen varias ventajas al utilizar un enfoque AOP para el registro en comparación con la inserción manual de registros:
  1. El código de registro es fácil de implementar y eliminar: sólo necesita agregar o eliminar un par de configuraciones de algún aspecto.
  2. Todo el código fuente para el registro se almacena en un solo lugar y no es necesario buscar manualmente todos los lugares de uso.
  3. El código destinado al registro se puede agregar en cualquier lugar, ya sean métodos y clases ya escritos o nuevas funciones. Esto reduce la cantidad de errores de desarrollador.
    Además, cuando elimina un aspecto de una configuración de diseño, puede estar absolutamente seguro de que se elimina todo el código de seguimiento y no falta nada.
  4. Los aspectos son código independiente que se puede reutilizar y mejorar una y otra vez.
¿Qué es el AOP?  Fundamentos de la programación orientada a aspectos - 3AOP también se utiliza para el manejo de excepciones, el almacenamiento en caché y la eliminación de algunas funciones para que sea reutilizable.

Conceptos básicos de AOP

Para avanzar en el análisis del tema, primero familiaricémonos con los conceptos principales de AOP. El consejo es lógica adicional, código, que se llama desde el punto de conexión. El asesoramiento se puede realizar antes, después o en lugar del punto de conexión (más sobre ellos a continuación). Posibles tipos de asesoramiento :
  1. Antes (Antes) : los consejos de este tipo se lanzan antes de la ejecución de los métodos de destino: puntos de conexión. Cuando usamos aspectos como clases, tomamos la anotación @Before para marcar el tipo de consejo como anterior. Cuando utilice aspectos como archivos .aj , este será el método before() .
  2. Después (Después) : consejo que se ejecuta después de completar la ejecución de los métodos: puntos de conexión, tanto en casos normales como cuando se lanza una excepción.
    Cuando usamos aspectos como clases, podemos usar la anotación @After para indicar que este es un consejo que viene después.
    Cuando utilice aspectos como archivos .aj , este será el método after() .
  3. Después del regreso : estos consejos se ejecutan solo si el método de destino funciona normalmente, sin errores.
    Cuando los aspectos se representan como clases, podemos usar la anotación @AfterReturning para marcar el consejo como ejecutado tras su finalización exitosa.
    Cuando se utilizan aspectos como archivos .aj, este será el método after() que devolverá (Object obj) .
  4. Después del lanzamiento : este tipo de consejo está destinado a aquellos casos en los que un método, es decir, un punto de conexión, genera una excepción. Podemos utilizar este consejo para manejar la ejecución fallida (por ejemplo, revertir toda la transacción o iniciar sesión con el nivel de seguimiento requerido).
    Para las clases de aspectos, la anotación @AfterThrowing se usa para indicar que este consejo se usa después de que se lanza una excepción.
    Cuando se utilizan aspectos en forma de archivos .aj , este será el método: lanzamiento después () (Excepción e) .
  5. Around es quizás uno de los tipos de consejos más importantes que rodean a un método, es decir, a un punto de conexión, con el que podemos, por ejemplo, elegir si ejecutamos un determinado método de punto de conexión o no.
    Puede escribir código de consejo que se ejecute antes y después de que se ejecute el método del punto de unión.
    Las responsabilidades del consejo incluyen llamar al método del punto de unión y devolver valores si el método devuelve algo. Es decir, en este consejo puedes simplemente imitar el funcionamiento del método de destino sin llamarlo y devolver algo propio como resultado.
    Para aspectos en forma de clases, utilizamos la anotación @Around para crear sugerencias que envuelven el punto de conexión. Cuando utilice aspectos como archivos .aj , este será el método around() .
Punto de unión : un punto en un programa en ejecución (llamar a un método, crear un objeto, acceder a una variable) donde se deben aplicar los consejos. En otras palabras, se trata de una especie de expresión regular con la que se encuentran lugares para introducir código (lugares para aplicar sugerencias). Un pointcut es un conjunto de puntos de conexión . El corte determina si un punto de conexión determinado se ajusta a una punta determinada. Aspect es un módulo o clase que implementa una funcionalidad de un extremo a otro. Un aspecto modifica el comportamiento del resto del código aplicando consejos en puntos de unión definidos por algún segmento . En otras palabras, es una combinación de puntas y puntos de conexión. Introducción : cambiar la estructura de una clase y/o cambiar la jerarquía de herencia para agregar funcionalidad de aspecto al código externo. El objetivo es el objeto al que se aplicará el consejo. Weaving es el proceso de vincular aspectos con otros objetos para crear los objetos proxy recomendados. Esto se puede hacer en tiempo de compilación, tiempo de carga o tiempo de ejecución. Hay tres tipos de tejido:
  • Tejido en tiempo de compilación : si tiene el código fuente de un aspecto y el código en el que usa los aspectos, puede compilar el código fuente y el aspecto directamente usando el compilador AspectJ;
  • tejido posterior a la compilación (tejido binario): si no puede o no quiere utilizar transformaciones del código fuente para entrelazar aspectos en su código, puede tomar clases o archivos jar ya compilados e inyectar aspectos;
  • El tejido en tiempo de carga es simplemente un tejido binario diferido hasta que el cargador de clases carga el archivo de clase y define la clase para la JVM.
    Para respaldar esto, se requieren uno o más "cargadores de clases de tejido". Son proporcionados explícitamente por el tiempo de ejecución o activados por el "agente de tejido".
AspectJ es una implementación específica de paradigmas AOP que implementa la capacidad de resolver problemas transversales. La documentación se puede encontrar aquí .

Ejemplos en Java

A continuación, para comprender mejor AOP, veremos pequeños ejemplos del nivel Hello World. ¿Qué es el AOP?  Fundamentos de la programación orientada a aspectos - 4Permítanme señalar de inmediato que en nuestros ejemplos usaremos tejido en tiempo de compilación . Primero necesitamos agregar la siguiente dependencia a nuestro pom.xml :
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjrt</artifactId>
  <version>1.9.5</version>
</dependency>
Como regla general, se utiliza un compilador Ajs especial para utilizar aspectos . IntelliJ IDEA no lo tiene de forma predeterminada, por lo que al elegirlo como compilador de aplicaciones debe especificar la ruta a la distribución de AspectJ . Puede leer más sobre el método para elegir Ajs como compilador en esta página. Este fue el primer método y el segundo (que usé) fue agregar el siguiente complemento a pom.xml :
<build>
  <plugins>
     <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>aspectj-maven-plugin</artifactId>
        <version>1.7</version>
        <configuration>
           <complianceLevel>1.8</complianceLevel>
           <source>1.8</source>
           <target>1.8</target>
           <showWeaveInfo>true</showWeaveInfo>
           <verbose>true</verbose>
           <Xlint>ignore</Xlint>
           <encoding>UTF-8</encoding>
        </configuration>
        <executions>
           <execution>
              <goals>
                 <goal>compile</goal>
                 <goal>test-compile</goal>
              </goals>
           </execution>
        </executions>
     </plugin>
  </plugins>
</build>
Después de esto, es recomendable volver a importar desde Maven y ejecutar mvn clean compile . Ahora pasemos a los ejemplos.

Ejemplo No. 1

Creemos una clase principal . En él tendremos un punto de inicio y un método que imprime los nombres que se le pasan en la consola:
public class Main {

  public static void main(String[] args) {
  printName("Толя");
  printName("Вова");
  printName("Sasha");
  }

  public static void printName(String name) {
     System.out.println(name);
  }
}
Nada complicado: le pasaron el nombre y lo mostraron en la consola. Si lo ejecutamos ahora, la consola mostrará:
Tolya Vova Sasha
Bueno, es hora de aprovechar el poder de AOP. Ahora necesitamos crear un aspecto de archivo . Vienen en dos tipos: el primero es un archivo con la extensión .aj , el segundo es una clase normal que implementa capacidades AOP mediante anotaciones. Primero veamos un archivo con extensión .aj :
public aspect GreetingAspect {

  pointcut greeting() : execution(* Main.printName(..));

  before() : greeting() {
     System.out.print("Привет ");
  }
}
Este archivo es algo similar a una clase. Averigüemos qué está pasando aquí: pointcut : un corte o un conjunto de puntos de conexión; saludo() : el nombre de este segmento; : ejecución - al ejecutar * - todo, llame - Main.printName(..) - este método. Luego viene el consejo específico, antes() , que se ejecuta antes de que se llame al método de destino : saludo() , el segmento al que reacciona este consejo, y debajo vemos el cuerpo del método en sí, que está escrito en Java. lenguaje que entendemos. Cuando ejecutamos main con este aspecto presente, obtendremos el siguiente resultado en la consola:
Hola Tolya Hola Vova Hola Sasha
Podemos ver que cada llamada al método printName ha sido modificada por un aspecto. Ahora echemos un vistazo a cómo se verá el aspecto, pero como una clase Java con anotaciones:
@Aspect
public class GreetingAspect{

  @Pointcut("execution(* Main.printName(String))")
  public void greeting() {
  }

  @Before("greeting()")
  public void beforeAdvice() {
     System.out.print("Привет ");
  }
}
Después del archivo de aspecto .aj , todo es más obvio:
  • @Aspect denota que la clase dada es un aspecto;
  • @Pointcut("execution(* Main.printName(String))") es un punto de corte que se activa en todas las llamadas a Main.printName con un argumento entrante de tipo String ;
  • @Before("greeting()") : consejo que se aplica antes de llamar al código descrito en el punto de corte del saludo() .
Ejecutar main con este aspecto no cambiará la salida de la consola:
Hola Tolya Hola Vova Hola Sasha

Ejemplo No. 2

Digamos que tenemos algún método que realiza algunas operaciones para los clientes y llamamos a este método desde main :
public class Main {

  public static void main(String[] args) {
  makeSomeOperation("Толя");
  }

  public static void makeSomeOperation(String clientName) {
     System.out.println("Выполнение некоторых операций для клиента - " + clientName);
  }
}
Usando la anotación @Around , crearemos algo así como una "pseudo-transacción":
@Aspect
public class TransactionAspect{

  @Pointcut("execution(* Main.makeSomeOperation(String))")
  public void executeOperation() {
  }

  @Around(value = "executeOperation()")
  public void beforeAdvice(ProceedingJoinPoint joinPoint) {
     System.out.println("Открытие транзакции...");
     try {
        joinPoint.proceed();
        System.out.println("Закрытие транзакции....");
     }
     catch (Throwable throwable) {
        System.out.println("Операция не удалась, откат транзакции...");
     }
  }
  }
Usando el método proceder del objeto ProceedingJoinPoint , llamamos al método del contenedor para determinar su lugar en el tablero y, en consecuencia, el código en el método anterior joinPoint.proceed(); - esto es Antes , que está debajo - Después . Si ejecutamos main obtendremos en la consola:
Abrir una transacción... Realizar algunas operaciones para el cliente - Tolya Cerrar una transacción....
Si agregamos un lanzamiento de excepción a nuestro método (de repente la operación falla):
public static void makeSomeOperation(String clientName)throws Exception {
  System.out.println("Выполнение некоторых операций для клиента - " + clientName);
  throw new Exception();
}
Luego obtendremos la salida en la consola:
Abriendo una transacción... Realizando algunas operaciones para el cliente - Tolya La operación falló, la transacción fue revertida...
Resultó ser un pseudoprocesamiento del fracaso.

Ejemplo No. 3

Como en el siguiente ejemplo, hagamos algo como iniciar sesión en la consola. Primero, veamos Main , donde ocurre nuestra pseudo lógica de negocios:
public class Main {
  private String value;

  public static void main(String[] args) throws Exception {
     Main main = new Main();
     main.setValue("<некоторое significado>");
     String valueForCheck = main.getValue();
     main.checkValue(valueForCheck);
  }

  public void setValue(String value) {
     this.value = value;
  }

  public String getValue() {
     return this.value;
  }

  public void checkValue(String value) throws Exception {
     if (value.length() > 10) {
        throw new Exception();
     }
  }
}
En main , usando setValue, estableceremos el valor de la variable interna - value , luego usando getValue, tomaremos este valor y en checkValue verificaremos si este valor tiene más de 10 caracteres. En caso afirmativo, se lanzará una excepción. Ahora veamos el aspecto con el que registraremos el funcionamiento de los métodos:
@Aspect
public class LogAspect {

  @Pointcut("execution(* *(..))")
  public void methodExecuting() {
  }

  @AfterReturning(value = "methodExecuting()", returning = "returningValue")
  public void recordSuccessfulExecution(JoinPoint joinPoint, Object returningValue) {
     if (returningValue != null) {
        System.out.printf("Успешно выполнен метод - %s, класса- %s, с результатом выполнения - %s\n",
              joinPoint.getSignature().getName(),
              joinPoint.getSourceLocation().getWithinType().getName(),
              returningValue);
     }
     else {
        System.out.printf("Успешно выполнен метод - %s, класса- %s\n",
              joinPoint.getSignature().getName(),
              joinPoint.getSourceLocation().getWithinType().getName());
     }
  }

  @AfterThrowing(value = "methodExecuting()", throwing = "exception")
  public void recordFailedExecution(JoinPoint joinPoint, Exception exception) {
     System.out.printf("Метод - %s, класса- %s, был аварийно завершен с исключением - %s\n",
           joinPoint.getSignature().getName(),
           joinPoint.getSourceLocation().getWithinType().getName(),
           exception);
  }
}
¿Que está pasando aqui? @Pointcut("execution(* *(..))") - se conectará a todas las llamadas a todos los métodos; @AfterReturning(value = "methodExecuting()", return = "returningValue") : consejo que se ejecutará después de que el método de destino se haya completado con éxito. Tenemos dos casos aquí:
  1. Cuando un método tiene un valor de retorno if (returningValue! = null) {
  2. Cuando no hay ningún valor de retorno más {
@AfterThrowing(value = "methodExecuting()", throwing = "exception") - consejo que se activará en caso de un error, es decir, cuando se lanza una excepción desde el método. Y en consecuencia, al ejecutar main , obtendremos una especie de registro en la consola:
El método - setValue, de la clase - Main fue ejecutado exitosamente. El método - getValue, de la clase - Main, fue ejecutado exitosamente, con el resultado de la ejecución - <algún valor>. El método - checkValue, de la clase - Main, se terminó de forma anormal con una excepción - java.lang.Exception Método - principal, clase-Main, falló con una excepción - java.lang.Exception
Bueno, como no manejamos la excepción, también obtendremos su seguimiento de pila: ¿Qué es el AOP?  Fundamentos de la programación orientada a aspectos - 5Puede leer sobre las excepciones y su manejo en estos artículos: Excepciones en Java y Excepciones y su manejo . Eso es todo para mí hoy. Hoy nos familiarizamos con AOP y pudiste ver que esta bestia no da tanto miedo como parece. ¡Adiós a todos!¿Qué es el AOP?  Fundamentos de la programación orientada a aspectos - 6
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION