La programación orientada a aspectos (AOP) es un paradigma de programación que es un desarrollo adicional de la programación orientada a objetos y procedimientos (OOP). La idea de AOP es resaltar la llamada funcionalidad de extremo a extremo. Y así, todo está en orden, aquí mostraré cómo hacerlo en Java: estilo de anotación Spring @AspectJ (también hay un estilo xml basado en esquema, la funcionalidad es similar).
Destacando la funcionalidad de extremo a extremo
A

y despues

Es decir existe una funcionalidad que afecta a varios módulos, pero no se relaciona directamente con el código comercial, y sería bueno colocarlo en un lugar separado, esto se muestra en la figura anterior.
Punto de unión

Punto de unión: el siguiente concepto de AOP, estos son los puntos de observación, la adhesión al código donde se planea la introducción de la funcionalidad.
Pointcut

Un punto de corte es un segmento, una consulta de puntos de conexión, puede ser uno o más puntos. Las reglas para consultar puntos son muy diversas, en la figura anterior, una solicitud de anotación sobre un método y un método específico. Las reglas se pueden combinar con &&, ||,!
Consejos

Consejo: un conjunto de instrucciones ejecutadas en los puntos de corte (Pointcut). Las instrucciones se pueden realizar en un evento de varios tipos:
- Antes - antes de una llamada al método
- Después - después de una llamada al método
- Después de devolver : después de devolver un valor de una función
- Después de tirar - en caso de excepción
- Después de finalmente : si se ejecuta el bloque de fin
- Alrededor : puede hacer el procesamiento previo, posterior y anterior a la llamada al método y, en general, omitir la llamada al método.
en un Pointcut puede "colgar" varios consejos de diferentes tipos.
Aspecto

Aspecto: un módulo que contiene descripciones de Pointcut y consejos.
Ahora daré un ejemplo y, finalmente, todo caerá (o casi todo) en su lugar. Todos sabemos sobre el código de registro que impregna muchos módulos, no relacionados con el código comercial, pero sin embargo es imposible sin él. Y entonces separo esta funcionalidad del código de negocios.
Ejemplo: registro de código
Servicio de destino
@Service public class MyService { public void method1(List<String> list) { list.add("method1"); System.out.println("MyService method1 list.size=" + list.size()); } @AspectAnnotation public void method2() { System.out.println("MyService method2"); } public boolean check() { System.out.println("MyService check"); return true; } }
Un aspecto con una descripción de Pointcut y consejos.
@Aspect @Component public class MyAspect { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Pointcut("execution(public * com.example.demoAspects.MyService.*(..))") public void callAtMyServicePublic() { } @Before("callAtMyServicePublic()") public void beforeCallAtMethod1(JoinPoint jp) { String args = Arrays.stream(jp.getArgs()) .map(a -> a.toString()) .collect(Collectors.joining(",")); logger.info("before " + jp.toString() + ", args=[" + args + "]"); } @After("callAtMyServicePublic()") public void afterCallAt(JoinPoint jp) { logger.info("after " + jp.toString()); } }
Y el código de prueba de llamada
@RunWith(SpringRunner.class) @SpringBootTest public class DemoAspectsApplicationTests { @Autowired private MyService service; @Test public void testLoggable() { List<String> list = new ArrayList(); list.add("test"); service.method1(list); service.method2(); Assert.assertTrue(service.check()); } }
Explicaciones En el servicio de destino no se menciona el registro, en el código de llamada aún más, todo el registro se concentra
en un módulo separado@Aspect
class MyAspect ...
En pointcut
@Pointcut("execution(public * com.example.demoAspects.MyService.*(..))") public void callAtMyServicePublic() { }
Solicité todos los métodos públicos de MyService con cualquier tipo de devolución
* y el número de argumentos
(..)En el
Consejo anterior y posterior, que se refiere a
Pointcut (callAtMyServicePublic) , escribí instrucciones para escribir en el registro. JoinPoint no es un parámetro obligatorio, que proporciona información adicional, pero si se usa, debería ser el primero.
¡Todo está espaciado en diferentes módulos! Código de llamada, destino, registro.
Resultado en consola

Las reglas de Pointcut pueden variar
Algunos ejemplos de Pointcut y consejos:
Solicitud de anotación sobre un método.
@Pointcut("@annotation(AspectAnnotation)") public void callAtMyServiceAnnotation() { }
Consejos para el
@Before("callAtMyServiceAnnotation()") public void beforeCallAt() { }
Solicitud de un método específico que indique los parámetros del método de destino.
@Pointcut("execution(* com.example.demoAspects.MyService.method1(..)) && args(list,..))") public void callAtMyServiceMethod1(List<String> list) { }
Consejos para el
@Before("callAtMyServiceMethod1(list)") public void beforeCallAtMethod1(List<String> list) { }
Punto de corte para el resultado de devolución
@Pointcut("execution(* com.example.demoAspects.MyService.check())") public void callAtMyServiceAfterReturning() { }
Consejos para el
@AfterReturning(pointcut="callAtMyServiceAfterReturning()", returning="retVal") public void afterReturningCallAt(boolean retVal) { }
Un ejemplo de comprobación de derechos para un aviso de tipo Alrededor, mediante anotación
@Retention(RUNTIME) @Target(METHOD) public @interface SecurityAnnotation { }
Los métodos que deben verificarse antes de la llamada se pueden anotar con "SecurityAnnotation", luego en Aspect obtendremos una porción de ellos, y todos ellos serán interceptados antes de la llamada y se realizará la verificación de derechos.
Código objetivo:
@Service public class MyService { @SecurityAnnotation public Balance getAccountBalance(User user) {
Código de llamada:
balance = myService.getAccountBalance(user); if (balance == null) { accessDenied(user); } else { displayBalance(balance); }
Es decir en el código de llamada y el objetivo, no hay verificación de derechos, solo el código comercial en sí.
Un ejemplo de perfilado del mismo servicio utilizando un Consejo de tipo Alrededor
@Aspect @Component public class MyAspect { @Pointcut("execution(public * com.example.demoAspects.MyService.*(..))") public void callAtMyServicePublic() { } @Around("callAtMyServicePublic()") public Object aroundCallAt(ProceedingJoinPoint call) throws Throwable { StopWatch clock = new StopWatch(call.toString()); try { clock.start(call.toShortString()); return call.proceed(); } finally { clock.stop(); System.out.println(clock.prettyPrint()); } } }
Si ejecutamos el código de llamada con llamadas a los métodos de MyService, obtenemos el tiempo de llamar a cada método. Por lo tanto, sin cambiar el código de llamada y el objetivo, agregué nuevas características: registro, perfilador y seguridad.
Ejemplo de uso en formularios de IU
Hay un código que al configurar oculta / muestra los campos en el formulario:
public class EditForm extends Form { @Override public void init(Form form) { formHelper.updateVisibility(form, settingsService.isVisible(COMP_NAME)); formHelper.updateVisibility(form, settingsService.isVisible(COMP_LAST_NAME)); formHelper.updateVisibility(form, settingsService.isVisible(COMP_BIRTH_DATE));
también puede eliminar updateVisibility en un aviso de tipo
Alrededor @Aspect public class MyAspect { @Pointcut("execution(* com.example.demoAspects.EditForm.init() && args(form,..))") public void callAtInit(Form form) { }
etc.
Estructura del proyecto

archivo pom <?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>demoAspects</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>demoAspects</name> <description>Demo project for Spring Boot Aspects</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.6.RELEASE</version> <relativePath/> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <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> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
Materiales
Programación Orientada a Aspectos con Spring