Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write an Aspect pointcut based on an annotated parameter

I'm having a bit of trouble working out how to create a pointcut that will operate on beans that have a specific annotated parameter. My eventual aim is to validate the value of the parameter before it's processed, but for the moment I just need to create the pointcut.

Consider the following annotation

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.PARAMETER })
public @interface MyAnnotation {}

I'd then like to apply this to a number of methods like:

public void method1(@MyAnnotation long i) {}
public void method2(String someThing, @MyAnnotation long i) {}
public void method3(String someThing, @MyAnnotation long i, byte value) {}

So

  • I don't care which class (or package) the methods are in
  • The position of the annotated argument will vary.
  • I do know that annotated value will only apply to a specific type

My pointcut implementation needs to be something along the lines of:

@Before(value = "* *(..) && args(verifyMe)")
public void verifyInvestigationId(long verifyMe) {}

I'm getting a bit confused about exactly what that @Before value needs to be and how to tie in the annotation and its type. At this point it's probably not worth listing the things I've tried!

Update: Based on the advice I've seen in http://stackoverflow.com/questions/3565718/pointcut-matching-methods-with-annotated-parameters/3567170#3567170 (and correcting a couple of misunderstandings and adding space I overlooked) I've got to the point where the following works:

@Before("execution(public * *(.., @full.path.to.MyAnnotation (*), ..))")
public void beforeMethod(JoinPoint joinPoint) {
    System.out.println("At least one of the parameters are annotated with @MyAnnotation");
}

This is almost what I need - all I need to do is pass the value of the annotated argument as an parameter to the method. I can't quite work out the syntax to get Spring to do this (the linked answer does not show this).

like image 919
Stormcloud Avatar asked Apr 16 '15 17:04

Stormcloud


People also ask

What methods does the pointcut expression reference?

Pointcuts use class, field, constructor, and method expressions to specify the actual joinpoint that should be intercepted/watched.

What is pointcut annotation?

Pointcut is a set of one or more JoinPoint where an advice should be executed. You can specify Pointcuts using expressions or patterns as we will see in our AOP examples. In Spring, Pointcut helps to use specific JoinPoints to apply the advice.

What is pointcut expression reference?

Pointcut is an expression language of spring AOP which is basically used to match the target methods to apply the advice. It has two parts ,one is the method signature comprising of method name and parameters. Other one is the pointcut expression which determines exactly which method we are applying the advice to.

What is the difference between a join point and a pointcut?

A join point is an individual place where you can execute code with AOP. E.g. "when a method throws an exception". A pointcut is a collection of join points. E.g. "when a method in class Foo throws an exception".


1 Answers

Very similar to my answer here which sheltem already pointed to, the solution looks like this (in annotation-style syntax this time because in Spring AOP you cannot use native AspectJ syntax):

Original poster's annotation:

package annotations;

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

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.PARAMETER })
public @interface MyAnnotation {}

Driver Application:

I use the driver application in order to test my AspectJ solution. In Spring the class as well as the aspect need to be Spring beans/components in order for this to work.

package de.scrum_master.app;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import annotations.MyAnnotation;

public class Application {
    public void method1(@MyAnnotation int i) {}
    public void method2(String id, @MyAnnotation float f) {}
    public void method3(int i, @MyAnnotation List<String> strings, @MyAnnotation String s) {}
    public void method4(int i, @MyAnnotation Set<Integer> numbers, float f, boolean b) {}
    public void method5(boolean b, String s, @MyAnnotation String s2, float f, int i) {}
    public void notIntercepted(boolean b, String s, String s2, float f, int i) {}

    public static void main(String[] args) {
        List<String> strings = new ArrayList<String>();
        strings.add("foo");
        strings.add("bar");
        Set<Integer> numbers = new HashSet<Integer>();
        numbers.add(11);
        numbers.add(22);
        numbers.add(33);

        Application app = new Application();
        app.method1(1);
        app.method2("foo", 1f);
        app.method3(1, strings, "foo");
        app.method4(1, numbers, 1f, true);
        app.method5(false, "foo", "bar", 1f, 1);
        app.notIntercepted(false, "foo", "bar", 1f, 1);
    }
}

Aspect:

package de.scrum_master.aspect;

import java.lang.annotation.Annotation;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.SoftException;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;

import annotations.MyAnnotation;

@Aspect
public class ArgCatcherAspect {
    @Before("execution(public * *(.., @MyAnnotation (*), ..))")
    public void interceptMethodsWithAnnotatedParameters(JoinPoint thisJoinPoint) {
        System.out.println(thisJoinPoint);
        MethodSignature signature = (MethodSignature) thisJoinPoint.getSignature();
        String methodName = signature.getMethod().getName();
        Class<?>[] parameterTypes = signature.getMethod().getParameterTypes();
        Annotation[][] annotations;
        try {
            annotations = thisJoinPoint.getTarget().getClass().
                getMethod(methodName, parameterTypes).getParameterAnnotations();
        } catch (Exception e) {
            throw new SoftException(e);
        }
        int i = 0;
        for (Object arg : thisJoinPoint.getArgs()) {
            for (Annotation annotation : annotations[i]) {
                if (annotation.annotationType() == MyAnnotation.class) {
                    System.out.println("  " + annotation + " -> " + arg);
                    // Verify 'arg' here or do whatever
                }
            }
            i++;
        }
    }
}

Console log:

execution(void de.scrum_master.app.Application.method1(int))
  @annotations.MyAnnotation() -> 1
execution(void de.scrum_master.app.Application.method2(String, float))
  @annotations.MyAnnotation() -> 1.0
execution(void de.scrum_master.app.Application.method3(int, List, String))
  @annotations.MyAnnotation() -> [foo, bar]
  @annotations.MyAnnotation() -> foo
execution(void de.scrum_master.app.Application.method4(int, Set, float, boolean))
  @annotations.MyAnnotation() -> [33, 22, 11]
execution(void de.scrum_master.app.Application.method5(boolean, String, String, float, int))
  @annotations.MyAnnotation() -> bar
like image 143
kriegaex Avatar answered Oct 06 '22 19:10

kriegaex