问题
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.
回答1:
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:
- only javax annotation
- javax + own annotation
- only your own annotation
- 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! :-)
回答2:
I don't think there's a magic way to do this, so go with the obvious:
- define your argument acceptance criteria;
- iterate over the args and filter based on the previously defined criteria and that's it.
It seems that your acceptance criteria is that the arg is not annotated with these javax annotations, right ?
Try this:
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
}
}
回答3:
this snippet code works for me:
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
}
来源:https://stackoverflow.com/questions/42537222/how-to-ignore-parameters-with-javax-annotation-when-calling-joinpoint-getargs-us