如何使用 aspectj 围绕 aspect 修改未知数量的方法参数

How to modify unknown number of method args with aspectj around aspect

我想创建一个注释,它使用环绕方面来通过该注释擦除参数。

例如,一个方法可能如下所示:

public void setName(@Scrubbed String name) { ... }

或者也许

public void setFullName(@Scrubbed String firstName, @Scrubbed String lastName) { ... }

我想做的是这些方面的事情:

Object around(String arg) : call(* *(.., @Scrubbed (String), ..)) {
    return proceed(this.scrubString(arg));
}

但是,我想以任意顺序处理任意数量的参数。 我正在做的工作,但似乎是一个 hack 是这样的:

Object around() : call(public * *(.., @Scrubbed (String), ..)) {
    Method method = MethodSignature.class.cast(thisJoinPoint.getSignature()).getMethod();
    Object[] args = thisJoinPoint.getArgs();
    Annotation[][] parameterAnnotations = method.getParameterAnnotations();
    for (int argIndex = 0; argIndex < args.length; argIndex++) {
        for (Annotation paramAnnotation : parameterAnnotations[argIndex]) {
            if (!(paramAnnotation instanceof Scrubbed)) {
                continue;
            }
            args[argIndex] = this.scrubString((String)args[argIndex]);
        }
    }
    try {
        return method.invoke(thisJoinPoint.getTarget(), args);
    } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
        e.printStackTrace();
    }
    return null;
}

我基本上是使用 joinPoint 来访问反射信息,最后使用 method.invoke 而不是 proceed()。

我希望能够访问 ProceedingJoinPoint 并调用它提供的 proceed(Ojbect[] args) 方法,但我不知道如何使用本机 aspectj 语法来做到这一点。

任何想法。我想我可以使用注释 @AJ aspectj 语法,但我们的其他方面都使用本机语法。

非常抱歉,我无法为您提供原生 AspectJ 语法的优雅解决方案,因为我更喜欢它而不是 annotation-based 语法。但这是您想要在 @AspectJ 语法中更容易实现的罕见情况之一。

至于任意位置和数字的参数注释,您需要按照您已经建议的方式使用反射。传统的切入点和参数绑定语法无法处理这种关于方法签名的不确定性。

但是在@AspectJ 语法中确实可以使用 getArgs() 参数数组的修改版本。我们开始吧:

标记注释:

package de.scrum_master.app;

import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target(PARAMETER)
public @interface Scrubbed {}

目标class + 驱动程序应用程序:

package de.scrum_master.app;

public class Application {
  public void setName(@Scrubbed String name) {
    System.out.println("name = " + name);
  }

  public void setFullName(@Scrubbed String firstName, @Scrubbed String lastName) {
    System.out.println("firstName = " + firstName + ", lastName = " + lastName);
  }

  public static void main(String[] args) {
    Application application = new Application();
    application.setName("Albert Einstein");
    application.setFullName("Albert", "Einstein");
  }
}

看点:

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 de.scrum_master.app.Scrubbed;

@Aspect
public class StringScrubberAspect {
  @Around("call(public * *(.., @de.scrum_master.app.Scrubbed (String), ..))")
  public Object scrubStringAdvice(ProceedingJoinPoint thisJoinPoint) throws Throwable {
    Method method = MethodSignature.class.cast(thisJoinPoint.getSignature()).getMethod();
    Object[] args = thisJoinPoint.getArgs();
    Annotation[][] parameterAnnotations = method.getParameterAnnotations();
    for (int argIndex = 0; argIndex < args.length; argIndex++) {
      for (Annotation paramAnnotation : parameterAnnotations[argIndex]) {
        if (paramAnnotation instanceof Scrubbed)
          args[argIndex] = scrubString((String) args[argIndex]);
      }
    }
    return thisJoinPoint.proceed(args);
  }

  private String scrubString(String string) {
    return string.replaceAll("[Ee]", "#");
  }
}

控制台日志:

name = Alb#rt #inst#in
firstName = Alb#rt, lastName = #inst#in

更新:我刚看到你已经建议了这个方法:

I supposed I could use annotation @AJ aspectj syntax, but the rest of our aspects are using native syntax.

这可能是一个外观缺陷,但没有理由不能混合使用这两种语法类型,只要您不在同一方面混合使用它们即可。 (甚至后者在很多情况下都有效,但在某些情况下却无效。)