Notes:
Annotation provides a formal way for us to add information to the code, which makes it very convenient for us to use the data in source code, compile time and run time.
Annotation is introduced in JAVA SE5, which makes the code cleaner and easier to read, and can realize compile time type checking, etc. When creating a class or interface that describes a property, consider using annotations to simplify or automate the process if there is repetitive work. We can save the annotation in the source code and use the Annotation API to process the annotation to get the data we want and process it. The use of annotation is relatively simple. JAVA SE5 has three built-in types:
- @Override means that the method in the current class will overwrite the method in the parent class. If it is not written, there will be no error. However, @ override can check. For example, if the method name is misspelled, the compiler will give an alarm.
- @Deprecated indicates that the annotated method has been discarded, and a warning message will be issued if the compiler is used.
- @SuppressWarnings turns off improper compiler warnings. Unless you are sure that the compiler's warning message is incorrect, it is best not to use this annotation.
Definition annotation
First, let's see how the built-in annotation @ Override is defined. It is under package java.lang:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
@Target and @ Retention are called meta annotations: meta annotations are used to annotate other annotations, such as: Br / >
@Retention defines at what level annotations are available, in SOURCE, CLASS, or RUNTIME.
In addition to @ Target, @ Retention, @ Documented and @ Inherited, use a table to list their respective roles:
@The scope of Retention is shown in the following figure:
annotation processor
First, define an annotation:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface AnnotationInfo { String[] value(); int requestCode() default 0; }
- The method defined in the annotation has no parameters, and the return type is limited to the original type, string, enumeration, annotation or the collection of the above types
- Methods defined in annotations can have default values
Runtime parsing annotations
@Target(ElementType.METHOD) indicates that our annotation works on methods.
@Retention(RetentionPolicy.RUNTIME) indicates that annotations will also exist during program running, that is, annotation information will also be loaded into VM of virtual machine, so relevant information of annotations can be obtained through reflection:
public class AnnotationExample { /** * Comment impersonation request permission */ @AnnotationInfo(value = {"android.permission.CALL_PHONE", "android.permission.CAMERA"}, requestCode = 10) public void requestPermission() { //Other logic } }
Next, write a Java class that parses annotations at run time: AnnotationRuntimeProcessor.java
public class AnnotationRuntimeProcessor { public static void main(String[] args) { try { //Get the Class object of AnnotationExample Class<?> cls = Class.forName("com.javastudy.Annotation.AnnotationExample"); //Get methods in AnnotationExample class Method[] methods = cls.getDeclaredMethods(); for (Method method : methods) { //Filtering methods without custom annotation AnnotationInfo boolean isHasAnnotation = method.isAnnotationPresent(AnnotationInfo.class); if (isHasAnnotation) { method.setAccessible(true); //Get annotation on method AnnotationInfo aInfo = method.getAnnotation(AnnotationInfo.class); if (aInfo == null) return; //Parsing the corresponding information on the annotation String[] permissions = aInfo.value(); System.out.println("value: " + Arrays.toString(permissions)); int requestCode = aInfo.requestCode(); System.out.println("requestCode: " + requestCode); } } } catch (Exception e) { e.printStackTrace(); } } }
The above logic is very simple. The Class object corresponding to the annotated Class is retrieved by reflection, the annotated method is filtered, and finally the annotation on the method is obtained and parsed. The operation result is as follows:
value: [android.permission.CALL_PHONE, android.permission.CAMERA] requestCode: 10
Parsing annotations at compile time
AbstractProcessor is an API under Java X. Java and javax are both Java API(Application Programming Interface) packages. Java is the core package, and javax's x is the extension package. To inherit AbstractProcessor, you need to implement the following methods:
public class ProcessorExample extends AbstractProcessor { @Override public synchronized void init(ProcessingEnvironment processingEnvironment) { //processingEnvironment provides various tool classes such as Elements Filer Types SourceVersion, etc. super.init(processingEnvironment); } /** * Scan evaluation and processing annotation code to generate Java code * * @param set annotation type * @param roundEnvironment Query the annotated elements with specific annotations in the current and previous information environment * @return Return true to indicate that the annotation has declared that subsequent processors will not process false to indicate that subsequent processors will process them */ @Override public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) { return false; } @Override public SourceVersion getSupportedSourceVersion() { return super.getSupportedSourceVersion(); } @Override public Set<String> getSupportedAnnotationTypes() { return super.getSupportedAnnotationTypes(); } }
- init(ProcessingEnvironment env): every annotation processor class must have an empty constructor. However, there is a special init() method that will be called by the annotation handler and enter the processingenvironment parameter. Processingenvironment provides many useful tool classes, such as Elements, Types and filers. We will see the details later.
- process(Set (? extends TypeElement) annotations, RoundEnvironment env): This is equivalent to the main function of each processor, main(). Here you write your code for scanning, evaluating, and processing annotations, as well as generating Java files. The input parameter roundenvironment allows you to query the annotated elements with specific annotations. We will see the details later.
- getSupportedAnnotationTypes(): Here you must specify to which annotation the annotation handler is registered. Note that its return value is a collection of strings containing the legal full name of the annotation type the processor wants to process. In other words, here you define which annotations your annotation processor registers with.
- getSupportedSourceVersion(): used to specify the Java version you are using. Typically, SourceVersion.latestSupported() is returned here. However, if you have a good reason to only support Java 6, you can also return sourceversion. Release? 6. The former is recommended.