使用Aspectj调用joinPoint.getArgs时如何忽略带有javax注释的参数?

How to ignore parameters with javax annotation when calling joinPoint.getArgs using Aspectj?

我的函数包含不同的 javax 查询注释,例如:@QueryParam@Context@PathParam 等。

有没有办法在调用 joinPoint.getArgs() 时排除这些参数?

示例:

    @POST
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    @Path("{pathParam}/v1/{pathParam2}/")
    @MyAnnotation
    public Response update(@PathParam("pathParam") String p1, @PathParam("pathParam2") int p2, MyObject x);



    @Before("@annotation(MyAnnotation)")
        public void doSomething(JoinPoint joinPoint){
            Object[] objects = joinPoint.getArgs(); // HERE - is there a way to get only MyObject and not other params..?
    }

我想要这样做的原因是我有几个 url,同时将 ~10% 标记为持久。这意味着我希望将输入数据保存在某个持久服务中。查询和上下文参数对我来说并不重要,但输入数据本身很重要。

我不认为有什么神奇的方法可以做到这一点,所以选择显而易见的方法:

  • 定义你的论点接受标准;
  • 遍历 args 并根据先前定义的条件进行过滤,仅此而已。

看来您的验收标准是 arg 没有用这些 javax 注释进行注释,对吗?

试试这个:

Object[] args = joinPoint.getArgs();
Annotation[][] anns = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameterAnnotations();

for (int i = 0; i < args.length; i++) {
    for (int j = 0; j < args[i].length; j++) {
        // check here for the annotations you would like to exclude
    }
}

假设你真的使用 full AspectJ 而不是 Spring AOP 像很多其他人一样,你应该意识到在 full AspectJ @annotation(XY)可能不仅匹配 execution() 个连接点,而且还匹配 call(),即您的建议将被触发两次。更糟糕的是,如果除了方法执行之外的其他地方也被注释了——例如classes, fields, constructors, parameters - 切入点也将匹配并且您尝试转换为 MethodSignature 将因此导致异常。

此外,请注意,在@AspectJ 语法中,您需要提供要匹配的注解的完全限定 class 名称,即不要忘记在包名称前加上前缀。否则根本就没有比赛。因此,在执行任何其他操作之前,您需要将切入点更改为:

@annotation(de.scrum_master.app.MyAnnotation) && execution(* *(..))

现在这是一个完全自洽的例子,SSCCE 产生可重复的结果,正如我在你的问题下的评论中所要求的:

译注:

package de.scrum_master.app;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {}

驱动申请:

如您所见,测试方法的参数带有不同类型的注解:

  1. 仅 javax 注释
  2. javax + 自带注解
  3. 只有你自己的注释
  4. 无注释

我们要忽略#1/2,只打印#3/4。

package de.scrum_master.app;

import javax.ws.rs.PathParam;
import javax.ws.rs.core.Response;

public class Application {
  public static void main(String[] args) {
    new Application().update("foo", 11, "bar", 22);
  }

  @MyAnnotation
  public Response update(
    @PathParam("pathParam") String p1,
    @PathParam("pathParam2") @MyAnnotation int p2,
    @MyAnnotation String text,
    int number
  ) {
    return null;
  }
}

看点:

正如用户 Andre Paschoal 开始在他的代码片段中展示的那样,您需要遍历参数和注释数组才能实现过滤技巧。我认为它非常丑陋,而且可能只是为了日志记录而变慢(我认为这是你想要做的),但对于它的价值,这是你的解决方案:

package de.scrum_master.aspect;

import java.lang.annotation.Annotation;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;

@Aspect
public class ParameterFilterAspect {
  @Before("@annotation(de.scrum_master.app.MyAnnotation) && execution(* *(..))")
  public void doSomething(JoinPoint joinPoint) {
    Object[] args = joinPoint.getArgs();
    MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
    Annotation[][] annotationMatrix = methodSignature.getMethod().getParameterAnnotations();
    for (int i = 0; i < args.length; i++) {
      boolean hasJavaxAnnotation = false;
      for (Annotation annotation : annotationMatrix[i]) {
        if (annotation.annotationType().getPackage().getName().startsWith("javax.")) {
          hasJavaxAnnotation = true;
          break;
        }
      }
      if (!hasJavaxAnnotation)
        System.out.println(args[i]);
    }
  }
}

控制台日志:

bar
22

太棒了! :-)

这段代码适合我:

Annotation[][] anns = ((MethodSignature)   thisJoinPoint.getSignature()).getMethod().getParameterAnnotations();

parameterValues = thisJoinPoint.getArgs();
signature = (MethodSignature) thisJoinPoint.getSignature();
parameterNames = signature.getParameterNames();
if (parameterValues != null) {
    for (int i = 0; i < parameterValues.length; i++) {

        boolean shouldBeExcluded = false;
        for (Annotation annotation : anns[i]) {
            if (annotation instanceof ExcludeFromCustomLogging) {//<<---------ExcludeFromCustomLogging is my class
                shouldBeExcluded = true;
                break;
            }
        }
        if (shouldBeExcluded) {
            //System.out.println("should be excluded===>"+parameterNames[i]);
            continue;
        }

  //.......and your business

}