Patterns for Using Custom Annotations
Join the DZone community and get the full member experience.
Join For Freeif you happen to create your own annotations, for instance to use with java 6 pluggable annotation processors, here are some patterns that i collected over time. nothing new, nothing fancy, just putting everything into one place, with some proposed names.
local-name annotation
have your tools accept any annotation as long as its single name (without the fully-qualified prefix) is the expected one. for example com.acme.notnull and net.companyname.notnull would be considered the same. this enables to use your own annotations rather than the one packaged with the tools, in order not to depend on them.
example in the guice documentation :
guice recognizes any @nullable annotation, like edu.umd.cs.findbugs.annotations.nullable or javax.annotation.nullable .
composed annotations
annotations can have annotations as values. this allows for some complex and tree-like configurations, such as mappings from one format to another (from/to xml, json, rdbm).
here is a rather simple example from the hibernate annotations documentation:
@associationoverride(
name="propulsion",
joincolumns = @joincolumn(name="fld_propulsion_fk")
)
multiplicity wrapper
java does not allow to use several times the same annotation on a given target.
to workaround that limitation, you can create a special annotation that expects a collection of values of the desired annotation type. for example, you’d like to apply several times the annotation @advantage , so you create the multiplicity wrapper annotation: @advantages (advantages = {@advantage}) .
typically the multiplicity wrapper is named after the plural form of its enclosed elements.
example in hibernate annotations documentation:
@attributeoverrides( {
@attributeoverride(name="iso2", column = @column(name="borniso2") ),
@attributeoverride(name="name", column = @column(name="borncountryname") )
} )
meta-inheritance
it is not possible in java for annotations to derive from each other. to workaround that, the idea is simply to annotate your new annotation with the “super” annotation, which becomes a meta annotation.
whenever you use your own annotation with a meta-annotation, the tools will actually consider it as if it was the meta-annotation.
this kind of meta-inheritance helps centralize the coupling to the external annotation in one place, while making the semantics of your own annotation more precise and meaningful.
example in spring annotations, with the annotation @component , but also works with annotation @qualifier :
create your own custom stereotype annotation that is itself annotated with @component:
@component
public @interface mycomponent {
string value() default "";
}
@mycomponent
public class myclass...
another example in guice, with the binding annotation :
@bindingannotation
@target({ field, parameter, method })
@retention(runtime)
public @interface paypal {}
// then use it
public class realbillingservice implements billingservice {
@inject
public realbillingservice(@paypal creditcardprocessor processor,
transactionlog transactionlog) {
...
}
refactoring-proof values
prefer values that are robust to refactorings rather than string litterals. myclass.class is better than “com.acme.myclass”, and enums are also encouraged.
example in hibernate annotations documentation:
@manytoone( cascade = {cascadetype.persist, cascadetype.merge}, targetentity=companyimpl.class )
and another example in the guice documentation :
@implementedby(paypalcreditcardprocessor.class)
configuration precedence rule
convention over configuration and sensible defaults are two existing patterns that make a lot of sense with respect to using annotations as part of a configuration strategy. having no need to annotate is way better than having to annotate for little value.
annotations are by nature embedded in the code, hence they are not well-suited for every case of configuration, in particular when it comes to deployment-specific configuration. the solution is of course to mix annotations with other mechanisms and use each of them where they are more appropriate.
the following approach, based on precedence rule, and where each mechanism overrides the previous one, appears to work well:
default value < annotation < xml < programmatic configuration
for example, the default values could be suited for unit testing, while the annotation define all the stable configuration, leaving the other options to configure for deployments at the various stages, like production or qa environments.
this principle is common (spring, java 6 ee among others), for example in jpa:
the concept of configuration by exception is central to the jpa specification.
conclusion
this post is mostly a notepad of various patterns on how to use annotations, for instance when creating tools that process annotations, such as the annotation processing tools in java 5 and the pluggable annotations processors in java 6.
don’t hesitate to contribute better patterns names, additional patterns and other examples of use.
from http://cyrille.martraire.com/2010/07/patterns-for-using-annotations/
Opinions expressed by DZone contributors are their own.
Comments