AspecJ - 与具有通用参数的方法匹配的切入点

时间:2017-11-13 12:30:33

标签: java generics aop aspectj spring-aop

我有一个接受任何类型作为参数的泛型方法 例如,我想要一个切入点匹配对方法的调用仅与' String'键入作为其参数。最终,要求是将建议的执行范围限制为“字符串”。参数。

这是我的通用类和方法:

public class Param<T> {
    public T execute(T s){
        return s;
    }
}

主类:我的应用程序使用Boolean和String作为参数调用方法。

public static void main(String[] args) {
    Param<String> sp = new Param<String>();
    String rs = sp.execute("myString"); //want a joint point

    Param<Boolean> bp = new Param<Boolean>();
    Boolean rb = bp.execute(true); //dont want a joint point
}

以下切入点对String和Boolean参数都有效(实际上适用于任何类型)。但是我希望切入点仅在参数类型为String时拦截方法调用。

@Pointcut("call(* com.amazon.auiqa.aspectj.generics.Param.execute(**))")
void param(){}

@Pointcut("execution(Object com.amazon.auiqa.aspectj.generics.Param.execute(Object))")
void param(){}

以下哪些不适合我:

 @Pointcut("execution(String com.amazon.auiqa.aspectj.generics.Param.execute(String))")
 @Pointcut("call(String com.amazon.auiqa.aspectj.generics.Param.execute(String))")

我想知道是否有可能实现我想在这里实现的目标。我想用方法返回类型做同样的事情。

2 个答案:

答案 0 :(得分:0)

您不能使用AspectJ,也不能使用任何其他字节码操作库,因为泛型类型信息实际上已从编译的字节码中删除(从Java 9开始),因此您的泛型方法变为public Object execute(Object s),因为类型参数T无限制。有关详细信息,请参阅Java文档中的Type Erasure

虽然原始方法签名以元数据的形式保留,但编译器可以在编译通用代码时检查是否遵守类型边界,但这无助于您以任何方式确定实例的泛型类型参数该类的实例化,因为根本不存在该信息。

答案 1 :(得分:0)

其他帖子提到的关于Java擦除的内容是真的。

  

AspectJ 5不允许在切入点表达式和类型模式中使用类型变量。相反,使用类型参数作为其签名一部分的成员将通过其擦除进行匹配。 Java 5定义了用于确定类型擦除的规则,如下所示。

     

|T|代表某种类型T的删除。然后:

     
      
  • 参数化类型T<T1,...,Tn>的删除为|T|。例如,List<String>的删除是List

  •   
  • 嵌套类型T.C的删除是|T|.C。例如,嵌套类型Foo<T>.Bar的删除是Foo.Bar

  •   
  • 数组类型T[]的删除是| T|[]。例如,List<String>[]的删除是List[]

  •   
  • 类型变量的擦除是其最左边的边界。例如,类型变量P的擦除为Object,类型变量N extends Number的擦除为Number

  •   

您可以找到更多here

但是,您可以执行以下操作:

  1. 捕获execute的所有Param方法执行。
  2. 使用String按参数类型过滤instanceof参数的要求。
  3. 请记住,需要加号(+)来表示通用。

    这是一个例子,

    @Component
    @Aspect
    public class ParamAspect {
    
        @Pointcut("execution(public * com.amazon.auiqa.aspectj.generics.Param+.execute(..))")
        public void pointcut() {
        }
    
        @Before("pointcut()")
        public void intercept(JoinPoint jp) {
            System.out.println(
                    "Entering class: " + jp.getSignature().getDeclaringTypeName() +
                            " - before method: " + jp.getSignature().getName());
    
            // check for argument type of String 
            Object[] args = jp.getArgs();
            if (args.length == 1) {
                if (args[0] instanceof String) {
                    System.out.println("1. parameter type is string");
                } else {
                    System.out.println("2. parameter type is not string");
                }
            }
        }
    }