使用Spring AOP获取方法参数?


74

我正在使用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
有可能这样做吗?


1
什么是方法签名addCustomer(..)
Sotirios Delimanolis 2013年

Answers:


99

您有几种选择:

首先,您可以使用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) 

8
如果我没记错的话,这两个选项之间的行为会有差异。仅当arg存在时,第二个才会触发,而即使参数不存在,第一个也将触发。
塞缪尔·EUSTACHI,

@SamuelEUSTACHI没有为第一个示例指定切入点表达式。如果我们假设执行addCustomer(..),请确定。可以没有参数,也可以没有很多。
Sotirios Delimanolis 2016年

第二种选择非常方便。我需要在将请求发送到某些xyz url之前和之后记录请求和响应以及其他一些参数,并且如果没有第二个选项,Pointcut表达式将变得非常复杂且难以理解。
Mohammed Salman Shaikh,

25

是的,可以使用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);
      ...
   }
}

13

如果必须记录所有args或方法具有一个参数,则可以像前面的答案中所述简单地使用getArgs。

如果必须记录特定的arg,则可以对其进行注释,然后按以下方式恢复其值:

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

@Around(“ ...”)此模式对@Around无效吗?
Harpreet Sandhu-TheRootCoder

"..." 意味着完成您的模式。
Andolsi引用

1
很好,我一直在寻找
Horatiu Jeflea,

1
太好了,这正是我为基于属性的访问控制评估所寻找的!
ryfterek

8

如果为许多建议定义一个切入点,还有另一种方法可能会有所帮助:

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

6

您可以使用以下两种方法之一。

@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)指示具有一个String输入参数的方法。


3

如果它是单个String参数,请执行以下操作: joinPoint.getArgs()[0];


3

您可以获取方法参数及其值,如果使用以下代码通过注释进行注释,则可以:

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

如果您使用@Aspect,则将一个方法添加到Aspect中,然后发送JoinPoint和所需的参数名称。

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

以及通话示例:

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

只需要知道转换对象的类型

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.