The most basic concept is: annotations are metadata.
It is additional data that you relate to classes, methods, attributes, parameters and variables.
Data can be used in time of compilation and implementation, as defined in the annotation.
Preprocessing during the build
At compile time, annotations can serve, for example, to generate additional code or even to modify bytecode class.
Compile time annotations are removed in bytecode and are not available at runtime.
Let’s look at some ideas about what you can do with this.
Code generators and manipulators
For example, Hibernate can add methods Wrappers to getters class originals to be able to load the data in mode Lazy. Did I speak Greek? It means that if you have the entities Cliente
and Conta
and calls a method cliente.getConta()
, in fact will call a substitute method generated by Hibernate which, in case the account has not already been read from the database, will make a SELECT
and create the object conta
with the respective data, then returning the loaded value.
Another example of preprocessing is the design Lombok. It is able to generate the most common routines used in Pojos, such as getters, setters, toString() and hashcode(). See an example, see what the annotation @Data
is able to:
@Data(staticConstructor="of")
public class Company {
private final Person founder;
private String name;
private List<Person> employees;
}
The above code is equivalent to the following:
public class Company {
private final Person founder;
private String name;
private List<Person> employees;
private Company(final Person founder) {
this.founder = founder;
}
public static Company of(final Person founder) {
return new Company(founder);
}
public Person getFounder() {
return founder;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public List<Person> getEmployees() {
return employees;
}
public void setEmployees(final List<Person> employees) {
this.employees = employees;
}
@java.lang.Override
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
if (o == null) return false;
if (o.getClass() != this.getClass()) return false;
final Company other = (Company)o;
if (this.founder == null ? other.founder != null : !this.founder.equals(other.founder)) return false;
if (this.name == null ? other.name != null : !this.name.equals(other.name)) return false;
if (this.employees == null ? other.employees != null : !this.employees.equals(other.employees)) return false;
return true;
}
@java.lang.Override
public int hashCode() {
final int PRIME = 31;
int result = 1;
result = result * PRIME + (this.founder == null ? 0 : this.founder.hashCode());
result = result * PRIME + (this.name == null ? 0 : this.name.hashCode());
result = result * PRIME + (this.employees == null ? 0 : this.employees.hashCode());
return result;
}
@java.lang.Override
public java.lang.String toString() {
return "Company(founder=" + founder + ", name=" + name + ", employees=" + employees + ")";
}
}
Developer help
In addition, annotations can be used for certain validation types in the build. Take for example the annotation @Override
. It defines that a subclass method is overriding a superclass method. Apparently that doesn’t help anything... until someone changes the method signature in the superclass. If someone changes the method in the superclass their subclass method would become a different method. In this case, the compiler will warn that your method with @Override
is not overwriting anything else! This helps a lot to detect problems, for example when updating a library.
The sky is the limit
Other tools generate annotation-based configuration files to improve runtime performance.
Anyway, you can even make your annotation processor to run in the compilation of a program and generate whatever you want, just use the API APT (Annotation Processing Tool), available from Java. See a article on the subject (in English).
Inspection at execution time
Already annotations that are kept at runtime can be used by libraries and frameworks to identify the elements and perform additional annotations.
Auto-setup
In the case of Spring, for example, you should have seen that you can annotate the class as follows:
@Component
public class MessagePrinter { ... }
What is the magic here? When Spring is started through the Listener in his web.xml
, it will read all classes within the configured package(s) (s) and add to the list of managed components whenever it finds an annotation like this.
Then you no longer need those gigantic Xmls that needed updating whenever you changed a package class or renamed it.
For more details about Spring, read my article What annotations to use for Spring 3 components.
Injection of Dependencies
Also, a note like @Autowired
allows you to specify to Spring that that attribute must be filled with some component of that type. Example:
@Autowired ClienteDAO clienteDAO;
Done. No XML, no freshness. If there is a class ClienteDAO
with a Spring annotation, it will automatically create an instance of it and assign there!
Automatic formatting
Many frameworks do the magic of automatically sending and recovering data from HTML forms, databases, files, etc. This is pretty cool, but how to specify the format of the data? Remembering that, for example, a date attribute may have different representations in the same data set.
For example, in JPA it is possible to specify whether the field with a Date
should save only the date or time as well:
@Temporal(TemporalType.DATE)
@Column(name="CAMPO_DATA")
public Date getData()
Without it, you would have to write code or configuration in a file.
In Spring, you can use the annotation @DateTimeFormat
to define what the date format is when it is converted to and from a String. So Spring can automatically receive the submitted data from an HTML form and convert it properly to the class types.
To infinity and beyond
Again, the limit on the use of runtime annotations is proportional to the developer’s creativity.
Considerations
The greatest care that every developer needs in my experience is to pay attention to the fact that the annotations are "static". This means that if a given annotated element can have a function at one time and another function at another time, it may be the case to use an external configuration.
Just to quote an example, if you used annotations to configure Dependency Injection with @Autorired
and then decided to change the injected object by a subclass, will need to recompile the code by adding a @Qualifier
. With the XML configuration, you wouldn’t need it. So, there are some cases where XML configuration gives greater flexibility for managing the application outside the development environment.
Anyway, annotations are an extremely powerful and flexible feature of the Java platform. They are metadata that can be read by frameworks and libraries rather than configuration via XML and imperative programming.
I think a fundamental property of Annotations is to function as configuration providers (as well as XML files) but whose data can be validated at compile time, which makes them more error-resistant.
– Piovezan
I like to define Annotations as semantic reviews available in Runtime.
– Maniero