Как игнорировать параметры с аннотацией 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 {}
Приложение для водителя:
Как видите, метод теста имеет параметры с различными типами аннотаций:
- только аннотация javax
- Javax + собственная аннотация
- только ваша собственная аннотация
- без аннотации
Мы хотим игнорировать #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
}