Methodenargumente mit Spring AOP abrufen?

73

Ich benutze Spring AOP und habe folgenden Aspekt:

@Aspect
public class LoggingAspect {

    @Before("execution(* com.mkyong.customer.bo.CustomerBo.addCustomer(..))")
    public void logBefore(JoinPoint joinPoint) {

        System.out.println("logBefore() is running!");
        System.out.println("hijacked : " + joinPoint.getSignature().getName());
        System.out.println("******");
    }

}

Der obige Aspekt fängt die addCustomerAusführung der Methode ab. addCustomerMethode nimmt String als Eingabe. Aber ich muss die Eingabe protokollieren, die an die addCustomerMethode innerhalb der logBeforeMethode übergeben wurde.
Ist das möglich?

user1016403
quelle
1
Was ist die Methodensignatur von addCustomer(..)?
Sotirios Delimanolis

Antworten:

97

Sie haben einige Möglichkeiten:

Zunächst können Sie die JoinPoint#getArgs()Methode verwenden, Object[]die alle Argumente der empfohlenen Methode zurückgibt . Je nachdem, was Sie mit ihnen machen möchten, müssen Sie möglicherweise ein Casting durchführen.

Zweitens können Sie den argsPointcut-Ausdruck folgendermaßen verwenden:

// use '..' in the args expression if you have zero or more parameters at that point
@Before("execution(* com.mkyong.customer.bo.CustomerBo.addCustomer(..)) && args(yourString,..)")

dann kann Ihre Methode stattdessen definiert werden als

public void logBefore(JoinPoint joinPoint, String yourString) 
Sotirios Delimanolis
quelle
8
Wenn ich mich nicht irre, gibt es einen Unterschied im Verhalten zwischen den beiden Optionen. Der zweite wird nur ausgelöst, wenn das Argument vorhanden ist, während der erste ausgelöst wird, auch wenn das Argument nicht vorhanden ist.
Samuel EUSTACHI
@SamuelEUSTACHI Für das erste Beispiel wurde kein Pointcut-Ausdruck angegeben. Wenn wir eine Ausführung von annehmen addCustomer(..), sicher. Kann keine oder viele Argumente sein.
Sotirios Delimanolis
Die zweite Option ist sehr praktisch. Ich musste die Anforderung und Antwort sowie einige andere Parameter vor und nach dem Senden einer Anforderung an eine xyz-URL protokollieren. Ohne die zweite Option wäre der Pointcut-Ausdruck sehr komplex und unlesbar geworden.
Mohammed Salman Shaikh
24

Ja, der Wert eines Arguments kann mit getArgs ermittelt werden

@Before("execution(* com.mkyong.customer.bo.CustomerBo.addCustomer(..))")
public void logBefore(JoinPoint joinPoint) {

   Object[] signatureArgs = thisJoinPoint.getArgs();
   for (Object signatureArg: signatureArgs) {
      System.out.println("Arg: " + signatureArg);
      ...
   }
}
Reimeus
quelle
13

Wenn Sie alle Argumente protokollieren müssen oder Ihre Methode ein Argument hat, können Sie einfach getArgs verwenden, wie in den vorherigen Antworten beschrieben.

Wenn Sie ein bestimmtes Argument protokollieren müssen, können Sie es mit Anmerkungen versehen und dann seinen Wert wie folgt wiederherstellen:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Data {
 String methodName() default "";
}

@Aspect
public class YourAspect {

 @Around("...")
 public Object around(ProceedingJoinPoint point) throws Throwable {
  Method method = MethodSignature.class.cast(point.getSignature()).getMethod();
  Object[] args = point.getArgs();
  StringBuilder data = new StringBuilder();
    Annotation[][] parameterAnnotations = method.getParameterAnnotations();
    for (int argIndex = 0; argIndex < args.length; argIndex++) {
        for (Annotation paramAnnotation : parameterAnnotations[argIndex]) {
            if (!(paramAnnotation instanceof Data)) {
                continue;
            }
            Data dataAnnotation = (Data) paramAnnotation;
            if (dataAnnotation.methodName().length() > 0) {
                Object obj = args[argIndex];
                Method dataMethod = obj.getClass().getMethod(dataAnnotation.methodName());
                data.append(dataMethod.invoke(obj));
                continue;
            }
            data.append(args[argIndex]);
        }
    }
 }
}

Anwendungsbeispiele:

public void doSomething(String someValue, @Data String someData, String otherValue) {
    // Apsect will log value of someData param
}

