Java annotations in detail

1. The concept of the Annotation

An annotation is a form of metadata, that can be added to Java source code. Classes, methods, variables, parameters and packages may be annotated. Annotations have no direct effect on the operation of the code they annotate.

  • Annotations are a type of metadata that can be added to Java source code.
  • Classes, methods, variables, parameters, and packages can all be annotated.
  • Annotations have no direct effect on the annotated code.
  • A comment is just a marker. Annotations work because they are parsed and processed accordingly

2. The classification of the Annotation

  • Standard annotations
    • Standard Annotations are the three AnnNotaions built into Java:
    • Override: Applies to a method that overrides a parent class.
    • Deprecated: Used to modify an obsolete method.
    • @Suppresswarnnings: Used to inform the Java compiler to disallow specific compilation warnings.
  • Meta-annotations
    • A meta-annotation is an Annotation that defines an Annotation
    • Meta-annotations can define the scope of an Annotation, what elements are used on
    • There are four types of meta-annotations: @Retention, @target, @Inherited, and @documented
  • The custom Annotation

3. The Annotation

  • @Retention: Note in the other annotation A that indicates the extent of Retention of A. Optional values SOURCE (SOURCE time), CLASS (compile time), RUNTIME (RUNTIME), default is CLASS
    • SOURCE:A is only retained in the SOURCE code,A will be ignored at compile time.
    • CLASS:A is stored in the CLASS file by compilation, but is ignored by the JVM at run time and is not visible at run time.
    • RUNTIME:A is retrieved by the JVM and is retrieved by reflection at RUNTIME.
  • @target: The additional annotation A limits which program elements A can modify. Unmarked Target means unrestricted and modifies all elements.
    • ANNOTATION_TYPE: A can be applied to additional annotations
    • CONSTRUCTOR: A can be used on A CONSTRUCTOR
    • FIELD: A can be used on fields or properties
    • LOCAL_VARIABLE: A can be used with local variables.
    • METHOD: A can be used for methods.
    • PACKAGE: A can be used on PACKAGE declarations.
    • PARAMETER: A can be used for method parameters
    • TYPE: A can be used on declarations of classes, interfaces (including annotations), or enumerations
  • Inherited: By default, annotations of a parent class are not Inherited by child classes.
    • Inherited is in the other note A.
    • Inherited only works when A is an annotation on A Class; it doesn’t work in any other case.
    • When annotation A is on class C, then all descendant classes of C inherit annotation A;
  • Documented on the other annotation (A),A will be included in the Javadoc Documented. Annotations do not become part of the document by default.

