Issue
I am defining a custom annotation in a custom library
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Configuration
@ComponentScan(
basePackages = {
"com.***",
....
})
public @interface EnableFoo {
String[] urls();
}
the client (which has the library as dependency) would call it like this
@EnableFoo(
urls = { "some-url", ... })
I want to get the parameter urls
of my annotation so I use this code
private List<String> initFoo() {
var beans = ctx.getBeansWithAnnotation(EnableFoo.class);
if (beans.size() != 1) {
throw new IllegalStateException(
"Multiple '@" + EnableFoo.class.getName() + "' declarations !");
}
Object bean = beans.values().iterator().next();
Class<?> clazz = bean.getClass();
if (clazz.isAnnotationPresent(EnableFoo.class)) { // <--- return false !!
String[] annotationUrls = clazz.getAnnotation(EnableFoo.class).urls();
....
}
}
the problem is that isAnnotationPresent
return false even though just before there ctx.getBeansWithAnnotation(EnableFoo.class)
Can somebody explain me this weird behavior and if it is the right approach
Edit 1
I update my code to use AnnotationUtils
but the annotation is always null
var annotation =
AnnotationUtils.getAnnotation(
AopProxyUtils.ultimateTargetClass(bean),
EnableFoo.class);
and here is my object bean
at debug
Answer
thanks to @M. Deinum comment the solution was to use findAnnotation
instead of getAnnotation
var annotation =
AnnotationUtils.findAnnotation(
bean.getClass(), EnableFoo.class)
Solution
private List<String> initFoo() {
var beans = ctx.getBeansWithAnnotation(EnableFoo.class);
if (beans.size() != 1) {
throw new IllegalStateException(
"Multiple '@" + EnableFoo.class.getName() + "' declarations !");
}
Object bean = beans.values().iterator().next();
Class<?> clazz = bean.getClass();
if (clazz.isAnnotationPresent(EnableFoo.class)) { // <--- return false !!
String[] annotationUrls = clazz.getAnnotation(EnableFoo.class).urls();
....
}
}
The problem with this is that bean.getClass
will return not the actual class but a proxy. Depending on the type of proxy and type of annotation it might or might not be detected.
Spring itself ships with some nice utility classes which know about proxies and which can help to obtain annotations (or the metadata). One of those is AnnotationUtils
. Use this to obtain the annotation from the class. To do this you will need the findAnnotation
as that will traverse the hierarchy of the class to find the annotation.
private List<String> initFoo() {
var beans = ctx.getBeansWithAnnotation(EnableFoo.class);
if (beans.size() != 1) {
throw new IllegalStateException(
"Multiple '@" + EnableFoo.class.getName() + "' declarations !");
}
Object bean = beans.values().iterator().next();
Class<?> clazz = bean.getClass();
String[] annotationUrls = AnnotationUtils.findAnnotation(clazz, EnableFoo.class).urls();
....
}
This should find the annotation and return the values you want.
Answered By - M. Deinum
Answer Checked By - Terry (JavaFixing Volunteer)