public void doSomething(String someValue, @Data(methodName = "id") SomeObject someData, String otherValue) {
    // Apsect will log returned value of someData.id() method
}
andolsi zied
quelle
@Around ("...") Ist dieses Muster für @Around nicht ungültig?
Harpreet Sandhu - TheRootCoder
"..." bedeutet komplett mit Ihrem Muster.
Andolsi zied
1
schön, wonach ich gesucht habe
Horatiu Jeflea
1
Hervorragend, genau das, wonach ich für meine Attribut-basierte Zugriffssteuerungsbewertung gesucht habe!
Ryfterek
7

Es gibt auch eine andere Möglichkeit, wenn Sie einen Pointcut für viele Ratschläge definieren. Dies kann hilfreich sein:

@Pointcut("execution(@com.stackoverflow.MyAnnotation * *(..))")
protected void myPointcut() {
}

@AfterThrowing(pointcut = "myPointcut() && args(someId,..)", throwing = "e")
public void afterThrowingException(JoinPoint joinPoint, Exception e, Integer someId) {
    System.out.println(someId.toString());
}

@AfterReturning(pointcut = "myPointcut() && args(someId,..)")
public void afterSuccessfulReturn(JoinPoint joinPoint, Integer someId) {
    System.out.println(someId.toString());
}
Iwo Kucharski
quelle
5

Sie können eine der folgenden Methoden verwenden.

@Before("execution(* ong.customer.bo.CustomerBo.addCustomer(String))")
public void logBefore1(JoinPoint joinPoint) {
    System.out.println(joinPoint.getArgs()[0]);
 }

oder

@Before("execution(* ong.customer.bo.CustomerBo.addCustomer(String)), && args(inputString)")
public void logBefore2(JoinPoint joinPoint, String inputString) {
    System.out.println(inputString);
 }

joinpoint.getArgs () gibt ein Objektarray zurück. Da die Eingabe eine einzelne Zeichenfolge ist, wird nur ein Objekt zurückgegeben.

Beim zweiten Ansatz sollte der Name in Ausdruck und Eingabeparameter in der Beratungsmethode, dh args(inputString)und , identisch seinpublic void logBefore2(JoinPoint joinPoint, String inputString)

Hier addCustomer(String)gibt die Methode mit einem String-Parameter.

sunj
quelle
3

Wenn es sich um ein einzelnes String-Argument handelt, gehen Sie wie folgt vor: joinPoint.getArgs()[0];

Ori Dar
quelle
3

Sie können den Methodenparameter und seinen Wert abrufen und wenn er mit einer Anmerkung mit folgendem Code versehen ist:

Map<String, Object> annotatedParameterValue = getAnnotatedParameterValue(MethodSignature.class.cast(jp.getSignature()).getMethod(), jp.getArgs()); ....

private Map<String, Object> getAnnotatedParameterValue(Method method, Object[] args) {
        Map<String, Object> annotatedParameters = new HashMap<>();
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        Parameter[] parameters = method.getParameters();

        int i = 0;
        for (Annotation[] annotations : parameterAnnotations) {
            Object arg = args[i];
            String name = parameters[i++].getDeclaringExecutable().getName();
            for (Annotation annotation : annotations) {
                if (annotation instanceof AuditExpose) {
                    annotatedParameters.put(name, arg);
                }
            }
        }
        return annotatedParameters;
    }
Matrix Buster
quelle
1

Wenn Sie @Aspect verwenden, können Sie diese Methode in Ihren Aspekt einfügen und den JoinPoint sowie den Namen des gewünschten Parameters senden.

private Object getParameter(ProceedingJoinPoint joinPoint, String parameterName) {
    Object valueParameter = null;
    if (Objects.nonNull(joinPoint) && joinPoint.getSignature() instanceof MethodSignature
            && Objects.nonNull(parameterName) ) {
        MethodSignature method = (MethodSignature)joinPoint.getSignature();
        String[] parameters = method.getParameterNames();
        for (int t = 0; t< parameters.length; t++) {
            if( Objects.nonNull(parameters[t]) && parameters[t].equals(parameterName)) {
                Object[] obj = joinPoint.getArgs();
                valueParameter = obj[t];
            }
        }
    }
    return valueParameter;
}

und das Anrufbeispiel:

Object parameterObject = getParameter(joinPoint, "nameClient");
if ( Objects.nonNull(parameterObject) ) {
    String parametro = String.valueOf(parameterObject);
}

Sie müssen nur den Objekttyp kennen, der konvertiert werden soll

Baldiry
quelle