Annotations (also known as metadata) provide a formal way for us to add information in our code, allowing us to use this data very conveniently at some later moment.
1. Basic syntax
Java SE5 has three built-in standard annotations
@Override: Indicates that the current method definition will overwrite the methods in the superclass. If you accidentally spell the wrong spelling, or the method signature does not match the overwritten method, the compiler will issue an error message.
@Deprecated: If the programmer uses an annotated element, the compiler will issue a warning message
@SupperessWarnings: Close the inappropriate compiler warning message.
Java SE5 has four built-in meta annotations
@Target: Indicates where this annotation can be used. Possible ElementType parameters include:
1) CONSTRUCTOR: The statement of the constructor
2)FIELD: Domain declaration (including enum instances)
3) LOCAL_VARIABLE: Local variable declaration
4)METHOD: Method statement
5) PACKAGE: Package Statement
6)PARAMETER: Parameter declaration
7)TYPE: class, interface (including annotation type) or enum declaration
@Retention: Indicates at what level to save the annotation information. Optional RetentionPolicy parameters include:
1) SOURCE: The annotation will be discarded by the compiler
2) CLASS: Annotations are available in class files, but will be discarded by the VM
3) RUNTIME: VM will also retain annotations during runtime, so the annotation information can be read through the reflection mechanism.
@Documented: Include this annotation in Javadoc
@Inherited: Allow subclasses to inherit annotations in parent classes Most of the time, programmers mainly define their own annotations and write their own processors to handle them.
UseCase.java
package com; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD)//Use to define where your annotation will be applied, and this is applied as a method//Use to define at which level the annotation is available, in the source code (class) or runtime (runtime) @Retention(RetentionPolicy.RUNTIME) public @interface UseCase { public int id(); public String description()default "no description"; } PasswordUtils .java package com; public class PasswordUtils { @UseCase(id=47,description="Passwords must contain at least one numeric") public boolean validatePassword(){ return true; } @UseCase(id=48) public String encryptPassword(String password){ return password; } @UseCase(id=49,description="Jong_Cai") public void showName(){ System.out.println("Jong_Cai"); } }
2. Write annotation processor
If there is no tool to read annotations, annotations will not be more useful than annotations. In the process of using annotations, an important part is to create and use annotation processors. Java SE5 extends the reflection mechanism API to help programmers construct such tools. At the same time, it also provides an external tool apt to help programmers parse Java source code with annotations. Below is a very simple annotation processor, which we will use to read the PasswordUtils class and use the reflection mechanism to find the @UseCase tag. We provide it with a set of id values, and then it lists the use cases found in PasswordUtils, as well as the missing use cases.
UseCaseTracker.java package com; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class UseCaseTracker { public static void trackUseCases(List<Integer> list, Class<?> cl) { for (Method m : cl.getDeclaredMethods()) { UseCase us = m.getAnnotation(UseCase.class); if (us != null) { System.out.println("Found Use Case:" + us.id() + " " + us.description()); list.remove(new Integer(us.id())); } } for (int i : list) { System.out.println("Warning:Missing use case-" + i); } } public static void main(String[] args) { List<Integer> list = new ArrayList<Integer>(); Collections.addAll(list, 47,48,49,50,51); trackUseCases(list, PasswordUtils.class); } }
This program uses two reflection methods: getDeclaredMethods() and getAnnotation(). They both belong to the AnnotatedElement interface (class, Method and Field and other classes implement this interface). The getAnnotation() method returns an annotation object of the specified type, which is UseCase. If the annotated method does not have an annotation of the type, it returns a null value. Then we extract the value of the element from the returned UseCase object by calling the id() and description() methods. The encryptPassword() method does not specify the description value when annotating, so when the processor processes its corresponding annotation, the default value of the description() method is obtained by the description() method.
Annotation is spreading in the world of Java. If you have time, write this simple annotations article. It is an introduction to Annotation. I hope you can throw a brick and learn together...
If you stop talking nonsense, practice is the bottom line.
3. Example
Let’s talk about the concept of annotation first, and then talk about how to design your own annotation.
First, in the java.lang.annotation package that comes with jdk, open the following source files:
Source file Target.java
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { ElementType[] value(); @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { ElementType[] value(); }
@interface is a keyword. When designing annotations, a type must be defined as @interface, and you cannot use class or interface keywords (will you think sun is a bit stingy, but it looks so similar to interface).
Source file Retention.java
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { RetentionPolicy value(); } @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { RetentionPolicy value(); }
After seeing this, you may be vague and don’t know what you are talking about. Don’t worry, take a look. The above files use the two fields RetentionPolicy and ElementType, and you may guess that these are two Java files. Indeed, the source codes of these two files are as follows:
Source file RetentionPolicy.java
public enum RetentionPolicy { SOURCE, CLASS, RUNTIME } public enum RetentionPolicy { SOURCE, CLASS, RUNTIME } This is an enum type, with three values, namely SOURCE, CLASS and RUNTIME.
SOURCE means that the Annotation type information will only be retained in the program source code. If the source code is compiled, the Annotation data will disappear and will not be retained in the compiled .class file.
ClASS means that the Annotation type information is retained in the program source code, and it will also be retained in the compiled .class file. When executing, this information will not be loaded into the virtual machine (JVM). Note that when you do not set a Retention value of Annotation type, the system default value is CLASS.
The third is RUNTIME, which means that information is retained in the source code and compiled .class file, and this information will be loaded into the JVM during execution.
For example, if the Retention in @Override is set to SOURCE, if the compilation is successful, you do not need these checked information; on the contrary, the Retention in @Deprecated is set to RUNTIME, which means that in addition to warning us which method is used during compilation, you can also check whether the method is Deprecated when executing.
Source file ElementType.java
public enum ElementType { TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE,PACKAGE } public enum ElementType { TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE,PACKAGE } @The ElementType in Target is used to specify which elements Annotation type can be used. Let's explain: TYPE (type), FIELD (attribute), METHOD (method), PARAMETER (parameter), CONSTRUCTOR (constructor), LOCAL_VARIABLE (local variable), ANNOTATION_TYPE, PACKAGE (package), where TYPE (type) refers to it being used on Class, Interface, Enum and Annotation types.
In addition, it can be seen from the source code of 1 that @Target himself also used himself to declare himself, and could only be used on ANNOTATION_TYPE.
If an Annotation type does not specify which elements @Target is used, it can be used on any element, and the element here refers to the above eight types.
Let me give you a few correct examples:
@Target(ElementType.METHOD)
@Target(value=ElementType.METHOD)
@Target(ElementType.METHOD,ElementType.CONSTRUCTOR)
Please refer to the javadoc documentation for details
The source files all use @Documented. The purpose of @Documented is to enable this Annotation type information to be displayed on the javaAPI description document; if it is not added, the information generated by this type will not be found when using javadoc to generate the API document.
Another point is that if you need to inherit Annotation data to a subclass, you will use the Annotation type @Inherited.
The following is the simplest Annotation example to design, which consists of four files;
Description.java
package lighter.javaeye.com; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Description { String value(); } package lighter.javaeye.com; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Description { String value(); }
Note: All Annotations will automatically inherit the interface java.lang.annotation, so you cannot inherit other classes or interfaces.
The most important point is how to set the parameters in the Annotation type:
First, you can only use public or default access rights to modify it. For example, String value(); here set the method to default default type.
Second, parameter members can only use eight basic data types: byte, short, char, int, long, float, double, boolean, and data types such as String, Enum, Class, annotations, as well as arrays of these types. For example, String value(); the parameter member here is String.
Third, if there is only one parameter member, it is best to set the parameter name to "value" and add brackets afterwards. Example: The above example has only one parameter member.
Name.java
package lighter.javaeye.com; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; //Note that the @Target here is different from @Description, and the parameter members are also different @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Name { String originate(); String community(); } package lighter.javaeye.com; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; //Note that the @Target here is different from @Description, and the parameter members are also different @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Name { String originate(); String community(); }
JavaEyer.java
package lighter.javaeye.com; @Description("javaeye, be the best software development exchange community") public class JavaEyer { @Name(originate="Founder:robbin",community="javaEye") public String getName() { return null; } @Name(originate="Founder: Jiangnan Baiyi",community="springside") public String getName2() { return "Borrow two ids of ids, please forgive me for writing this example!"; } } package lighter.javaeye.com; @Description("javaeye, be the best software development exchange community") public class JavaEyer { @Name(originate="Founder:robbin",community="javaEye") public String getName() { return null; } @Name(originate="Founder: Jiangnan Baiyi",community="springside") public String getName2() { return "Borrow two ids of ids, please forgive me for writing this example!"; } }Write a TestAnnotation that can run the TestAnnotation class that extracts JavaEyer information
package lighter.javaeye.com; import java.lang.reflect.Method; import java.util.HashSet; import java.util.Set; public class TestAnnotation { /** * author lighter * Note: For details, please refer to the javaDoc documentation for the usage of the Annotation API*/ public static void main(String[] args) throws Exception { String CLASS_NAME = "lighter.javaeye.com.JavaEyer"; Class test = Class.forName(CLASS_NAME); Method[] method = test.getMethods(); boolean flag = test.isAnnotationPresent(Description.class); if(flag) { Description des = (Description)test.getAnnotation(Description.class); System.out.println("Description:"+des.value()); System.out.println("------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- HashSet<Method>(); for(int i=0;i<method.length;i++) { boolean otherFlag = method[i].isAnnotationPresent(Name.class); if(otherFlag) set.add(method[i]); } for(Method m: set) { Name name = m.getAnnotation(Name.class); System.out.println(name.originate()); System.out.println("Created Community:"+name.community()); } } } package lighter.javaeye.com; import java.lang.reflect.Method; import java.util.HashSet; import java.util.Set; public class TestAnnotation { /** * author lighter * Note: For details, please refer to the javaDoc documentation for the usage of the Annotation API*/ public static void main(String[] args) throws Exception { String CLASS_NAME = "lighter.javaeye.com.JavaEyer"; Class test = Class.forName(CLASS_NAME); Method[] method = test.getMethods(); boolean flag = test.isAnnotationPresent(Description.class); if(flag) { Description des = (Description)test.getAnnotation(Description.class); System.out.println("Description:"+des.value()); System.out.println("------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- { boolean otherFlag = method[i].isAnnotationPresent(Name.class); if(otherFlag) set.add(method[i]); } for(Method m: set) { Name name = m.getAnnotation(Name.class); System.out.println(name.originate()); System.out.println("Created community:"+name.community()); } } } Running results:
Description: javaeye, the best software development exchange community founder: robbin created community: javaEye founder: javaEye created community: springside