2015-04-16 5 views
5

Sto avendo un po 'di problemi a capire come creare un collegamento di punti che opererà su bean che hanno uno specifico parametro annotato. Il mio obiettivo finale è quello di convalidare il valore del parametro prima che venga elaborato, ma per il momento ho solo bisogno di creare il pointcut.Come scrivere un pointcut di Aspect basato su un parametro annotato

Si consideri la seguente annotazione

@Retention(RetentionPolicy.RUNTIME) 
@Target({ ElementType.PARAMETER }) 
public @interface MyAnnotation {} 

ho poi volessi applicare questo ad un certo numero di metodi come:

public void method1(@MyAnnotation long i) {} 
public void method2(String someThing, @MyAnnotation long i) {} 
public void method3(String someThing, @MyAnnotation long i, byte value) {} 

Così

  • non mi interessa che classe (o pacchetto) i metodi sono in
  • La posizione dell'annotazione l'argomento sarà diverso.
  • so che il valore annotato si applica solo a un tipo specifico

mia implementazione pointcut deve essere qualcosa lungo le linee di:

@Before(value = "* *(..) && args(verifyMe)") 
public void verifyInvestigationId(long verifyMe) {} 

mi sto un po 'confuso su esattamente ciò che deve essere il valore @Before e come legare l'annotazione e il suo tipo. A questo punto probabilmente non vale la pena elencare le cose che ho provato!

Aggiornamento: Sulla base del parere che ho visto in http://stackoverflow.com/questions/3565718/pointcut-matching-methods-with-annotated-parameters/3567170#3567170 (e correggere un paio di incomprensioni e l'aggiunta di spazio ho trascurato) ho al punto in cui le seguenti opere:

@Before("execution(public * *(.., @full.path.to.MyAnnotation (*), ..))") 
public void beforeMethod(JoinPoint joinPoint) { 
    System.out.println("At least one of the parameters are annotated with @MyAnnotation"); 
} 

questo è quasi quello che mi serve - tutto quello che dovete fare è passare il valore dell'argomento annotato come parametro per il metodo. Non riesco a capire la sintassi per fare in modo che Spring lo faccia (la risposta collegata non mostra questo).

+0

