Get method arguments using Spring AOP?

JavaSpring

Java Problem Overview


I am using Spring AOP and have below aspect:

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

Above aspect intercepts addCustomer method execution. addCustomer method takes string as an input. But I need to log input passed to addCustomer method inside logBefore method.
Is it possible to do so ?

Java Solutions


Solution 1 - Java

You have a few options:

First, you can use the JoinPoint#getArgs() method which returns an Object[] containing all the arguments of the advised method. You might have to do some casting depending on what you want to do with them.

Second, you can use the args pointcut expression like so:

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

then your method can instead be defined as

public void logBefore(JoinPoint joinPoint, String yourString) 

Solution 2 - Java

Yes, the value of any argument can be found using 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);
      ...
   }
}

Solution 3 - Java

If you have to log all args or your method have one argument, you can simply use getArgs like described in previous answers.

If you have to log a specific arg, you can annoted it and then recover its value like this :

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

Examples of use :

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
}

Solution 4 - Java

There is also another way if you define one pointcut for many advices it can be helpful:

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

Solution 5 - Java

Your can use either of the following methods.

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

or

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

joinpoint.getArgs() returns object array. Since, input is single string, only one object is returned.

In the second approach, the name should be same in expression and input parameter in the advice method i.e. args(inputString) and public void logBefore2(JoinPoint joinPoint, String inputString)

Here, addCustomer(String) indicates the method with one String input parameter.

Solution 6 - Java

you can get method parameter and its value and if annotated with a annotation with following code:

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

Solution 7 - Java

If it's a single String argument, do: joinPoint.getArgs()[0];

Solution 8 - Java

if your using @Aspect an option is add this method inside your Aspect and send the JoinPoint and the name of parameter you need.

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

and the call example:

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

Only need know the type of object for convert

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser1016403View Question on Stackoverflow
Solution 1 - JavaSotirios DelimanolisView Answer on Stackoverflow
Solution 2 - JavaReimeusView Answer on Stackoverflow
Solution 3 - Javaandolsi ziedView Answer on Stackoverflow
Solution 4 - JavaIwo KucharskiView Answer on Stackoverflow
Solution 5 - JavasunjView Answer on Stackoverflow
Solution 6 - JavaMatrix BusterView Answer on Stackoverflow
Solution 7 - JavaOri DarView Answer on Stackoverflow
Solution 8 - JavaBaldiryView Answer on Stackoverflow