Quantcast
Channel: Planeta Código
Viewing all articles
Browse latest Browse all 2720

Blog Bitix: Programación orientada a aspectos con AspectJ, Spring AOP y la clase Proxy

$
0
0

Los aspectos permiten separar código con distintas funcionalidades y centralizar un código común que sin utilizarlos está repartido por toda la aplicación. Son un concepto potente y una vez entendidos sus conceptos ofrecen muchas posibilidades para simplificar el código y mejorar su mantenimiento. Hay varias posibilidades, dos de las más utilizadas son AspectJ y Spring AOP, en el caso de que estas no se puedan utilizar el JDK incluye la clase Proxy para usos básicos aunque más limitados.

Java

Ciertas funcionalidades son transversales y están repartidas por toda la aplicación. Añadir y mezclar el código de esta funcionalidades con el código en los métodos hace que el código del método sea más complicado incluso puede que ese código de utilidad sea de mayor tamaño que el fundamental del método.

Algunos ejemplos de funcionalidades transversales son trazas, métricas de rendimiento, seguridad, caches o transacciones. La programación orientada a aspectos permite extraer este código transversal y aplicarlo en aquellos puntos de la aplicación donde sea necesario sin estar mezclado con el código al que se aplica. Esto facilita la legibilidad del código, su mantenimiento y la separación con conceptos.

La programación orientada a aspectos se usa mucho en las aplicaciones que usan Spring pero hay otras librerías que lo permiten, incluso el propio JDK tiene alguna clase sin necesitar de dependencias adicionales.

La programación define varios términos:

  • Aspect: es una funcionalidad genérica aplicable a múltiples objetos. Cada aspecto trata una sola funcionalidad.
  • Join point: es el punto de ejecución donde se puede aplicar un aspecto como la llamada a un método, su retorno o el acceso a una propiedad.
  • Advice: es la acción que se realiza en un pointcut.
  • Pointcut: es una expresión que busca joint points, tiene un advice asociado que se ejecuta en todos los joint points que concuerdan con la expresión.
  • weaving: proceso que aplica los aspectos a las clases, puede ser en tiempo de compilación o en tiempo de ejecución.

Esta es una clase normal con un método en la que a modo de ejemplo en la llamada al método se le apliquen dos aspectos, uno para añadir una traza cuando se llame al método y su valor de retorno y otro aspecto para medir cuando tiempo tarda en ejecutarse. La clase Foo descnoce los aspectos que se van a aplicar, no hay que hacer ninguna modificación en ella ni para añadirle los aspectos ni para quitarselos.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
packageio.github.picodotdev.blogbitix.aspects;importjava.util.Random;publicclassFooimplementsIFoo{    publicvoidecho(){        System.out.println("echo");    }    publicintsum(inta,intb){        returna+b;    }    publicvoidsleep(){        try{            longtime=newRandom().nextInt(1500);            Thread.sleep(time);        }catch(Exceptione){}    }}
Foo.java

La interfaz solo es necesaria para un aspecto implementado con la clase Proxy de Java.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
packageio.github.picodotdev.blogbitix.aspects;importjava.util.Random;publicinterfaceIFoo{    voidecho();    intsum(inta,intb);    voidsleep();}
IFoo.java

Se recomienda usra la forma más simple que sea suficiente para las necesidad de la aplicación. Spring AOP es más simple que usar AspectJ y no hay necesidad de aplicar el compilador de AspectJ en el proceso de compilación. Si solo se necesita aplicar advices en la ejecución de métodos de beans de Spring, Spring AOP es suficiente.

Si no se usa spring o se necesitan aplicar aspectos en objetos no gestionados por el contenedor de Spring (como objetos de dominio) o aplicar advices en joint points distintos a las ejecuciones de métodos, por ejemplo para la obtención o asignación de una propiedad entonces la opción a usar es AspectJ.

Programación orientada a aspectos con AspectJ

AspectJ es una librería específica y la que más posibilidades ofrece de las que muestro en el artículo. Hay varias formas de utilizar AspectJ, la de usarla mediante anotaciones es bastante simple.

Una de las ventajas de AspectJ es que no requiere usar Spring para utilizarla pero para ello en el momento de compilación hay que realizar un proceso denominado weaving para añadir la funcionalidad de los aspectos que transformar el bytecode de las clases. Aplicar los aspectos transformando el código permite que los aspectos no penalicen en tiempo de ejecución y ofrezca mejor rendimiento que Spring AOP, aunque el rendimiento no es algo determinante en la mayoría de los proyectos. Por contra es más compleja y requiere aplicar a las clases un proceso de postcompilación.

