OBSOLETE: move to https://github.com/t1/power-annotations
About
Helps libraries with using annotations, supporting defaulting (e.g. from class to field) and stereotypes, so their users can encapsulate the libraries' annotations into their own, allowing to abstract and reuse them.
Similar to David Blevins' MetaTypes but this one can propagate stereotype properties and more. And this one uses the standard CDI @Stereotype
annotation and assumes that you have can change the annotations you want to check (making things much easier).
The main entry point is the Annotations class.
Tutorial
Setup
Add a dependency:
<dependency>
<groupId>com.github.t1</groupId>
<artifactId>stereotype-helper</artifactId>
<version>${latestVersion}</version>
</dependency>
Where latestVersion
is (you can also download it from there).
The Basics: Reflecting on Annotations
Say you have an annotated field:
class Target {
@FieldAnnotation
public String field;
}
Without stereotype-helper
, your code to check the presence of the annotation would look like this:
Field field = Target.class.getDeclaredField("field");
boolean present = field.isAnnotationPresent(FieldAnnotation.class);
With stereotype-helper
, you'd use the Annotations
class, instead:
Field field = Target.class.getDeclaredField("field");
boolean present = Annotations.on(field).isAnnotationPresent(FieldAnnotation.class);
There's a shortcut for those two lines:
boolean present = Annotations.onField(Target.class, "field")
.isAnnotationPresent(FieldAnnotation.class);
Annotations
has corresponding methods for methods and classes. They all return the standard Java java.lang.reflect.AnnotatedElement
objects, so the syntax of using them stays exactly the same, only the entry point is different.
No big deal... so far.
Resolving Stereotypes
Stereotypes are a part of CDI to group annotations into reusable roles. But they mostly work only for CDI and Java EE annotations, as only few other frameworks know how to resolve stereotypes to the annotations they group. stereotype-helper
does this for your framework (or library or wherever you want to react to the annotations)... as you tell from it's name.
First let's define a stereotype:
@Transactional
@MyAnnotation
@Stereotype
@Target(Method)
@Retention(RUNTIME)
public @interface Action {}
@Transactional
is a Java EE annotation; @MethodAnnotation
is the annotation we'd like to handle in our own framework. The other annotations define it as a stereotype.
When you use this stereotype like this:
class Type {
@Action
public void myMethod() {
...
}
}
... then this should be true:
Annotations.onMethod(Type.class, "myMethod").isAnnotationPresent(MyAnnotation.class);
I.e. the annotations on the stereotype show up as if they where directly annotated at the target (method in this case).
Note that this resolution is recursive, i.e. it works also for stereotypes that are annotated with other stereotypes.
Stereotype Property Propagation
Say you have an annotation with a property like this:
public @interface Color {
public String value() default "none";
}
... and a stereotype like this:
@Color("red")
@Stereotype
public @interface Red {
public String value();
}
... then calling ...getAnnotation(Color.class).value()
on something annotated as @Red
will return red
(which is generally not the case for stereotypes in CDI). Even more important, an annotation @Red("strawberry")
would turn out as strawberry
, i.e. the property is propagated from the stereotype to the annotation. One stereotype property can also be propagated to multiple annotations.
For propagation to work, the name as well as the type of the property must match. Alternatively you can explicitly configure it with PropagetTo
, specifying the target types and property names.
Defaulting Annotations
Annotations on a class are effective for all methods and fields within that class, but can be overriden.
Annotations on a package are effective for all classes, methods, and fields within that package, but can be overriden.
Annotation Inheritance From Interfaces
In standard Java, classes inherit the annotations of their super class if the annotation itself is annotated as @Inherited
. stereotype-helper
does that for all interfaces implemented, too.