Как игнорировать параметры с аннотацией javax при вызове joinPoint.getArgs с использованием Aspectj?

У меня есть функции, которые включают в себя различные аннотации javax Query, такие как: @QueryParam, @Context, @PathParam так далее..

Есть ли способ исключить эти параметры при вызове joinPoint.getArgs()?

Пример:

    @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..?
    }

Причина, по которой я хочу это сделать, заключается в том, что у меня есть несколько URL, при этом отмечая ~10% как постоянные. Это означает, что я хочу, чтобы входные данные были сохранены в каком-то постоянном сервисе. Параметры Query & Context не важны для меня, но сами входные данные.

2 ответа

Решение

Предполагая, что вы действительно используете полный AspectJ, а не Spring AOP, как многие другие, вы должны знать, что в полном AspectJ @annotation(XY) потенциально совпадает не только execution() точки соединения, но также call()ваш совет будет вызван дважды. Еще хуже, если другие места, кроме выполнения методов, также аннотированы - например, классы, поля, конструкторы, параметры - pointcut также будет совпадать, и ваша попытка привести к MethodSignature будет вызывать исключение как следствие.

Кроме того, обратите внимание, что в синтаксисе @AspectJ необходимо указать полное имя класса аннотации, с которой вы хотите сопоставить, то есть не забудьте также добавить имя пакета. Иначе не будет никакого совпадения. Поэтому, прежде чем делать что-то еще, вы хотите изменить свой pointcut на:

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

Теперь вот полностью самосогласованный пример, SSCCE, дающий воспроизводимые результаты, как я и просил в комментарии под вашим вопросом:

Аннотация:

package de.scrum_master.app;

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

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

Приложение для водителя:

Как видите, метод теста имеет параметры с различными типами аннотаций:

  1. только аннотация javax
  2. Javax + собственная аннотация
  3. только ваша собственная аннотация
  4. без аннотации

Мы хотим игнорировать #1/2 и печатать только #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;
  }
}

аспект:

Так же, как пользователь Andre Paschoal начал показывать в своем фрагменте кода, вам нужно перебрать и аргументы, и массивы аннотаций, чтобы осуществить трюк с фильтрацией. Я думаю, что это довольно некрасиво и, возможно, медленно только ради регистрации (и я предполагаю, что это то, что вы хотите сделать), но для чего оно стоит, вот ваше решение:

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]);
    }
  }
}

Консольный журнал:

bar
22

Tadaa!:-)

Я не думаю, что есть волшебный способ сделать это, поэтому перейдите к очевидному:

  • определить критерии принятия аргумента;
  • перебираем аргументы и фильтруем на основе ранее определенных критериев и все.

Кажется, что ваш критерий принятия заключается в том, что arg не аннотирован этими аннотациями javax, верно?

Попробуй это:

Object[] args = joinPoint.getArgs();
Annotation[][] anns = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameterAnnotations();

for (int i = 0; i < args.length; i++) {
    for (int j = 0; j < args[i].length; j++) {
        // check here for the annotations you would like to exclude
    }
}

Этот фрагмент кода работает для меня:

Annotation[][] anns = ((MethodSignature)   thisJoinPoint.getSignature()).getMethod().getParameterAnnotations();

parameterValues = thisJoinPoint.getArgs();
signature = (MethodSignature) thisJoinPoint.getSignature();
parameterNames = signature.getParameterNames();
if (parameterValues != null) {
    for (int i = 0; i < parameterValues.length; i++) {

        boolean shouldBeExcluded = false;
        for (Annotation annotation : anns[i]) {
            if (annotation instanceof ExcludeFromCustomLogging) {//<<---------ExcludeFromCustomLogging is my class
                shouldBeExcluded = true;
                break;
            }
        }
        if (shouldBeExcluded) {
            //System.out.println("should be excluded===>"+parameterNames[i]);
            continue;
        }

  //.......and your business

}
Другие вопросы по тегам