1. What is annotation
One word can describe annotations, that is metadata, that is, data that describes data. Therefore, it can be said that the annotation is the metadata of the source code. For example, the following code:
@Override public String toString() { return "This is String Representation of current object."; }
In the above code, I rewrite the toString() method and use the @Override annotation. However, even if I don't use @Override annotation to mark the code, the program can execute normally. So, what does the annotation mean? Is there any benefit to writing this way? In fact, @Override tells the compiler that this method is a rewrite method (metadata describing the method). If the method does not exist in the parent class, the compiler will report an error, prompting that the method has not rewrite the method in the parent class. If I accidentally spell the wrong typo, for example, write toString() as toString(){double r}, and I don't use the @Override annotation, the program can still be compiled and run. But the results of the operation will be very different from what I expected. Now we understand what annotations are, and using annotations helps read the program.
Annotation is a special modifier applied to classes, methods, parameters, variables, constructors and package declarations. It is a tool selected by the JSR-175 standard to describe metadata.
2. Why use annotations
Before (or even after use), XML is widely used to describe metadata. Some application developers and architects have begun to find that the maintenance of XML is getting worse. They want to use something tightly coupled to the code, rather than a code description that is loosely coupled to the code (and in some cases even completely separate) like XML.
If you want to set a lot of constants or parameters for your application, XML is a good choice in this case because it will not be connected to specific code. If you want to declare a method as a service, it would be better to use Annotation because in this case, annotations and methods need to be tightly coupled, and developers must recognize this.
Another important factor is that Annotation defines a standard way of describing metadata. Before this, developers usually define metadata in their own way. For example, use tagged interfaces, annotations, transient keywords, and so on. Each programmer defines metadata in his own way, unlike Annotation's standard way.
3. Basic syntax
Writing Annotation is very simple, you can compare the definition of Annotation with the definition of interface. Let's look at two examples: one is the standard annotation @Override, and the other is the user-defined annotation @Todo.
@Target() @Retention() public @interface Override { }
You may have some questions about the @Override annotation, it does nothing, so how does it check that there is a function with the same name in the parent class? Of course, don't be surprised, I'm just teasing you. The definition of @Override annotation is not just that little code. This part is very important, I have to repeat again: Annotations is just metadata and has nothing to do with business logic. It's a bit difficult to understand, but that's it. If Annotations does not contain business logic, someone must implement these logic. Metadata users do this. Annotations only provides information on the properties it defines (class/method/package/domain). Annotations' users (again, some code) read this information and implement the necessary logic.
When we use Java's annotations (such as @Override), the JVM is a user, which works at the bytecode level. At this point, application developers cannot control or use custom annotations. So, let's explain how to write custom Annotations.
Let's talk about the key points of writing custom Annotations one by one. In the example above, you see some annotations applied to the annotations.
3.1 Four basic meta annotations
The J2SE5.0 version provides four kinds of meta annotations, specifically annotating other annotations:
@Documented – Whether the annotation will be included in JavaDoc
@Retention – When to use this annotation
@Target? – Where does the annotation be used
@Inherited – Whether subclasses are allowed to inherit this annotation
@Documented – A simple Annotations tag annotation indicating whether annotation information is added to the java document.
@Retention – defines the life cycle of this annotation.
– Discarded during compilation phase. These annotations no longer have any meaning after compilation, so they do not write bytecode. @Override, @SuppressWarnings all belong to this type of annotation.
– Discard when the class is loaded. Useful in processing bytecode files. This is the default method for annotations.
– The annotation is never discarded, and the annotation is retained during the runtime, so the information of the annotation can be read using the reflection mechanism. Our custom annotations usually use this method.
@Target – Indicates where the annotation is used. If not specified, the annotation can be placed anywhere. Here are some of the available parameters. It should be noted that the annotation of attributes is compatible. If you want to add annotation to all 7 attributes and only exclude one attribute, then you need to define the target to include all attributes.
: Used to describe a class, interface, or enum declaration
: Used to describe instance variables
ElementType.LOCAL_VARIABLE
ElementType.ANNOTATION_TYPE Another comment
Used to record package information of java files
@Inherited – Define the relationship between this annotation and a subclass
So, how is the internal definition of annotations? Annotations only supports basic types, String and enum types. All attributes in the comment are defined as methods and allow default values to be provided.
@Target() @Retention() @interface Todo { public enum Priority {LOW, MEDIUM, HIGH} public enum Status {STARTED, NOT_STARTED} String author() default "Yash"; Priority priority() default ; Status status() default Status.NOT_STARTED; }
The following example demonstrates how to use the above annotations.
@Todo(priority = , author = "Yashwant", status = ) public void incompleteMethod1() { //Some business logic is written //But it's not complete yet }
If there is only one attribute in the annotation, it can be named directly "value", and there is no need to indicate the attribute name when using it.
@interface Author{ String value(); } @Author("Yashwant") public void someMethod() { }
3.2 Repeat comments
Before Java 8, only one Annotation of the same type could be used before the same program element; if multiple Annotations of the same type should be used before the same element, the Annotation "container" must be used.
Let's first introduce this kind of "container".
First define a MyTag annotation:
//Specify annotations to be kept until runtime@Retention() //Specify annotations to modify classes, interfaces, and enumerations@Target() @interface MyTag { String name() default "test"; int age() default 20; }
Then define the container annotation of MyTag annotation:
//Specify annotations to be kept until runtime@Retention() //Specify annotations to modify classes, interfaces, and enumerations@Target() @interface MyTags { MyTag[] value(); }
Then you can use the annotation as follows
@MyTags({ @MyTag(name="Test 1",age=21), @MyTag(name="Test 2",age=22) }) public class Test { public static void main(String[] args) { //Analysis of the annotation through reflection Class testClass= ; //Get MyTags annotation MyTags myTagsAnnotation= (MyTags) (); //Get the MyTag annotation added to it MyTag[] myTags=(); for(MyTag myTag : myTags) { (("name:%1$s,age:%2$d",(),())); } } }
Print:
name:test 1, age:21
name:test 2, age:22
java8 provides sugar syntax for the above cumbersome syntax. A new @Repeatable meta annotation has been added to java8. You only need to add the upper meta annotation @Repeatable() to the MyTag annotation.
Observation shows that @Repeatable still needs to rely on container annotations, so it can still be used in the following way:
@MyTags({ @MyTag(name="Test 1",age=21), @MyTag(name="Test 2",age=22) })
Also, because MyTag is a duplicate annotation, it can also be used as follows:
@MyTag(name="Test 1",age=21) @MyTag(name="Test 2",age=22)
It should be noted here that repeated annotations are just a simple way to write them. Multiple repeated annotations will actually be array elements of value member variables as "container" annotations. For example, the MyTag annotation repeated above will be processed as an array element of the value member variable of the @MyTags annotation.
4. Use annotations
Now we know that we can specify the survival cycle of the annotation by using the @Retention annotation. There are three types of survival cycles of the annotation, namely:,,, These three values respectively indicate that the survival cycle of the annotation is source code, bytecode, and in runtime.
Next, we will introduce the processing of annotations in different stages:
4.1 Annotations for runtime processing
In fact, in the above case, it has been shown how to use reflection to obtain annotated data. If you want to process annotations when the program is running, the declaration period of the annotations must be declared as: @Retention() .
Since the annotation itself does not contain any business logic, in the runtime, we can implement specific logic through reflection.
First define a Debug annotation:
//Specify annotations to be kept until runtime@Retention() //Specify that this annotation can only be used in methods@Target() @interface Debug { boolean value() default false; }
Next, relate this annotation to the specific business logic:
public class DebugTest { public static void main(String[] args) { Class debugTestClass = ; //Get all methods Method[] methods = (); for (Method method : methods) { (true);//Disable security mechanism if (()) {//Check whether Debug annotation is used Debug debug = ();//Get annotated example String name = ();//Get the method name if (()) { ("method:" + name + " should debug"); } else { ("method:" + name + " should't debug"); } } } } @Debug(false) public void testMethod1() { } @Debug(true) public void testMethod2() { } @Debug(true) public void testMethod3() { } @Debug(false) public void testMethod4() { } @Debug(true) public void testMethod5() { } }
4.2 Annotations for compilation-time processing
If it is an annotation that needs to be processed during compilation, the declaration period of the annotation can be declared as: @Retention().
Here we need to introduce APT first. API (Annotation Processing Tool) is an annotation processing tool. It detects the source code, finds out the Annotation information contained in the source code, and then performs additional processing on the Annotation information. When using the APT tool to process Annotation, you can generate additional source files and other files based on the Annotation in the source file (the specific content of the file is determined by the writer of the Annotation processor). APT will also generate Class files together with the compiled source code file.
The main purpose of using APT is to simplify the workload of developers, because APT can generate some affiliate files (such as source files, class files, program release description files, etc.) while compiling the program source code. The contents of these affiliate files are also related to the source code. In other words, using APT can replace traditional maintenance tools for code information and attached files.
The tools provided by Java have a -processor option, which specifies an Annotation processor. If the Annotation processor is specified when compiling the java source file, then the Annotation processor will extract and process the Annotaion in the Java source file at compile time.
Each Annoataion processor needs to implement the Processor interface under the package, but implementing this interface must implement all methods under the interface. Therefore, the method of inheriting AbstractProcessor is usually used to implement the Annotation processor. An Annotation processor can handle one or more Annotaion annotations.
In Hibernate, if you use a non-annotation method, you must also maintain an additional Hibernate mapping file (a file named *.) for each Java Bean class file written. The following will use APT to simplify this operation.
To demonstrate the use of APT to generate additional files based on the annotations in the source file, three types of annotations are defined below.
@Persistent annotation that identifies the persistent class:
//Specify that this annotation can modify classes, interfaces, and enumerations@Target() //Specify that the annotation is retained until compile time@Retention() //Specify that the annotation can be displayed in the document (creating the document through javadoc, you can see the annotation information on the elements modified by the annotation)@Documented public @interface Persistent { String table(); }
@Id annotation for identifying attributes:
//Specify that this annotation can only modify fields@Target() //Specify that the annotation is retained until compile time@Retention() //Specify that the annotation can be displayed in the document (creating the document through javadoc, you can see the annotation information on the elements modified by the annotation)@Documented public @interface Id { String column(); String type(); String generator(); }
@Property annotation that identifies properties
//Specify that this annotation can only modify fields@Target() //Specify that the annotation is retained until compile time@Retention() //Specify that the annotation can be displayed in the document (creating the document through javadoc, you can see the annotation information on the elements modified by the annotation)@Documented public @interface Property { String column(); String type(); }
After having three Annotations, we define a simple Java Bean class.
@Persistent(table="personInfo") public class Person { @Id(column="person_id",type="integer",generator="identity") private int id; @Property(column="person_name",type="string") private String name; @Property(column="person_age",type="integer") private int age; public Person(){} public Person(int id,String name,int age) { =id; =name; =age; } //Setters and getters for all attributes...}
Next, write an API tool that generates a Hibernate mapping file based on the annotations in the java class.
import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; //Specify that the latest version of this annotation supports the Java platform is 6.0@SupportedSourceVersion(SourceVersion.RELEASE_6) //Specify that Persistent, id, Property annotations can be processed@SupportedAnnotationTypes({"Persistent","Id","Property"}) public class HibernateAnnotationProcessor extends AbstractProcessor{ @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { //Define the file output stream, used to generate additional files PrintStream ps=null; try{ for(Element t:()){ //Get the class name being processed Name className=(); // @Persistent Annotation before obtaining the class definition Persistent per= (); //Create file output stream ps=new PrintStream(new FileOutputStream(new File(className+"."))); //Execute output ("<?xml version=\"1.0\"?>"); ("<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\""); ("\"/hibernate-mapping-3.\">"); ("<hibernate-mapping>"); ("<class name=\""+className+"\" table=\""+()+"\" >"); for(Element f:()) { //Only handle Annotation on member variables if(()==) { //Get @Id Annotation before member variable definition Id id=(); //But when the @id annotation exists, the <id ../> element is output if(id!=null) { ("<id name=\""+()+"\" "+ "column=\""+()+"\" "+ "type=\""+()+"\">"); ("<generator class=\""+()+"\" />"); ("</id>"); continue; } // Get @Property Annotation before member variable Property p=(); if(p!=null) { ("<property name=\""+()+"\" "+ "column=\""+()+"\" "+ "type=\""+()+"\" />"); continue; } } } ("</class>"); ("</hibernate-mapping>"); } }catch(Exception e) { (); }finally{ if(ps!=null) (); } return true; } }
After compilation, execute the following command:
javac -processor HibernateAnnotationProcessor
You can see that there is an additional file under this path
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "/hibernate-mapping-3."> <hibernate-mapping> <class name="Person" table="personInfo" > <id name="id" column="person_id" type="integer"> <generator class="identity" /> </id> <property name="name" column="person_name" type="string" /> <property name="age" column="person_age" type="integer" /> </class> </hibernate-mapping>