2013-03-27 165 views
35

我使用Spring AOP和低於方面:使用Spring AOP獲取方法參數?

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

} 

上述方面攔截addCustomer方法執行。 addCustomer方法將字符串作爲輸入。 但我需要登錄輸入傳遞給addCustomer方法logBefore方法。
是否有可能這樣做?

+0

什麼是'addCustomer的方法簽名(..)'? – 2013-03-27 13:54:09

回答

46

您有幾種選擇:

首先,您可以使用JoinPoint#getArgs()方法,該方法返回一個包含建議方法的所有參數的Object[]。你可能需要做一些演員,取決於你想要對他們做什麼。

其次,你可以使用args切入點表達式,像這樣:

// 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,..)") 

然後可以代替你的定義方法,

public void logBefore(JoinPoint joinPoint, String yourString) 
+2

如果我沒有弄錯,這兩個選項之間的行爲有所不同。第二個只會在arg存在時觸發,而第一個將會被觸發,即使參數不存在。 – 2016-10-10 15:30:28

+0

@SamuelEUSTACHI尚未爲第一個示例指定切入點表達式。如果我們假設執行'addCustomer(..)',不能有任何爭論或許多。 – 2016-10-10 15:35:30

15

是的,可以使用發現的任何參數的值getArgs

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

如果它是一個字符串參數,這樣做: joinPoint.getArgs()[0];

2

你可以使用下面的方法。

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

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

joinpoint.getArgs()返回對象陣列。因爲輸入是單個字符串,所以只返回一個對象。

在第二種方法中,該名稱應在通知方法表達和輸入參數相同即args(inputString)public void logBefore2(JoinPoint joinPoint, String inputString)

這裏,addCustomer(String)指示與一個字符串輸入參數的方法。

2

也有另一種方式,如果你定義一個切入點許多建議它可以幫助:

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

你可以得到方法參數和它的值,如果註釋與下面的代碼註釋:

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

如果您需要登錄的所有ARGS或者你的方法有一個說法,你可以SIMPL Ÿ使用前面的答案中描述的getArgs。

如果您需要登錄一個特定的精氨酸,可以annoted它,然後恢復像這樣的價值:

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

使用的例子:

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 
}