首页 > 解决方案 > 如何修改未知数量的方法参数与方面的方面

问题描述

我想创建一个注释,它使用环绕方面来使用该注释清理参数。

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

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

但是,我想以任何顺序处理任意数量的参数。我有什么工作,但看起来像一个黑客是这样的:

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

解决方案


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

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

getArgs()但是在@AspectJ 语法中确实可以继续修改参数数组的版本。开始了:

标记注释:

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 {}

目标类+驱动应用:

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

更新:我刚刚看到您已经建议了这种方法:

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

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


推荐阅读