Las expresiones de los ponintcuts son similares a una definición de la firma del los métodos, ámbitos de visibilidad, tipos de parámetros y tipo de retorno además del paquete. Es posible hacer expresiones boleanas compuestas para hacer más especifica una expresión. Este pointcut se aplica en la ejecución del método sum de la clase Foo que recibe dos parámetros de tipo int y retorna un valor de tipo int.

1
execution(int Foo.sum(int,int))
pointcut.txt

En la clase Aspects se definen los aspectos con una colección de pointcuts con sus código de advice asociado.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
packageio.github.picodotdev.blogbitix.aspects;importorg.aspectj.lang.ProceedingJoinPoint;importorg.aspectj.lang.annotation.After;importorg.aspectj.lang.annotation.Aspect;importorg.aspectj.lang.annotation.Before;importorg.aspectj.lang.annotation.Around;@AspectpublicclassAspects{    @Before("execution(void Foo.echo())")    publicvoidechoStart(){        System.out.println("aspect echo begin");    }    @After("execution(void Foo.echo())")    publicvoidechoEnd(){        System.out.println("aspect echo end");    }    @Around("execution(int Foo.sum(int,int))")    publicObjectlog(ProceedingJoinPointpjp)throwsThrowable{        System.out.println("aspect sum begin");        Objecto=pjp.proceed();        System.out.println("aspect sum end: "+o);        returno;    }    @Around("execution(void Foo.sleep())")    publicvoidtime(ProceedingJoinPointpjp)throwsThrowable{        longstart=System.currentTimeMillis();        Objecto=pjp.proceed();        longend=System.currentTimeMillis();        System.out.println("aspect time: "+(end-start));    }}
Aspects.java

Con la herramienta de construcción Gradle hay que incluir un plugin para aplicar el proceso de weaving. El proceso de weaving consiste en aplicar los aspectos a las clases, AspectJ lo realiza en tiempo de compilación modificando el bytecode de las clases en un segundo paso de compilación, con anterioridad el compilador de Java ha transformado el código fuente de las clases en bytecode.

1
2
3
4
5
6
7
plugins{    id'java'    id'application'    id'org.springframework.boot'version'2.2.4.RELEASE'    id'io.freefair.aspectj.post-compile-weaving'version'4.1.6'}...
build-1.gradle
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
packageio.github.picodotdev.blogbitix.aspects;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.boot.CommandLineRunner;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.EnableAspectJAutoProxy;@SpringBootApplication@EnableAspectJAutoProxypublicclassMainimplementsCommandLineRunner{    ...    @Override    publicvoidrun(String...args)throwsException{        // AspectJ
        System.out.println("");        System.out.println("AspectJ");        Foofoo=newFoo();        foo.echo();        foo.sum(3,7);        foo.sleep();        ...    }    publicstaticvoidmain(String[]args){        SpringApplication.run(Main.class,args);    }}
Main-1.java

En la salida del programa para el apartado de AspectJ se observa que el código de los aspectos se ejecuta al llamar a los métodos de la instancia de la clase Foo.

1
2
3
4
5
6
7
AspectJ
aspect echo begin
echo
aspect echo end
aspect sum begin
aspect sum end: 10
aspect time: 546
System.out-1

Programación orientada a aspectos con Spring AOP

Spring incluye su solución para la programación orientada a aspectos, más limitada que AspectJ pero suficiente para la mayoría de los casos tampoco requiere aplicar el proceso weaving de AspectJ en tiempo de compilación. La limitación de Spring AOP es que los joint points solo pueden ser métodos. Utiliza las mismas anotaciones de AspectJ para aplicar los aspects en tiempo de ejecución.

Otra diferenia con AspectJ es que los aspectos se aplican usando proxys que son una clase que envuelve a la instancia a la que se le aplica el aspecto, una vez dentro de la clase objetivo si se llama a otro método de forma interna a ese otro método no se le aplica su aspecto.

Suponiendo una clase que tiene un méodo foo y bar y desde fuera se llama a foo y este llama a bar para que en llamada desde foo a bar se apliquen los aspectos de bar hay que usar este código. Usar este código implica poner en el código una dependencia a Spring, lo cual no es deseable para el código de dominio.

1
2
3
4
5
...publicvoidfoo(){((Foo)AopContext.currentProxy()).bar();}...
SpringProxy.java

En el proxy es donde se ejecuta el código del advice.

Llamada a un método normalLlamada a un método con un proxy

Llamada a un método normal y con un proxy

