【问题标题】:Spring AOP - annotation with argsSpring AOP - 带参数的注释
【发布时间】:2018-07-24 08:55:43
【问题描述】:

我在 spring boot 中遇到了问题。我正在尝试为一些 RestControllers 提供额外的功能,并且我正在尝试使用一些自定义注释来实现它。这是一个例子。

我的注释:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyCustomAnnotation {
  String someArg();
}

我的方面:

@Aspect
@Component
public class MyAspect {
  @Around(
    value = "@annotation(MyCustomAnnotation)",
    argNames = "proceedingJoinPoint,someArg"
  )
  public Object addMyLogic(ProceedingJoinPoint proceedingJoinPoint, String someArg)
    throws Throwable
  {
    System.out.println(someArg);
    return proceedingJoinPoint.proceed();
  }
}

我的方法:

@MyCustomAnnotation(someArg = "something")
@GetMapping("/whatever/route")
public SomeCustomResponse endpointAction(@RequestParam Long someId) {
  SomeCustomResult result = someActionDoesNotMatter(someId);
  return new SomeCustomResponse(result);
}

主要基于文档(https://docs.spring.io/spring/docs/3.0.3.RELEASE/spring-framework-reference/html/aop.html - 7.2.4.6 建议参数)我很确定,它应该可以工作。

我在这里,因为它不...

让我抓狂的是,即使是 Intellij,在尝试帮助处理 argNames(空字符串 -> 红色下划线 -> alt+enter -> 正确的 argNames 属性)时也会给我这个,并保持红色......

根据文档,甚至不需要proceedingJoinPoint(没有它也不起作用):“如果第一个参数是JoinPoint,ProceedingJoinPoint...”

使用当前设置,它显示“未绑定切入点参数'someArg'”

在这一点上,我还应该注意,没有 args 它可以正常工作。

其实我有两个问题:

  1. 为什么这不起作用? (这很明显)

  2. 如果我想为某些控制器提供一些额外的功能,并且我想从外部对其进行参数化,那么它在 Spring Boot 中是正确的模式吗? (对于 python,使用装饰器很容易做到这一点 - 我不太确定,我不会被类似的语法误导)

一个例子(上面的例子很抽象):

我想创建一个@LogEndpointCall 注解,路由的开发者稍后可以将它放在他正在开发的端点上

...但是,如果他可以添加一个字符串(或者更可能是一个枚举)作为参数,那就太好了

@LogEndpointCall(EndpointCallLogEnum.NotVeryImportantCallWhoCares)

@LogEndpointCall(EndpointCallLogEnum.PrettySensitiveCallCheckItALot)

这样会触发相同的逻辑,但使用不同的参数 -> 并将保存到不同的目的地。

【问题讨论】:

    标签: spring-boot spring-aop spring-annotations


    【解决方案1】:

    您不能直接将注释属性绑定到通知参数。只需绑定注解本身并正常访问其参数即可:

    @Around("@annotation(myCustomAnnotation)")
    public Object addMyLogic(
      ProceedingJoinPoint thisJoinPoint,
      MyCustomAnnotation myCustomAnnotation
    )
      throws Throwable
    {
      System.out.println(thisJoinPoint + " -> " + myCustomAnnotation.someArg());
      return thisJoinPoint.proceed();
    }
    

    它将使用 Spring AOP 打印类似的内容

    execution(SomeCustomResponse de.scrum_master.app.Application.endpointAction(Long)) -> something
    

    和 AspectJ 类似的东西(因为 AJ 也知道调用连接点,而不仅仅是执行)

    call(SomeCustomResponse de.scrum_master.app.Application.endpointAction(Long)) -> something
    execution(SomeCustomResponse de.scrum_master.app.Application.endpointAction(Long)) -> something
    

    【讨论】:

    • 我在使用完整的包名时遇到了问题。我正在尝试这个:@Around("@annotation(com.xyz.myCustomAnnotation)")。当我需要只输入注释的名称时,如上面的答案所示:@Around("@annotation(myCustomAnnotation)")。上面的答案帮助我访问了注释及其参数。
    • 如果您将注解绑定到方法参数并且您不得,则您必须使用完整的包名i> 如果你这样做,请使用它。这是合乎逻辑的,因为如果有参数,可以很容易地确定类型。如果没有,则需要准确指定类型,因为它无法从导入的类中推断出来。
    【解决方案2】:

    如果您希望您的方法拦截考虑 args 的方法,您必须在切入点表达式中明确提及,要让它在这里工作是您应该做的:

    @Around(
    value = "@annotation(MyCustomAnnotation) && args(someArg)",
    argNames = "someArg")
    

    请注意,我添加了 && args(someArg),您可以根据需要添加任意数量的参数,在 argNames 中您可以省略 proceedingJoinPoint

    【讨论】:

    • 这个答案是错误的。问题不在于绑定方法参数,而在于访问绑定注释的参数。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-22
    • 1970-01-01
    • 2013-05-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-15
    相关资源
    最近更新 更多