2013-03-27 73 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 
}