2015-04-16 12 views
5

Ich habe ein bisschen Mühe herauszufinden, wie man einen Pointcut erstellt, der auf Beans funktioniert, die einen bestimmten annotierten Parameter haben. Mein Ziel ist es, den Wert des Parameters vor der Verarbeitung zu validieren, aber für den Moment muss ich nur den Pointcut erstellen.Wie schreibe ich einen Aspect Pointcut basierend auf einem annotierten Parameter

Betrachten Sie die folgende Anmerkung

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

ich dann diese wie auf eine Reihe von Methoden anwenden möchten:

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

So

  • ist mir egal, was Klasse (oder Paket) die Methoden sind in
  • Die Position des Annotats ed Argument wird variieren.
  • Ich weiß, dass kommentierte Wert wird nur

Meine pointcut Implementierung etwas entlang der Linien werden muss, um eine bestimmte Art gelten:

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

Ich bin immer ein wenig verwirrt über genau das, was @Before Wert sein muss und wie man die Annotation und ihren Typ bindet. An diesem Punkt ist es wahrscheinlich nicht wert, die Dinge aufzulisten, die ich ausprobiert habe!

aktualisieren: Basierend auf den Rat, den ich in http://stackoverflow.com/questions/3565718/pointcut-matching-methods-with-annotated-parameters/3567170#3567170 gesehen habe (und Korrektur ein paar Missverständnisse und das Hinzufügen von Raum, den ich übersehen) ich habe zu dem Punkt, wo die folgenden Werke:

@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"); 
} 

das ist fast, was ich brauche - alles, was ich tun müssen, um den Wert des kommentierten Argument als Parameter übergeben zu die Methode. Ich kann die Syntax nicht ganz ausarbeiten, um Spring dazu zu bringen (die verknüpfte Antwort zeigt das nicht).

+0

mögliches Duplikat [Pointcut passende Methoden mit annotierten Parametern] (http://stackoverflow.com/questions/2766844/pointcut-matching-methods-with-annotated-Parameter) – sheltem

+0

@sheltem, Danke, dass du mich darauf hingewiesen hast. Leider war das eines der Dinge, die ich ausprobiert habe, aber gescheitert ist. Die Protokolle enthalten den Fehler: Pointcut ist nicht wohlgeformt: Erwartung 'Namensmuster' an Zeichenposition 56 Ausführung (öffentlich * * (.., @ aspects.VerifyMe (*), ..)) – Stormcloud

+0

BTW: Es gibt zwei Sterne getrennt durch ein Leerzeichen nach dem Wort "public" - stackoverflow hat sie als kursiv interpretiert! – Stormcloud

Antwort

5

Sehr ähnlich my answer here die sheltem schon zu spitz, sieht die Lösung wie folgt aus (in Annotation-Stil Syntax, weil dieses Mal im Frühjahr AOP Sie keine nativen AspectJ Syntax verwenden):

Originalplakat der Anmerkung:

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 {} 

Treiberanwendung:

Ich verwende die Treiberanwendung, um meine AspectJ-Lösung zu testen. Im Frühjahr müssen sowohl die Klasse als auch der Aspekt Spring Beans/Komponenten sein, damit dies funktioniert.

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); 
    } 
} 

Richtung:

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++; 
     } 
    } 
} 

Konsolprotokoll:

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

Sehr gründlich, gute Antwort. –

0

Dies ist, was ich schließlich nach etwa damit das Hantieren (Importe weggelassen):

@Aspect 
public class VerifyAspect { 

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

Keine Notwendigkeit für etwas Frühling spezifisch, wie AspectJ schon liefert Ihnen die Parameter, wenn dies gewünscht wird.

+0

Schön (und sicherlich näher als alles, was ich geschafft habe zu arbeiten!), Aber dies hat eine Einschränkung, dass das Argument übergeben als "verifyMe" ist immer das eigentliche Argument an die Cut-Point-Methode übergeben. Gibt es eine Möglichkeit, etwas in der Art von "sende mir die Argumente mit der angegebenen Anmerkung" zu sagen? – Stormcloud

+0

Sie haben recht, das gibt Ihnen wahrscheinlich nicht den annotierten Parameter in jedem Fall. Hmm ... zurück zum Zeichenbrett! – sheltem

+0

Obwohl als eine Ad-hoc-Lösung, könnten Sie AspectJ geben Sie den JoinPoint und gehen Sie durch seine Parameter durch Reflexion, um diejenigen mit Ihrer Annotation kommentiert zu finden. Nicht sehr elegant, aber sollte für einen Anfang arbeiten. – sheltem