possibile duplicato del [corrispondenti metodi pointcut con parametri annotati] (http://stackoverflow.com/questions/2766844/pointcut-matching-methods-with-annotated-parameters) – sheltem

+0

@sheltem, Grazie per avermi indicato che questo. Purtroppo questa è stata una delle cose che ho provato, ma fallita. I log contenevano l'errore: Pointcut non è ben formato: aspettando 'nome pattern' all'esecuzione del personaggio 56 esecuzione (pubblico * * (.., @ aspetti.VerifyMe (*), ..)) – Stormcloud

+0

BTW: ci sono due stelle separate da uno spazio dopo la parola "pubblico" - StackOverflow le ha interpretate come in corsivo! – Stormcloud

risposta

5

Molto simile al my answer here che sheltem già puntato, la soluzione si presenta così (nella sintassi di annotazione in stile questa volta, perché in primavera AOP non è possibile utilizzare la sintassi nativa AspectJ):

annotazione del manifesto originale:

package annotations; 

import java.lang.annotation.ElementType; 
import java.lang.annotation.Retention; 
import java.lang.annotation.RetentionPolicy; 
import java.lang.annotation.Target; 

@Retention(RetentionPolicy.RUNTIME) 
@Target({ ElementType.PARAMETER }) 
public @interface MyAnnotation {} 

driver Applicazione:

Uso l'applicazione driver per testare la mia soluzione AspectJ. In primavera, la classe e l'aspetto devono essere i bean/componenti Spring in modo che funzioni.

package de.scrum_master.app; 

import java.util.ArrayList; 
import java.util.HashSet; 
import java.util.List; 
import java.util.Set; 

import annotations.MyAnnotation; 

public class Application { 
    public void method1(@MyAnnotation int i) {} 
    public void method2(String id, @MyAnnotation float f) {} 
    public void method3(int i, @MyAnnotation List<String> strings, @MyAnnotation String s) {} 
    public void method4(int i, @MyAnnotation Set<Integer> numbers, float f, boolean b) {} 
    public void method5(boolean b, String s, @MyAnnotation String s2, float f, int i) {} 
    public void notIntercepted(boolean b, String s, String s2, float f, int i) {} 

    public static void main(String[] args) { 
     List<String> strings = new ArrayList<String>(); 
     strings.add("foo"); 
     strings.add("bar"); 
     Set<Integer> numbers = new HashSet<Integer>(); 
     numbers.add(11); 
     numbers.add(22); 
     numbers.add(33); 

     Application app = new Application(); 
     app.method1(1); 
     app.method2("foo", 1f); 
     app.method3(1, strings, "foo"); 
     app.method4(1, numbers, 1f, true); 
     app.method5(false, "foo", "bar", 1f, 1); 
     app.notIntercepted(false, "foo", "bar", 1f, 1); 
    } 
} 

Aspetto:

package de.scrum_master.aspect; 

import java.lang.annotation.Annotation; 

import org.aspectj.lang.JoinPoint; 
import org.aspectj.lang.SoftException; 
import org.aspectj.lang.annotation.Aspect; 
import org.aspectj.lang.annotation.Before; 
import org.aspectj.lang.reflect.MethodSignature; 

import annotations.MyAnnotation; 

@Aspect 
public class ArgCatcherAspect { 
    @Before("execution(public * *(.., @MyAnnotation (*), ..))") 
    public void interceptMethodsWithAnnotatedParameters(JoinPoint thisJoinPoint) { 
     System.out.println(thisJoinPoint); 
     MethodSignature signature = (MethodSignature) thisJoinPoint.getSignature(); 
     String methodName = signature.getMethod().getName(); 
     Class<?>[] parameterTypes = signature.getMethod().getParameterTypes(); 
     Annotation[][] annotations; 
     try { 
      annotations = thisJoinPoint.getTarget().getClass(). 
       getMethod(methodName, parameterTypes).getParameterAnnotations(); 
     } catch (Exception e) { 
      throw new SoftException(e); 
     } 
     int i = 0; 
     for (Object arg : thisJoinPoint.getArgs()) { 
      for (Annotation annotation : annotations[i]) { 
       if (annotation.annotationType() == MyAnnotation.class) 
        System.out.println(" " + annotation + " -> " + arg); 
        // Verify 'arg' here or do whatever 
      } 
      i++; 
     } 
    } 
} 

Console di registro:

execution(void de.scrum_master.app.Application.method1(int)) 
    @annotations.MyAnnotation() -> 1 
execution(void de.scrum_master.app.Application.method2(String, float)) 
    @annotations.MyAnnotation() -> 1.0 
execution(void de.scrum_master.app.Application.method3(int, List, String)) 
    @annotations.MyAnnotation() -> [foo, bar] 
    @annotations.MyAnnotation() -> foo 
execution(void de.scrum_master.app.Application.method4(int, Set, float, boolean)) 
    @annotations.MyAnnotation() -> [33, 22, 11] 
execution(void de.scrum_master.app.Application.method5(boolean, String, String, float, int)) 
    @annotations.MyAnnotation() -> bar 
+0

Molto accurata, buona risposta. Grazie! –

0

Questo è quello che ho finito al dopo armeggiare con esso (importazioni omesso):

@Aspect 
public class VerifyAspect { 

    @Before("execution(* *(.., @annotations.MyAnnotation (*), ..)) && args(.., verifyMe)") 
    public void verifyInvestigationId(final Object verifyMe) { 
     System.out.println("Aspect verifying: " + verifyMe); 
    } 
} 

Non c'è bisogno di qualcosa di Primavera-specifici, come AspectJ si fornisce già con i parametri, se lo si desidera.

+0

Bello (e certamente più vicino a tutto ciò che sono riuscito a ottenere!), Ma questo ha una limitazione che l'argomento passato come "verifyMe" è sempre l'argomento passato al metodo cut-point. C'è un modo per dire qualcosa sulla falsariga di "mandami l'argomento (i) con l'annotazione specificata"? – Stormcloud

+0

Hai ragione, questo probabilmente non ti dà il parametro annotato in ogni caso. Hmm ... torna al tavolo da disegno! – sheltem

+0

Sebbene sia una soluzione ad hoc, è possibile consentire ad AspectJ di darti il ​​JoinPoint e analizzare i parametri tramite riflessione per trovare quelli annotati con la tua annotazione. Non molto elegante, ma dovrebbe funzionare per cominciare. – sheltem

Problemi correlati