Some months ago I attended a presentation at which
Wilfred Springer demonstrated his very cool
Preon binary codec library. Defining binary file formats in Preon requires quite a lot of fairly repetitive sets of annotations, and during a chat after the talk Wilfred mentioned how much more convenient it would be if one could just define "shortcuts":
@RequiredEnumProperty(column = "AGENT")
for
@NotNull
@Column(name = "AGENT")
@Enumerated(EnumType.STRING)
for instance - and use those instead. Sort-of "macro annotatations" for Java, if you like.
A thought that has presumably also occurred to many frequent users of Hibernate, JAXB or other annotation-heavy frameworks.
Hence @Composite.
Just to dispel any misconceptions up front: here be no bytecode weaving or other runtime magic, so @Composite does not affect the semantics of the "regular" AnnotatedElement methods. Composite annotations are instead supported via an AnnotatedElements interface, which provides all the familiar annotation-related methods, and "unpacks" registered composite annotations to their "leaf" types.
So @Composite is not (yet) drop-in magic - you will need to explicitly call the AnnotatedElements interface from your code.
The AtCompositeDemo class included in the project basically looks like this:
public class AtCompositeDemo {
...
// define a composite annotation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
@CompositeAnnotation
public @interface TargetRetentionLeafCompositeAnnotation {
boolean runtimeRetention() default false;
@LeafAnnotation
Target targetLeafAnnotation() default @Target({ ElementType.METHOD });
@LeafAnnotation(factoryClass = RetentionLeafAnnotationFactory.class)
Retention retentionLeafAnnotation() default @Retention(RetentionPolicy.RUNTIME);
}
// apply the composite annotation...
@Resource
@TargetRetentionLeafCompositeAnnotation(runtimeRetention = true)
private static @interface AnnotatedAnnotation {}
// ...to two targets
@Resource
@TargetRetentionLeafCompositeAnnotation(runtimeRetention = false)
private static @interface OtherAnnotatedAnnotation {}
public static void main(String[] args) {
// get a configured instance of the AnnotatedElements interface
AnnotatedElements annotatedElements = ...
log.info("Retrieving annotations from AnnotatedAnnotation.class");
log.info(Arrays.toString(annotatedElements.getAnnotations(AnnotatedAnnotation.class)));
log.info("Retrieving annotations from OtherAnnotatedAnnotation.class");
log.info(Arrays.toString(annotatedElements.getAnnotations(OtherAnnotatedAnnotation.class)));
}
}
When run, it should produce output similar to the following
Retrieving annotations from AnnotatedAnnotation.class
[@javax.annotation.Resource(shareable=true, mappedName=, description=, name=,
type=class java.lang.Object, authenticationType=CONTAINER), @java.lang.annotation.Retention(
value=RUNTIME), @java.lang.annotation.Target(value=[METHOD])]
Retrieving annotations from OtherAnnotatedAnnotation.class
[@javax.annotation.Resource(shareable=true, mappedName=, description=, name=,
type=class java.lang.Object, authenticationType=CONTAINER), @java.lang.annotation.Retention(
value=CLASS), @java.lang.annotation.Target(value=[METHOD])]
which demonstrates the key features of @Composite: namely, that the composite annotation is correctly "expanded" into a @Target and a @Retention annotation, and the "regular" @Resource annotation is still picked up.
More details can be found in the following
blog post. The code itself is available at
Google Code.