Message-ID: <95511020.1539.1432452450165.JavaMail.firstname.lastname@example.org> Subject: Exported From Confluence MIME-Version: 1.0 Content-Type: multipart/related; boundary="----=_Part_1538_1676732044.1432452450145" ------=_Part_1538_1676732044.1432452450145 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Content-Location: file:///C:/exported.html
Meta-annotations, also known as annotation aliases are annotations that = are replaced at compile time by other annotations (one meta-annotation is a= n alias for one or more annotations). This feature was added in Groovy 2.1.= 0. It can be used to dramatically reduce the verbosity of code involving mu= ltiple annotations.
Let's start with a simple example. Imagine you have the @Servic= e and @Transactional annotations and that you want to an= notate a class with both:
What meta-annotations allows you is to replace the two annotations by a = single one. First of all, you need to define your alias:
We are defining a @TransactionalService annotation here, a= meta-annotation, that collects the @Service an= d @Transactional annotations. This is done by annotating the = interface with @AnnotationCollector.
Before going further, we have to understand how meta-annotations work. F= irst of all, Groovy supports both precompiled and source form meta-annotati= ons. This means that your meta-annotation may be precompiled,= or you can have it in the same source tree as the one you are currently co= mpiling. Second, this is a Groovy feature only. There is no chance for you = to annotate a Java class with a meta-annotation and hope it will do the sam= e as in Groovy. Likewise, you cannot write a meta-annotation in Java: both = the meta-annotation definition and usage have to be G= roovy code.
When the Groovy compiler encounters a class annotated with a meta-annota= tion, it replaces it with the collected annotations. = That is, in our previous example, that it will replace @Transactio= nalService with @Transactional and @Service. This happens during the semantic analysis phase, that is to say exactly = when AST transformations are collected.
More than replacing the alias, a meta-annotation is capable of processin= g the collected annotations, including processing arguments. The default pr= ocessor includes interesting behaviour with regards to annotation parameter= s.
Here, we will imagine two annotations, each of them taking one argument:=
And that you want create a meta-annotation named @Explosive:
Then, by default, when the annotations are replaced, they will get the v= alues as they were defined in the alias. More interesting, the alias suppor= ts overriding specific values:
Here, the value provided as a parameter to @Explosive over= rides the one defined in the annotation.
What happens if two annotations define the same parameter name? The defa= ult processor will copy the annotation value to all annotations that accept= this parameter. Take this example (you can run it in a Groovy console, for= example):
this would print:
which means that in the second case, the annotation value was copied in = both annotations. But what happens if, for example, the annotation type for= Foo is String, and the one for Bar is an integer? The current behaviour of= the annotation is to fail at compile time. Does it mean that there's nothi= ng more you can do? No. Meta-annotations support a special feature called a= processor, that will help you deal with such problems.
A custom annotation processor will let you choose how to explode a meta-= annotation. The behaviour of the meta-annotation is, in this case, totally = up to you. You can do whatever you want. Let's take a look, for example, on= how @CompileDynamic is implemented in Groovy 2.1.0. @CompileDynamic is a meta-annotation that replaces itself with <= em>@CompileStatic(TypeCheckingMode.SKIP). The problem is that the defa= ult meta annotation processor doesn't support enums and we need to produce = that enum. So, instead of defining @CompileDynamic like this:=
We will define it like this:
The first thing you may notice is that our interface is no longer annota= ted with @CompileStatic. The reason for this is that we rely = on the extra processor parameter, that references a class whi= ch will generate the annotation. And here is what thi= s class looks like:
Our custom processor extends the default one and overrides the = public List<AnnotationNode> visit(AnnotationNode collector, Annotatio= nNode aliasAnnotationUsage, AnnotatedNode aliasAnnotated, SourceUnit source= ) method. This method is responsible for returning the list of annotat= ions that will be added to the AST tree in place of the alias. The body of = this method, hence, is only there to generate the appropriate Anno= tationNode, nothing more! Of course, you can rely on the superclass to= deal with arguments, for example.