javapostannotationsaspectjjavax

How to ignore parameters with javax annotation when calling joinPoint.getArgs using Aspectj?


I have functions that include different javax Query annotations like : @QueryParam , @Context , @PathParam etc..

is there a way to exclude these parameters when calling joinPoint.getArgs()?

Example:

    @POST
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    @Path("{pathParam}/v1/{pathParam2}/")
    @MyAnnotation
    public Response update(@PathParam("pathParam") String p1, @PathParam("pathParam2") int p2, MyObject x);



    @Before("@annotation(MyAnnotation)")
        public void doSomething(JoinPoint joinPoint){
            Object[] objects = joinPoint.getArgs(); // HERE - is there a way to get only MyObject and not other params..?
    }

The reason I want to do it is that I have several urls, while marking ~10% as persistent. It means that I want the input data to be saved in some persistent service. The Query & Context params are not important to me, but the input data itself is.


Solution

  • Assuming you really use full AspectJ and not Spring AOP like so many others, you should be aware of the fact that in full AspectJ @annotation(XY) potentially matches not just execution() joinpoints but also call(), i.e. your advice will be triggered twice. Even worse, if other places than method executions are also annotated - e.g. classes, fields, constructors, parameters - the pointcut will also match and your try to cast to MethodSignature will cause an exception as a consequence.

    Furthermore, please note that in @AspectJ syntax you need to provide the fully qualified class name of the annotation you want to match against, i.e. don't forget to also prepend the package name. Otherwise there will be no match at all. So before doing anything else you want to change your pointcut to:

    @annotation(de.scrum_master.app.MyAnnotation) && execution(* *(..))
    

    Now here is a fully self-consistent example, an SSCCE producing repeatable results, as requested by me in the comment under your question:

    Annotation:

    package de.scrum_master.app;
    
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyAnnotation {}
    

    Driver application:

    As you can see, the test method has parameters with different types of annotations:

    1. only javax annotation
    2. javax + own annotation
    3. only your own annotation
    4. no annotation

    We want to ignore #1/2 and only print #3/4.

    package de.scrum_master.app;
    
    import javax.ws.rs.PathParam;
    import javax.ws.rs.core.Response;
    
    public class Application {
      public static void main(String[] args) {
        new Application().update("foo", 11, "bar", 22);
      }
    
      @MyAnnotation
      public Response update(
        @PathParam("pathParam") String p1,
        @PathParam("pathParam2") @MyAnnotation int p2,
        @MyAnnotation String text,
        int number
      ) {
        return null;
      }
    }
    

    Aspect:

    Just as user Andre Paschoal started to show in his code fragment, you need to iterate over both the arguments and annotations arrays in order to pull off the filtering trick. I think it is quite ugly and possibly slow just for logging's sake (and I assume this is what you want to do), but for what it is worth, here is your solution:

    package de.scrum_master.aspect;
    
    import java.lang.annotation.Annotation;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.reflect.MethodSignature;
    
    @Aspect
    public class ParameterFilterAspect {
      @Before("@annotation(de.scrum_master.app.MyAnnotation) && execution(* *(..))")
      public void doSomething(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Annotation[][] annotationMatrix = methodSignature.getMethod().getParameterAnnotations();
        for (int i = 0; i < args.length; i++) {
          boolean hasJavaxAnnotation = false;
          for (Annotation annotation : annotationMatrix[i]) {
            if (annotation.annotationType().getPackage().getName().startsWith("javax.")) {
              hasJavaxAnnotation = true;
              break;
            }
          }
          if (!hasJavaxAnnotation)
            System.out.println(args[i]);
        }
      }
    }
    

    Console log:

    bar
    22
    

    Tadaa! :-)