Para que Spring procese las anotaciones require usar la anotación @EnableAspectJAutoProxy y que Spring encuentre la clase de los aspectos, anotándola con @Component o devolviendo una instancia en el contenedor de dependencias como en este caso.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
packageio.github.picodotdev.blogbitix.aspects;...@SpringBootApplication@EnableAspectJAutoProxypublicclassMainimplementsCommandLineRunner{    ...    @Bean    publicFoofoo(){        returnnewFoo();    }    @Bean    publicAspectsaspects(){        returnnewAspects();    }    @Override    publicvoidrun(String...args)throwsException{        ...        // Spring AOP
        System.out.println("");        System.out.println("Spring AOP (AspectJ anotations)");        fooBean.echo();        fooBean.sum(3,7);        fooBean.sleep();        ...    }}
Main-2.java

El plugin para realizar el proceso de weaving con AspectJ no es necesario. Spring realiza e proceso de weaving en tiempo de ejecución.

1
2
3
4
5
6
7
plugins{    id'java'    id'application'    id'org.springframework.boot'version'2.2.4.RELEASE'    //id 'io.freefair.aspectj.post-compile-weaving' version '4.1.6'
}...
build-2.gradle

El resultado es el mismo que con AspectJ.

1
2
3
4
5
6
7
Spring AOP (AspectJ anotations)
aspect echo begin
echo
aspect echo end
aspect sum begin
aspect sum end: 10
aspect time: 1049
System.out-2

Programación orientada a aspectos con la clase Proxy

Para casos muy sencillos donde no sea posible aplicar una de las opciones anteriores al no poder usar sus librerías por restricciones del proyecto en cuanto a dependencias usables está la alternativa incluida en el JDK. La clase Proxy está incorporada en el propio JDK, permite hacer cosas sencillas sin dependencias adicionales.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
packageio.github.picodotdev.blogbitix.aspects;importjava.lang.reflect.InvocationHandler;importjava.lang.reflect.Method;importjava.lang.reflect.Proxy;publicclassLogProxyimplementsInvocationHandler{    protectedObjectobject;    protectedProxyproxy;    publicLogProxy(Objectobject){        this.object=object;        proxy=(Proxy)proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);    }    publicProxygetProxy(){        returnproxy;    }    publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{        System.out.println("proxy "+method.getName()+" begin");        Objecto=method.invoke(object,args);        System.out.print("proxy "+method.getName()+" end");        if(!method.getReturnType().equals(Void.TYPE)){            System.out.print(": "+o);        }        System.out.println();        returno;    }}
LogProxy.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
packageio.github.picodotdev.blogbitix.aspects;importjava.lang.reflect.InvocationHandler;importjava.lang.reflect.Method;importjava.lang.reflect.Proxy;publicclassProfileProxyimplementsInvocationHandler{    protectedObjectobject;    protectedProxyproxy;    publicProfileProxy(Objectobject){        this.object=object;        proxy=(Proxy)proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);    }    publicProxygetProxy(){        returnproxy;    }    publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{        longstart=System.currentTimeMillis();        Objecto=method.invoke(object,args);        longend=System.currentTimeMillis();        if(method.getName().equals("sleep")){            System.out.println("proxy time: "+(end-start));        }        returno;    }}
ProfileProxy.java

En este caso se observa que se ha aplicado el aspecto de AspectJ y además los aspectos de los proxys de este apartado.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
Java Proxy
proxy echo begin
aspect echo begin
echo
aspect echo end
proxy echo end
proxy sum begin
aspect sum begin
aspect sum end: 10
proxy sum end: 10
proxy sleep begin
aspect time: 323
proxy time: 323
proxy sleep end
System.out-3

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando el comando ./gradlew run.


Viewing all articles
Browse latest Browse all 2720

Trending Articles


Girasoles para colorear


mayabang Quotes, Torpe Quotes, tanga Quotes


Tagalog Quotes About Crush – Tagalog Love Quotes


OFW quotes : Pinoy Tagalog Quotes


Long Distance Relationship Tagalog Love Quotes


Tagalog Quotes To Move on and More Love Love Love Quotes


5 Tagalog Relationship Rules


Best Crush Tagalog Quotes And Sayings 2017


Re:Mutton Pies (lleechef)


FORECLOSURE OF REAL ESTATE MORTGAGE


Sapos para colorear


tagalog love Quotes – Tiwala Quotes


Break up Quotes Tagalog Love Quote – Broken Hearted Quotes Tagalog


Patama Quotes : Tagalog Inspirational Quotes


Pamatay na Banat and Mga Patama Love Quotes


Tagalog Long Distance Relationship Love Quotes


BARKADA TAGALOG QUOTES


“BAHAY KUBO HUGOT”


Vimeo 10.7.0 by Vimeo.com, Inc.


Vimeo 10.7.1 by Vimeo.com, Inc.