Consider this code:
public example(String s, int i, @Foo Bar bar) {
/* ... */
}
I want to check if the method has an annotation @Foo
and get the argument or throw an exception if no @Foo
annotation is found.
My current approach is to first get the current method and then iterate through the parameter annotations:
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
class Util {
private Method getCurrentMethod() {
try {
final StackTraceElement[] stes = Thread.currentThread().getStackTrace();
final StackTraceElement ste = stes[stes.length - 1];
final String methodName = ste.getMethodName();
final String className = ste.getClassName();
final Class<?> currentClass = Class.forName(className);
return currentClass.getDeclaredMethod(methodName);
} catch (Exception cause) {
throw new UnsupportedOperationException(cause);
}
}
private Object getArgumentFromMethodWithAnnotation(Method method, Class<?> annotation) {
final Annotation[][] paramAnnotations = method.getParameterAnnotations();
for (Annotation[] annotations : paramAnnotations) {
for (Annotation an : annotations) {
/* ... */
}
}
}
}
Is this the right approach or is there a better one?
How would the code inside the forach loop look like? I'm not sure if I have understood the what getParameterAnnotations
actually returns...
The outer for loop
for (Annotation[] annotations : paramAnnotations) {
...
}
should use an explicit counter, otherwise you don't know what parameter you are processing right now
final Annotation[][] paramAnnotations = method.getParameterAnnotations();
final Class[] paramTypes = method.getParameterTypes();
for (int i = 0; i < paramAnnotations.length; i++) {
for (Annotation a: paramAnnotations[i]) {
if (a instanceof Foo) {
System.out.println(String.format("parameter %d with type %s is annotated with @Foo", i, paramTypes[i]);
}
}
}
Also make sure your annotation type is annotated with @Retention(RetentionPolicy.RUNTIME)
From your question it is not entirely clear what you are trying to do. We agree on the difference of formal parameters vs. actual arguments:
void foo(int x) { }
{ foo(3); }
where x
is a parameter and 3
is an argument?
It is not possible to get the arguments of methods via reflection. If it is possible at all, you would have to use the sun.unsafe
package. I can't tell you much about that though.
getParameterAnnotations
returns an array with the length equals to the amount of method parameters. Each element in that array contains an array of annotations on that parameter.
So getParameterAnnotations()[2][0]
contains the first ([0]
) annotation of the third ([2]
) parameter.
If you only need to check if at least one parameter contains an annotation of a specific type, the method could look like this:
private boolean isAnyParameterAnnotated(Method method, Class<?> annotationType) {
final Annotation[][] paramAnnotations = method.getParameterAnnotations();
for (Annotation[] annotations : paramAnnotations) {
for (Annotation an : annotations) {
if(an.annotationType().equals(annotationType)) {
return true;
}
}
}
return false;
}
If you're looking for annotations on the method, you probably want method.getAnnotations()
or method.getDeclaredAnnotations()
.
The method.getParameterAnnotations()
call gives you annotations on the method's formal parameters, not on the method itself.
Looking back at the question title, I suspect you are looking for annotations on the parameters, which I didn't read in the content of the question. If that's the case, your code looks fine.
See Method Javadoc and AnnotatedElement Javadoc.