springaopspring-aopspring-aspects

Retrieve parameter value from ProceedingJoinPoint


In my Request i have a parameter name "accessToken", how do i get request parameter value from ProceedingJoinPoint ?

public Object handleAccessToken(ProceedingJoinPoint joinPoint) throws Throwable { 
    final Signature signature = joinPoint.getStaticPart().getSignature();
    if (signature instanceof MethodSignature) {
        final MethodSignature ms = (MethodSignature) signature;
        String[] params = ms.getParameterNames();
        for (String param : params) {
            System.out.println(param);
            // here how do i get parameter value using param ?
        }
    }
}

Calling method:

public MyResponse saveUser(
    @RequestParam("accessToken") String accessToken,
    @RequestBody final UserDto userDto
) {
    // code 
}

I want to get this access token in AOP.

Thanks in advance.


Solution

  • Okay, Shamseer, I just have a little sparetime, so I am trying to answer your question without you answering all my questions from my comments. The way I go about this is that I will not use parameter names, but try to match on parameters with an annotation @RequestParam("accessToken"), i.e. I will match on the annotation type and value with a magic name of "accessToken" rather than a method parameter name which might change due to a simple refactoring of someone who is unaware of your aspect, due to stripping debug information from the class file during compilation or due to obfuscation.

    Here is some self-consistent sample code which was tested against AspectJ, not Spring AOP, but the syntax of the latter is a subset of the former one's syntax anyway:

    Sample class with main method:

    There are three methods, all of which have a @RequestParam annotation on one of the parameters, but only two of those have the magic value of "accessToken". They should be matched regardless of the parameter type (one String and one int), but the one with @RequestParam("someParameter") should not be matched. Strictly speaking, all method executions are matched, but runtime reflection eliminates the undesired ones. If your annotations would be on class or method level or on parameter types, we could match them directly in the pointcut without reflection, but in the case of parameter annotations this is beyond AspectJ's current (v1.8.4) capabilities and we have to use reflection, unfortunately.

    package de.scrum_master.app;
    
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestParam;
    
    public class MyResponse {
        public MyResponse saveUser(
            @RequestParam("accessToken") String accessToken,
            @RequestBody final UserDto userDto
        ) {
            return this;
        }
    
        public MyResponse doSomething(
            @RequestParam("someParameter") String text,
            @RequestBody final UserDto userDto
        ) {
            return this;
        }
    
        public MyResponse doSomethingElse(
            @RequestParam("accessToken") int number
        ) {
            return this;
        }
    
        public static void main(String[] args) {
            MyResponse myResponse = new MyResponse();
            myResponse.doSomething("I am not a token", new UserDto());
            myResponse.saveUser("I am a token", new UserDto());
            myResponse.doSomethingElse(12345);
        }
    }
    

    Dummy helper class to make the code compile:

    package de.scrum_master.app;
    
    public class UserDto {}
    

    Aspect:

    Please note that my catch-all pointcut execution(* *(..)) is just for illustration. You should narrow it down to the methods you actually want too match.

    package de.scrum_master.aspect;
    
    import java.lang.annotation.Annotation;
    import java.lang.reflect.Method;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.web.bind.annotation.RequestParam;
    
    @Aspect
    public class AccessTokenAspect {
        @Around("execution(* *(..))")
        public Object handleAccessToken(ProceedingJoinPoint thisJoinPoint) throws Throwable {
            System.out.println(thisJoinPoint);
            Object[] args = thisJoinPoint.getArgs();
            MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getStaticPart().getSignature();
            Method method = methodSignature.getMethod();
            Annotation[][] parameterAnnotations = method.getParameterAnnotations();
            assert args.length == parameterAnnotations.length;
            for (int argIndex = 0; argIndex < args.length; argIndex++) {
                for (Annotation annotation : parameterAnnotations[argIndex]) {
                    if (!(annotation instanceof RequestParam))
                        continue;
                    RequestParam requestParam = (RequestParam) annotation;
                    if (! "accessToken".equals(requestParam.value()))
                        continue;
                    System.out.println("  " + requestParam.value() + " = " + args[argIndex]);
                }
            }
            return thisJoinPoint.proceed();
        }
    }
    

    Console output:

    execution(void de.scrum_master.app.MyResponse.main(String[]))
    execution(MyResponse de.scrum_master.app.MyResponse.doSomething(String, UserDto))
    execution(MyResponse de.scrum_master.app.MyResponse.saveUser(String, UserDto))
      accessToken = I am a token
    execution(MyResponse de.scrum_master.app.MyResponse.doSomethingElse(int))
      accessToken = 12345
    

    See also this answer on a related, but somewhat simpler question with similar code.