4. Customize annotations

  1. Create a custom Annotation process

    • public@interface User-defined annotation name
      public @interface CustomAnnotation{***}
          
      Copy the code
    • Set the Retention scope and Target of a custom Annotation. Retention and Target are the two most important Anitation elements.
      @Retention( RetentionPolicy.RUNTIME )
      @Target( ElementType.TYPE )
      public @interface CustomAnnotation{***}
      Copy the code
    • Set the Annotation parameters for your custom Annotation (Annotation member)
      • Annotate the data type supported by the parameter
        • All basic data types (int, float, Boolean, byte, double, char, long, short)
        • Type String
        • The Class type
        • Enum type
        • The Annotation type
        • One-dimensional arrays of all the above types
      • Annotate how parameters are declared
        @Retention( RetentionPolicy.RUNTIME ) @Target( ElementType.TYPE ) public @interface CustomAnnotation{ Public enum Skill{JAVA,ANDROID,IOS} Skill mySkill() default Skill.ANDROID; String attr1(); Int attr2() default 100; Public public Boolean attr3() default false; } @Retention( RetentionPolicy.RUNTIME ) @Target( ElementType.TYPE ) public @interface CustomAnnotation{ // Only one annotation argument, use value() String value(); }Copy the code
        • The parameter types of the custom Annotation must meet the range 1 through 6 of the previous article.
        • The parameter access method for a custom Annotation can only be public or not write.
        • The parameter of a custom Annotation can be set to default.
        • Custom annotations use value() if they have only one argument.
  2. Default value for the Annotation parameter of a custom Annotation

    An annotation element must have a definite value, either specified in the default value used to define the annotation or when using the annotation. The value of an annotation element of a non-primitive type cannot be NULL. Therefore, using an empty string or 0 as the default is a common practice. This constraint may make it harder for the processor to show the existence of an element or the lack of status, because each statement annotations, all elements are present, and have the corresponding values, in order to circumvent this constraint, we can define some special values, such as an empty string or negative, said an element does not exist at a time, when the custom annotation, it has become a habit.

    Example: @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface AnotherAnnotation{ String author() default ""; int age() default -1; }Copy the code
  3. Use the custom annotations you just created

    @CustomAnnotation(attr1 = "Attribute 1", attr2 = 90, attr3 = true) public Class AnnotationTestClass{***}Copy the code

5. The Annotation

  • Runtime Annotation parsing

    RUNTIME Annotation is an Annotation that @Retention is RUNTIME

    • Class,Method, and Field all have the following three methods to call

      • public T getAnnotation(Class annotationClass) Gets an annotation of the specified type as passed in. Returning NULL indicates that the current element does not carry this annotation.
      • public final boolean isAnnotationPresent(Class<? extends Annotation> annotationType) Checks if the passed annotation exists in the current element.
      • public Annotation[] getAnnotations() Returns all annotations for the element, including those on which the element is not explicitly defined.
    • Run-time Annotation parsing example

      public void testCustomAnnotation() { try { Class cls = Class.forName("com.jet.annotation.AnnotationTestClass"); CustomAnnotation customAnnotation = (CustomAnnotation)cls.getAnnotation(CustomAnnotation.class); System.out.println("customAnnotation mySkill:" + cus.mySkill()); System.out.println("customAnnotation attr1:" + cus.attr1()); System.out.println("customAnnotation attr2:" + cus.attr2()); } catch (ClassNotFoundException e) { e.printStackTrace(); }}Copy the code
  • Annotation parsing at compile time

    Compile-time annotations refer to annotations that @retention is CLASS and are resolved by the jasmine compiler automatically


6. Annotation parsing at compile time

Annotation parsing is relatively complex at compile time, which is analyzed separately below

First off: The following content only discusses compile-time Annotation parsing

  1. Parsing annotations at compile time is done by the Annotation Processor
  2. Annotation Processor
    • Annotation processor is a tool in JavAC that scans and processes annotations at compile time
    • We can register custom annotation handlers for specific annotations
    • The JVM automatically runs registered annotation handlers during compilation
    • An annotated Annotation Processor that takes Java code (or a compiled class) as input and generates a. Java file as output. This means we can generate new Java code! The generated Java code is in a generated.java file. The newly generated.java file is compiled by Javac just like regular, hand-written Java source code
  3. Each annotation processor is descended from AbstractProcessor, and there are four methods to keep an eye on
Public abstract class AbstractProcessor implements Processor {public synchronized void Init (ProcessingEnvironment processingEnv) // Its return value is a collection of strings, Contains the processor to process the annotations types of legal full name public Set < String > getSupportedAnnotationTypes () / / specify the annotation processor using JAVA version, usually returns SourceVersion. LatestSupported () public SourceVersion getSupportedSourceVersion () / / real generates JAVA code Annotations //roundEnv: roundEnv allows you to query for annotated elements that contain specific annotations, equivalent to "global source context" // If true, the annotations are declared and do not require subsequent processors to process them; // If false is returned, these annotations are undeclared and may require subsequent processors to process them. extends TypeElement> annotations,RoundEnvironment roundEnv) }Copy the code
  1. To customize the annotation processor, you inherit AbstractProcessor and rewrite the above four methods

Compile-time Annotation parsing, Annotation framework (3) — Compile-time Annotation, handwritten ButterKnife, according to the above process of the article to knock over the code, I believe you can create and parse custom annotations have a deep understanding!

7. The impact of annotations on App

  1. Run-time annotations have an impact on performance, and compile-time annotations have no impact on App performance.
  2. The parsing of run-time annotations is entirely dependent on reflection, which is slower than direct calls and only has an impact if the run-time annotations are used too much
  3. Java files are compiled into.class files. Then class files are packaged and a series of processing. Generated apk. And it finally made it to our phones. Compile-time annotations, on the other hand, are what happens when Java builds.class files. It has nothing to do with our APK operation, there is no problem affecting performance;
  4. Compile-time annotation library, which is generally referenced in app as follows:
dependencies { implementation fileTree(dir: 'libs', include: [' *. Jar ']) implementation 'com. Android. Support: appcompat - v7:27.1.1' / / API module: android library implementation Annotations module: Java Library Implementation Project (': Butterknife-Annotations ') // Annotations module: Java Library Implementation Project (': Butterknife-Annotations ') //Annotation parsing module: Java Library annotationProcessor project(': butterknife-Compiler ')}Copy the code

In fact, in the process of packaging and generating APK, only the API module and Annotation module will be packaged into APK. The Annotation parsing module is provided for IDE and does not exist in our APK.

8. Annotation parsing process

Annotation parsing process:

  1. Java Module where the annotation is created: lib_annotation
    • All required annotations are written in this module
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.FIELD)
public @interface BindView {
    int value();
}
Copy the code
  1. Create the Java Module: lib_processor needed to parse annotations
    • build.gradle
dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) // Module implementation Project (':lib_annotation') // Javapoet used to generate Java file implementation 'com. Squareup: javapoet: 1.11.1'}Copy the code
- In package, create annotation parsing class CustomAnnotationProcess, inherit AbstractProcessor - in main - create resources/ meta-INF /services/ folder - In the services folder to create javax.mail. The annotation. Processing. The Processor file - javax.mail. The annotation. Processing. The Processor to create annotation class CustomAnnotationProcess ``` com.huanhailiuxin.a28_lib_processor.CustomAnnotationProcess ``` - The most important part,CustomAnnotationProcessCopy the code
  1. Create a call to Android Module: lib_bind that generates the ‘bindClass’ declared in lib_processor
public final class Binding { public static void bind(Activity oriActivity) { try { Class oriClass = Class.forName(oriActivity.getClass().getCanonicalName()); Class bindClass = Class.forName(oriActivity.getClass().getCanonicalName() + "$Binding"); Constructor constructor = bindClass.getDeclaredConstructor(oriClass); constructor.newInstance(oriActivity); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); }}}Copy the code
  1. In the app module, reference lib_annotation, lib_processor, lib_bind
    • Add annotations to the specified UI control in the Activity
    • Bind
@BindView(R.id.tv_compile_annotation) public TextView tv_compile_annotation; @BindView(R.id.parent) public ScrollView parent; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_compile_annotations); *** // Call the Binding method binding.bind (this); Tv_compile_annotation.settext (" test compile-time annotation parsing "); parent.setBackgroundColor(getResources().getColor(R.color.colorAccent)); } // After the actual packaging of APK, Implementation Project (':lib_annotation') implementation Project (':lib_annotation') implementation project(':lib_bind') annotationProcessor project(':lib_processor')Copy the code

Android has been used in detail

  • Use of annotations in Android
    @IntDef({RED, BLUE, YELLOW})
    @Retention(RetentionPolicy.SOURCE)
    public @interface LightColors{};
    
    public static final int RED = 1;
    public static final int BLUE = 2;
    public static final int YELLOW = 3;
    
    public void setColor(@LightColors int color){}Copy the code