programming - programacion orientada a aspectos java tutorial
ProgramaciĆ³n orientada a aspectos de Java con anotaciones (5)
Aquí está mi contribución a este post muy útil.
Tomaremos un ejemplo muy simple: debemos tomar medidas en el procesamiento de algunos métodos. Están anotados con anotaciones personalizadas, que contienen datos para manejar. Dados estos datos, queremos provocar una excepción o dejar que el proceso continúe como si el método no estuviera anotado.
El código de Java para definir nuestro aspecto:
package com.example;
public class AccessDeniedForCustomAnnotatedMethodsAspect {
public Object checkAuthorizedAccess(ProceedingJoinPoint proceedingJointPoint)
throws Throwable {
final MethodSignature methodSignature = (MethodSignature) proceedingJointPoint
.getSignature();
// how to get the method name
final String methodName = methodSignature
.getMethod()
.getName();
// how to get the parameter types
final Class<?>[] parameterTypes = methodSignature
.getMethod()
.getParameterTypes();
// how to get the annotations setted on the method
Annotation[] declaredAnnotations = proceedingJointPoint
.getTarget()
.getClass()
.getMethod(methodName, parameterTypes)
.getDeclaredAnnotations();
if (declaredAnnotations.length > 0) {
for (Annotation declaredAnnotation : Arrays.asList(declaredAnnotations)) {
// I just want to deal with the one that interests me
if(declaredAnnotation instanceof CustomAnnotation) {
// how to get the value contained in this annotation
(CustomAnnotation) declaredAnnotation).value()
if(test not OK) {
throw new YourException("your exception message");
}
// triggers the rest of the method process
return proceedingJointPoint.proceed();
}
}
}
}
La configuración xml:
<aop:config>
<aop:aspect id="accessDeniedForCustomAnnotatedMethods"
ref="accessDeniedForCustomAnnotatedMethodsAspect">
<aop:around pointcut="execution(@xxx.zzz.CustomAnnotation * *(..))"
method="checkAuthorizedAccess" />
</aop:aspect>
</aop:config>
<bean id="accessDeniedForCustomAnnotatedMethodsAspect"
class="xxx.yyy.AccessDeniedForCustomAnnotatedMethodsAspect" />
Espero eso ayude !
En un post titulado "AOP Fundamentals" , pedí una explicación en inglés de King sobre qué es AOP y qué hace. Recibí algunas respuestas muy útiles y enlaces a artículos que me ayudaron a informarme sobre toda la teoría.
Pero ahora AOP ha captado toda mi atención, y todos estos artículos y extractos de capítulos son fantásticos, pero en todos los casos consisten en teorías nobles, modelos vagos de UML y un orden de abstracción que son demasiado altos para mi gusto.
Aquí está mi comprensión de la teoría de AOP, solo para aclarar, así que si ves algo que se ve mal, házmelo saber:
Las inquietudes transversales, como el Registro, la Autenticación, la Sincronización, la Validación, el Manejo de Excepciones, etc. se vuelven altamente acopladas en sistemas que no son AOP, ya que son utilizados universalmente por casi todos los componentes / módulos del código base.
AOP define aspectos (clases / métodos) que abstraen estas preocupaciones transversales con el uso de puntos de unión , consejos y puntos de corte .
a. Consejo : el código real (método de un aspecto, ¿quizás?) Implementando la preocupación transversal (es decir, hacer el registro real, validar, autenticar, etc.)
segundo. Punto de unión : un evento que se desencadena en un código que no es AOP que hace que se ejecute un consejo de un aspecto particular ("entretejido" en el código que no es AOP)
do. Punto de corte : esencialmente, una asignación de puntos de unión (eventos desencadenantes) para la ejecución de consejos
Todos los aspectos se modularizan (LoggingAspect, AuthenticationAspect, ValidationAspect, etc.) en componentes y se registran con AspectWeaver . Cuando el código que no es AOP / POJO llega a través de un punto de unión, AspectWeaver "teje" (integra) el consejo asignado alrededor del código que no es AOP:
public class LoggingAspect { // ... public void log(String msg) { ... } } public class ExceptionHandlingAspect { // .. public void handle(Exception exc) { ... } } public class NonAOPCode { // ... @LoggingAspect @ExceptionHandlingAspect public void foo() { // do some stuff... } } // Now in the driver public static int main void(String[] args) { NonAOPCode nonAOP = new NonAOPCode(); nonAOP.foo(); } // The AspectWeaver *magically* might weave in method calls so main now becomes: { NonAOPCode nonAOP = new NonAOPCode(); log(someMsg); nonAOP.foo(); handle(someExc); }
La pregunta de los $ 64,000: ¿Mi comprensión del AOP basado en Java está en el objetivo o está muy lejos, y por qué? ¿Cómo se podrían usar correctamente las anotaciones para implementar aspectos, consejos, puntos de unión, puntos de corte y este llamado tejedor de aspectos?
Cambiar el comentario
// The AspectWeaver *magically* might weave in method calls so main now becomes
a
// The AspectWeaver *magically* might weave in method calls so main now
// becomes effectively (the .class file is not changed)
Me gusta el escrito de primavera de AOP. Echa un vistazo al Capítulo 7
Encontré la respuesta yo mismo después de mucho trabajo de excavación y codo ...
Sí, AOP debería estar basado en anotaciones en el mundo de Java, sin embargo, no puede procesar anotaciones relacionadas con aspectos como las anotaciones regulares (metadatos). Para interceptar una llamada a un método etiquetado y "tejer" los métodos de asesoramiento antes / después, necesita la ayuda de algunos motores de AOP muy ingeniosos como AspectJ. @Christopher McCann ofreció una solución realmente agradable en otro hilo relacionado con las anotaciones, donde sugirió el uso de AOP Alliance junto con Google Guice. Después de leer la documentación de Guice sobre el soporte de AOP, esto es exactamente lo que estoy buscando: un marco fácil de entender para tejer en el "consejo" (llamadas de método) de problemas transversales, como el registro, la validación, el almacenamiento en caché, etc.
Este era un gilipollas.
Hace algunos meses escribí un artículo con un ejemplo sobre cómo implementé un caso práctico de combinación de aspectos Aspect / J con anotaciones de Java, que pueden resultarle útiles:
http://technomilk.wordpress.com/2010/11/06/combining-annotations-and-aspects-part-1/
Creo que los aspectos aplicados a las anotaciones son una buena combinación porque hacen que el aspecto sea más explícito en su código, pero de una manera limpia, y puede usar parámetros en sus anotaciones para una mayor flexibilidad.
Por cierto, la forma en que funciona Aspect / J es mediante la modificación de sus clases en tiempo de compilación, no en tiempo de ejecución. Ejecuta sus fuentes y aspectos a través del compilador Aspect / J y crea los archivos de clase modificados.
Spring AOP, en la medida en que lo entiendo, realiza el tejido (manipulando los archivos de clase para incluir el procesamiento de aspectos) de una manera diferente, al crear objetos proxy, creo que en el momento de la creación de instancias (pero no confío en mi palabra) .
Imaginemos que desea registrar el tiempo empleado por algunos métodos @LogExecTime
usando una anotación @LogExecTime
.
Primero creo una anotación LogExecTime
:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogExecTime {
}
Luego defino un aspecto:
@Component // For Spring AOP
@Aspect
public class LogTimeAspect {
@Around(value = "@annotation(annotation)")
public Object LogExecutionTime(final ProceedingJoinPoint joinPoint, final LogExecTime annotation) throws Throwable {
final long startMillis = System.currentTimeMillis();
try {
System.out.println("Starting timed operation");
final Object retVal = joinPoint.proceed();
return retVal;
} finally {
final long duration = System.currentTimeMillis() - startMillis;
System.out.println("Call to " + joinPoint.getSignature() + " took " + duration + " ms");
}
}
}
Creo una clase LogExecTime
con LogExecTime
:
@Component
public class Operator {
@LogExecTime
public void operate() throws InterruptedException {
System.out.println("Performing operation");
Thread.sleep(1000);
}
}
Y un principal uso de Spring AOP:
public class SpringMain {
public static void main(String[] args) throws InterruptedException {
ApplicationContext context = new GenericXmlApplicationContext("applicationContext.xml");
final Operator bean = context.getBean(Operator.class);
bean.operate();
}
}
Si ejecuto esta clase, obtengo el siguiente resultado en la salida estándar:
Starting timed operation
Performing operation
Call to void testaop.Operator.Operate() took 1044 ms
Ahora con la magia . Como utilicé Spring AOP en lugar del tejedor AspectJ, la magia está ocurriendo en tiempo de ejecución utilizando mecanismos de proxy-ish. Así que los archivos .class
se dejan intactos. Por ejemplo, si depuro este programa y puse un punto de interrupción en operate
, verá cómo Spring ha realizado la magia:
Como la implementación de Spring AOP no es intrusiva y utiliza los mecanismos de Spring, necesita agregar la anotación @Component
y crear el objeto utilizando el contexto de Spring en lugar de ser completamente new
.
AspectJ en el otro lado cambiará los archivos .class
. Probé este proyecto con AspectJ y descompuse la clase Operator con jad. Lo que lleva a:
public void operate()
throws InterruptedException
{
JoinPoint joinpoint = Factory.makeJP(ajc$tjp_0, this, this);
operate_aroundBody1$advice(this, joinpoint, LogTimeAspect.aspectOf(), (ProceedingJoinPoint)joinpoint, (LogExecTime)(ajc$anno$0 == null && (ajc$anno$0 = testaop/Operator.getDeclaredMethod("operate", new Class[0]).getAnnotation(testaop/LogExecTime)) == null ? ajc$anno$0 : ajc$anno$0));
}
private static final void operate_aroundBody0(Operator ajc$this, JoinPoint joinpoint)
{
System.out.println("Performing operation");
Thread.sleep(1000L);
}
private static final Object operate_aroundBody1$advice(Operator ajc$this, JoinPoint thisJoinPoint, LogTimeAspect ajc$aspectInstance, ProceedingJoinPoint joinPoint, LogExecTime annotation)
{
long startMillis = System.currentTimeMillis();
Object obj;
System.out.println("Starting timed operation");
ProceedingJoinPoint proceedingjoinpoint = joinPoint;
operate_aroundBody0(ajc$this, proceedingjoinpoint);
Object retVal = null;
obj = retVal;
long duration = System.currentTimeMillis() - startMillis;
System.out.println((new StringBuilder("Call to ")).append(joinPoint.getSignature()).append(" took ").append(duration).append(" ms").toString());
return obj;
Exception exception;
exception;
long duration = System.currentTimeMillis() - startMillis;
System.out.println((new StringBuilder("Call to ")).append(joinPoint.getSignature()).append(" took ").append(duration).append(" ms").toString());
throw exception;
}
private static void ajc$preClinit()
{
Factory factory = new Factory("Operator.java", testaop/Operator);
ajc$tjp_0 = factory.makeSJP("method-execution", factory.makeMethodSig("1", "operate", "testaop.Operator", "", "", "java.lang.InterruptedException", "void"), 5);
}
private static final org.aspectj.lang.JoinPoint.StaticPart ajc$tjp_0; /* synthetic field */
private static Annotation ajc$anno$0; /* synthetic field */
static
{
ajc$preClinit();
}