Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing annotation properties to meta-annotations

Say I have an annotation with a property:

@Named(name = "Steve") private Person person 

and I want to create a compound annotation with several meta-annotations, including the one that takes a property

@Named @AnotherAnnotation @YetAnotherAnnotation public @interface CompoundAnnotation {      ... } 

Is there a way that I can pass properties to the compound annotation to one of the meta annotations?

Eg, something like this:

@CompoundAnnotation(name = "Bob") private Person person; 

that is equivalent to, but much more convenient than

@Named(name = "Bob") @AnotherAnnotation @YetAnotherAnnotation private Person person; 

Thanks!

PS apologies for my poor choice of an example annotation - I didn't have the javax.inject.@Named annotation in mind, just some arbitrary annotation that has properties.


Thank you everyone for your answers/comments.

It definitely seems to be the case that this is not possible. However, it just happens that there is a simple work-around for my case-in-point, which I will share in case it helps anyone:

I am working with Spring and want to create my own Annotations that have @Component as a meta-annotation, thus being autodetected by component scanning. However, I also wanted to be able to set the BeanName property (corresponding to the value property in @Component) so I could have custom bean names.

Well it turns out that the thoughtful guys at Spring made it possible to do just that - the AnnotationBeanNameGenerator will take the 'value' property of whatever annotation it is passed and use that as the bean name (and of course, by default, it will only get passed annotations that are @Component or have @Component as a meta-annotation). In retrospect this should have been obvious to me from the start - this is how existing annotations with @Component as a meta-annotation, such as @Service and @Registry, can provide bean names.

Hope that is useful to someone. I still think it's a shame that this is not possible more generally though!

like image 305
Tom McIntyre Avatar asked Apr 10 '12 11:04

Tom McIntyre


People also ask

What is @target annotation in Java?

If an @Target meta-annotation is present, the compiler will enforce the usage restrictions indicated by ElementType enum constants, in line with JLS 9.7. 4. For example, this @Target meta-annotation indicates that the declared type is itself a meta-annotation type.

Can annotation be inherited?

Annotations, just like methods or fields, can be inherited between class hierarchies. If an annotation declaration is marked with @Inherited , then a class that extends another class with this annotation can inherit it.

Is @target a meta annotation?

@Target annotation is a meta-annotation, i.e., it can only be used to annotate other annotations.

Is @override is meta annotation?

The @Override annotation is a type of marker annotation that is used above a method to indicate to the Java compiler that the subclass method is overriding the superclass method.


2 Answers

It is a few years later now, and since you are using Spring, what you are asking for is sort of possible now using the @AliasFor annotation.

For example:

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @SpringApplicationConfiguration @ActiveProfiles("test") public @interface SpringContextTest {      @AliasFor(annotation = SpringApplicationConfiguration.class, attribute = "classes")     Class<?>[] value() default {};      @AliasFor("value")     Class<?>[] classes() default {}; } 

Now you can annotate your test with @SpringContextTest(MyConfig.class), and the amazing thing is that it actually works the way you would expect.

N.B. When you need to programmatically get the attribute values, the Spring automagical aliasing works only when you use AnnotatedElementUtils instead of AnnotationUtils, as the documentation says:

AnnotatedElementUtils defines the public API for Spring's meta-annotation programming model with support for annotation attribute overrides. If you do not need support for annotation attribute overrides, consider using AnnotationUtils instead.

Example:

final Named namedAnnotation = AnnotatedElementUtils.findMergedAnnotation(Person.class, Named.class); final String name = namedAnnotation.name(); assertEquals("Steve", name); 
like image 138
Maarten Brak Avatar answered Sep 27 '22 00:09

Maarten Brak


Is there a way that I can pass properties to the compound annotation to one of the meta annotations?

I think the simple answer is "no". There is no way to ask Person what annotations it has on it and get @Named for example.

The more complex answer is that you can chain annotations but you would have to investigate these annotations via reflection. For example, the following works:

@Bar public class Foo {     public static void main(String[] args) {         Annotation[] fooAnnotations = Foo.class.getAnnotations();         assertEquals(1, fooAnnotations.length);         for (Annotation annotation : fooAnnotations) {             Annotation[] annotations =                 annotation.annotationType().getAnnotations();             assertEquals(2, annotations.length);             assertEquals(Baz.class, annotations[0].annotationType());         }     }      @Baz     @Retention(RetentionPolicy.RUNTIME)     public @interface Bar {     }      @Retention(RetentionPolicy.RUNTIME)     public @interface Baz {     } } 

However the following statement will return null:

// this always returns null Baz baz = Foo.class.getAnnotation(Baz.class) 

This means that any 3rd party class that is looking for the @Baz annotation won't see it.

like image 30
Gray Avatar answered Sep 25 '22 00:09

Gray