Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 887
Ramesh Daddagol Avatar asked Nov 13 '17 12:11

Ramesh Daddagol


People also ask

Which of the following option will match the given pointcut?

Explanation: Union means the methods that either pointcut matches. Intersection means the methods that both pointcuts match. Union is usually more useful. Explanation: Using the static methods in the org.

What can spring AOP pointcut match?

Spring AOP only supports method execution join points for Spring beans, so you can think of a pointcut as matching the execution of methods on Spring beans.

What methods does this pointcut expression reference?

This pointcut matches any method that starts with find and has only one parameter of type Long. If we want to match a method with any number of parameters, but still having the fist parameter of type Long, we can use the following expression: @Pointcut("execution(* *.. find*(Long,..))")

What is pointcut in AspectJ?

AspectJ provides primitive pointcuts that capture join points at these times. These pointcuts use the dynamic types of their objects to pick out join points. They may also be used to expose the objects used for discrimination. this(Type or Id) target(Type or Id)


2 Answers

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.

like image 173
Nándor Előd Fekete Avatar answered Oct 19 '22 00:10

Nándor Előd Fekete


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");
            }
        }
    }
}
like image 42
Indra Basak Avatar answered Oct 19 '22 00:10

Indra Basak