I need to create an aspect with a pointcut matching a method if:
The ms.getParameterNames() call in the above solution doesnt seem to work when the method is implemented from an interface. I get back nulls.
However, if I enable CGLIB, then it works.
<aop:aspectj-autoproxy proxy-target-class="true"/>
Updated:
OK, the best reference I could find is on this page: Annotations, Pointcuts and Advice.
You can match the method, however you won't be able to catch the parameter (just the method and the annotation). So what you will have to do is a combination of pointcut matching and reflection. Something like this:
@Pointcut(
"execution(@com.xxx.xxx.annotation.MyAnnotationForMethod * *(.., @com.xxx.xxx.annotation.MyAnnotationForParam (*), ..))")
public void annotatedMethod(){}
@Before("annotatedMethod()")
public void doStuffOnParam(final JoinPoint jp){
final Signature signature = jp.getSignature();
if(signature instanceof MethodSignature){
final MethodSignature ms = (MethodSignature) signature;
final Method method = ms.getMethod();
final String[] parameterNames = ms.getParameterNames();
final Class<?>[] parameterTypes = ms.getParameterTypes();
final Annotation[][] parameterAnnotations =
method.getParameterAnnotations();
for(int i = 0; i < parameterAnnotations.length; i++){
final Annotation[] annotations = parameterAnnotations[i];
final MyAnnotationForParam paramAnnotation =
getAnnotationByType(annotations, MyAnnotationForParam.class);
if(paramAnnotation != null){
this.processParameter(ms.toShortString(),
parameterNames[i],
parameterTypes[i],
paramAnnotation);
}
}
}
}
/**
* In an array of annotations, find the annotation of the specified type, if any.
* @return the annotation if available, or null
*/
@SuppressWarnings("unchecked")
private static <T extends Annotation> T getAnnotationByType(final Annotation[] annotations,
final Class<T> clazz){
T result = null;
for(final Annotation annotation : annotations){
if(clazz.isAssignableFrom(annotation.getClass())){
result = (T) annotation;
break;
}
}
return result;
}
/**
* Do some processing based on what we found.
* @param signature method signature
* @param paramName parameter name
* @param paramType parameter type
* @param paramAnnotation annotation we found
*/
private void processParameter(final String signature,
final String paramName,
final Class<?> paramType,
final MyAnnotationForParam paramAnnotation){
System.out.println(MessageFormat.format(
"Found parameter ''{0}'' \n of type ''{1}'' \n with annotation ''{2}'' \n in method ''{3}''",
paramName,
paramType,
paramAnnotation,
signature));
}
Here is my test class for the above aspect:
public class TestClass{
@MyAnnotationForMethod
public void simpleTestMethod(@MyAnnotationForParam final String param1){
System.out.println("Method body (simple)");
};
@MyAnnotationForMethod
public void complexTestMethod(final String param1,
@MyAnnotationForParam final Float param2,
@MyAnnotationForParam final Boolean param3){
System.out.println("Method body (complex)");
};
public static void main(final String[] args){
System.out.println("Starting up");
final TestClass testObject = new TestClass();
testObject.simpleTestMethod("Hey");
testObject.complexTestMethod("Hey", 123.4f, false);
System.out.println("Finished");
}
}
and here is the output:
Starting up
Found parameter 'param1'
of type 'class java.lang.String'
with annotation '@com.xxx.xxx.annotation.MyAnnotationForParam()'
in method 'TestClass.simpleTestMethod(..)'
Method body (simple)
Found parameter 'param2'
of type 'class java.lang.Float'
with annotation '@com.xxx.xxx.annotation.MyAnnotationForParam()'
in method 'TestClass.complexTestMethod(..)'
Found parameter 'param3'
of type 'class java.lang.Boolean'
with annotation '@com.xxx.xxx.annotation.MyAnnotationForParam()'
in method 'TestClass.complexTestMethod(..)'
Method body (complex)
Finished
You will probably want to cache a lot of this, there is no need to parse every parameter of every annotation in every execution. Keep a map of which parameter of which method carries the annotation and process only those parameters.