【问题标题】:Retrieve parameter value from ProceedingJoinPoint从 ProceedingJoinPoint 检索参数值
【发布时间】:2014-12-26 16:34:20
【问题描述】:

在我的请求中我有一个参数名称“accessToken”,我如何从 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 ?
        }
    }
}

调用方法:

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

我想在 AOP 中获取这个访问令牌。

提前致谢。

【问题讨论】:

  • 请提供更多信息,例如切面切入点和要截取的一个或多个代码样本,参数类型和参数位置(例如从方法签名的左/右计数时的第一个、第二个、第三个参数)。然后我会用参数绑定而不是丑陋的getArgs() 或反射代码来提供一个优雅的答案。
  • 感谢您的回复,我正在尝试验证 accessToken 。在我的其余应用程序中,我正在发送带有请求正文的 acceeesToken 类似于 {"accessToken":"myValue"} 我需要检索该访问令牌来自 ProceedingJoinPoint。
  • 我想看代码,而不是散文中的描述。我是 AspectJ 专家,而不是 Spring 专家。
  • 我刚刚编辑了我的问题..添加了调用方法源代码。
  • 请同时显示建议的切入点以及它们所在的类/方面名称和包。请不要只分享sn-ps,而是给我一张大图,最好是SSCCE .还请回答我之前关于参数位置的问题,并告诉我每个方法是否可以有多个带有访问令牌注释的参数。

标签: aop spring-aop spring-aspects


【解决方案1】:

好的,Shamseer,我只是有一点空闲时间,所以我试图在你不回答我的 cmets 的所有问题的情况下回答你的问题。我这样做的方法是我将使用参数名称,但尝试使用注释@RequestParam("accessToken") 匹配参数,即我将匹配注释类型和具有“accessToken”魔术名称而不是方法参数名称的值,由于在编译期间从类文件中剥离调试信息或由于混淆,可能会由于不了解您方面的人的简单重构而改变。

这是一些自洽的示例代码,它针对 AspectJ 而不是 Spring AOP 进行了测试,但后者的语法无论如何都是前者语法的子集:

带有 main 方法的示例类:

共有三种方法,所有方法都在其中一个参数上有@RequestParam注解,但其中只有两个具有“accessToken”的神奇值。无论参数类型如何(一个String 和一个int)都应该匹配,但不应匹配带有@RequestParam("someParameter") 的那个。严格来说,所有的方法执行都是匹配的,但是运行时反射会消除不想要的。如果您的注释将在类或方法级别或参数类型上,我们可以直接在切入点中匹配它们而无需反射,但在参数注释的情况下,这超出了 AspectJ 当前 (v1.8.4) 的能力,我们必须使用反射,很遗憾。

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

用于编译代码的虚拟助手类:

package de.scrum_master.app;

public class UserDto {}

方面:

请注意,我的包罗万象的切入点execution(* *(..)) 仅用于说明。你应该把范围缩小到你真正想要匹配的方法。

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

控制台输出:

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

另请参阅this answer,了解一个相关但更简单的问题,使用类似的代码。

【讨论】:

  • 也许你想重构你的代码并使访问令牌成为一个自己的类而不是一个简单的字符串。这样,您可以以类型安全的方式工作,并且还可以轻松地将切入点与参数类型而不是变量名称或注释值匹配。
  • @kriegaex 我想为上面的代码创建一个通用方法,但是我遇到了一个问题,因为我无法将AnnotationType 传递到我的if (!(annotation instanceof RequestParam {AnnotationType})) 行的方法中我将 RequestParam 作为参数传递给您的代码
  • 对 4 年前的问题发表评论并不是提出和回答新问题的好工具,无论是否相关。我建议您使用MCVE 创建一个新问题,在此处链接到我的答案并准确解释您想要做什么以及它在哪里/如何失败。谢谢。
  • 如果你想要 argNames 参考 stackoverflow.com/a/49155868/234110 做这样的事情 String[] argNames = ((CodeSignature) joinPoint.getSignature()).getParameterNames();
  • 我知道,并且我已经解释过这是一个坏主意,因为结果方面将是 (a) 糟糕的设计,(b) 脆弱(如果有人重命名方法参数会破坏),( c) 在这种情况下是不必要的,因为我们可以简单地匹配参数的注释,(d) 如果在没有调试信息的情况下编译将无法工作。
【解决方案2】:

要获取作为方法参数输入的参数,您可以尝试以下操作:

Object[] methodArguments = joinPoint.getArgs();

【讨论】:

  • 对不起,我的问题,我不知道我的问题是否正确? getArgs()方法是否使用Reflection取回参数?
猜你喜欢
  • 2023-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-02
  • 2011-08-08
  • 2011-08-08
  • 2011-07-01
相关资源
最近更新 更多