Ramesh Daddagol
Ramesh Daddagol

Reputation: 83

AspectJ - pointcut to match a method that has generic parameters

I have a generic method that accepts any type as its parameter.
For example, I would like a pointcut that matches the calls made to the method only with 'String' type as its parameter. Ultimately the requirement is to limit the scope which the advices get executed for to 'String' parameters.

Here is my generic class and method:

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

Main class: My app makes calls to the method with both Boolean and String as parameters.

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
}

Below pointcuts are valid for both String and Boolean parameters (work for any type actually). But I would like a pointcut to intercept method calls only when parameter is of type 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(){}

Below ones did not work for me:

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

I was wondering if it is possible to achieve what I want to achieve here. I would like to do the same thing with method return types.

Upvotes: 5

Views: 6328

Answers (2)

Indra Basak
Indra Basak

Reputation: 7394

It's true what the other posting mentioned about Java erasure.

AspectJ 5 does not allow the use of type variables in pointcut expressions and type patterns. Instead, members that use type parameters as part of their signature are matched by their erasure. Java 5 defines the rules for determing the erasure of a type as follows.

Let |T| represent the erasure of some type T. Then:

  • The erasure of a parameterized type T<T1,...,Tn> is |T|. For example, the erasure of List<String> is List.

  • The erasure of a nested type T.C is |T|.C. For example, the erasure of the nested type Foo<T>.Bar is Foo.Bar.

  • The erasure of an array type T[] is |T|[]. For example, the erasure of List<String>[] is List[].

  • The erasure of a type variable is its leftmost bound. For example, the erasure of a type variable P is Object, and the erasure of a type variable N extends Number is Number.

You can find more here.

However, you can do the following:

  1. Capture all execute method executions of Param.
  2. Filter your requirements of String arguments by the type of argument by using instanceof.

Remember, a plus sign (+) is needed to indicate a generic.

Here is an example,

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

Upvotes: 1

N&#225;ndor Előd Fekete
N&#225;ndor Előd Fekete

Reputation: 7098

You cannot do that with AspectJ, nor with any other bytecode manipulation library as generic type information is actually erased from the compiled bytecode (as of Java 9), so your generic method becomes public Object execute(Object s), since the type argument T is unbounded. See Type Erasure at the Java Documentation for further info.

While the original method signature is preserved in the form of metadata, the compiler can check whether type bounds are respected or not while compiling against generic code, but this will not help you in any way to determine what generic type argument an instance of that class was instantiated with, because that information is simply not present at all.

Upvotes: 6

Related Questions