Definition of annotations

Definition: Annotations, also called metadata. A code level specification. It is a feature introduced in JDK1.5 and later and is on the same level as classes, interfaces, and enumerations. It can be declared in front of packages, classes, fields, methods, local variables, method parameters, etc., used to describe and comment these elements. Common annotations are as follows:

Main functions of annotations

  1. Generate documentation. This is the most common and the earliest annotation provided in Java. Common ones are @see @param @return
  2. Track code dependencies, implement alternative configuration file functions, alternative XML functions, and compare how much is used in SpringMVC.
  3. Do formatting checks at compile time, such as @overRider@callsuper@nullable and @nonNULL
  4. @layoutres, @integerres, 3) Process class annotation @UithRead, @binderThread,@MainThread, @workerThread 4) ButterKnife , EventBus, Dagger2 framework

Yuan comments:

So how is an annotation defined? Let’s first look at the commonly used Override annotation.

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
Copy the code

Here involves meta-annotations, we generally use meta-annotations to define annotations, meta-annotations mainly have the following. We will introduce them one by one.

  1. @Target
  2. @Retention
  3. @Documented
  4. @Inherited
  5. @repTable JAVA8 (New)

@Target

Describe the scope of use of annotations (that is, where the modified annotations can be used). The main types are as follows:

public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,

    /** Field declaration (includes enum constants) */
    FIELD,

    /** Method declaration */
    METHOD,

    /** Formal parameter declaration */
    PARAMETER,

    /** Constructor declaration */
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE,

    /**
     * Type parameter declaration
     *
     * @since1.8 * /
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since1.8 * /
    TYPE_USE
}

Copy the code

@Documented

Describes whether to retain annotation information when generating help documents for classes using the Javadoc tool.

@Inherited

Inherited annotations are used to make annotations that are modified by @Inherited Inherited. (If a class uses an annotation modified by @Inherited, its child classes automatically inherit the annotation.)

@Retention

Retention annotation values are in RetentionPolicy

public enum RetentionPolicy {
   SOURCE,
   CLASS,
   RUNTIME
}
Copy the code
  1. SOURCE: valid in the SOURCE file (i.e. retained in the SOURCE file). @Override and @SuppressWarnings are annotations of this type;
  2. CLASS: valid in a CLASS file (i.e., CLASS reserved)
  3. RUNTIME: valid at RUNTIME (i.e., reserved at RUNTIME)

RetentionPolicy.Source

Take a look at the retentionPolicy.source scenario: Since the annotation is retained during compilation, you can do something about the policy during compilation.

  1. Enumerated type

We sometimes define enumerations using enums, which often take up twice as much memory as static constants, but we prefer to define enumerations using annotations. The usage is as follows:

  @IntDef({Color.RED , Color.YELLOW ,Color.BLUE})
  @Retention(RetentionPolicy.SOURCE)
   public @interface Color{
    int RED = 1;
    int YELLOW = 2;
    int BLUE= 3;
   }
Copy the code
  1. Lombok has a similar scenario

There are also scenarios such as those where Java code is automatically generated. The most common is the use of lombok, can automatically generate the field of the get and set methods, and toString method, constructor and detailed implementation methods and principle to see: www.jianshu.com/p/fc06578e8…

RetentionPolicy.Class

CLASS: valid in a CLASS file (that is, CLASS reserved). Compile-time annotation annotation processor implementation rely mainly on AbstractProcessor, this class is in the javax.mail annotation. Processing package, convenience for ourselves generate Java source files at the same time, we also need to introduce some third-party libraries, Javapoet is mainly used to generate Java source files for referenceGithub.com/square/java…The auto-service is used to generate auxiliary information, such as meta-INF /services information.Typical case frame: ButterknifeThe basic principles are as follows:

Annotation processor

First, let’s look at what an Annotation handler is. An Annotation handler is a javAC tool that scans and processes annotations at compile time. You can customize annotations and register them with the annotation handler that handles your annotations. An annotation processor that takes Java code (or compiled bytecode) as input and generates a file (usually a.java file) as output. The generated Java code is in a generated.java file, so you cannot modify existing Java classes, such as adding methods to existing classes. These generated Java files are compiled by JavAC just like any other plain, hand-written Java source code.

Customize the RetentionPolicy.Class annotation scenario

AbstractProcessor interfaces are only introduced here, and custom annotations will be explained later.

public class MyProcessor extends AbstractProcessor {


   private Elements mElementsUtils;

   /** * note initialization interface, generally used to do some initialization preparation operations; * <p> * processingEnvironment provides several tools * including action elements, print information, file managers, and other tools * *@param processingEnvironment
    */
   @Override
   public synchronized void init(ProcessingEnvironment processingEnvironment) {
       super.init(processingEnvironment);
       // Element manipulation tool
       Elements elementUtils = processingEnvironment.getElementUtils();
       mElementsUtils = elementUtils;

       // Create Java source files or class files
       Filer filer = processingEnvironment.getFiler();
       // Region information
       Locale locale = processingEnvironment.getLocale();
       // Processor-specific options passed to the annotation processing tool
       Map<String, String> options = processingEnvironment.getOptions();
       // Any generated source and class files should conform to the source version
       SourceVersion sourceVersion = processingEnvironment.getSourceVersion();
       // Implementations of utility methods used to operate on types
       Types typeUtils = processingEnvironment.getTypeUtils();
       // Returns messages to report errors, warnings, and other notifications.
       Messager messager = processingEnvironment.getMessager();
   }

   /** ** Annotation processing interface, annotation really implemented logic * we can generate Java source files through JavaPoet **@paramAnnotations Specifies the type of annotation that the request handles *@paramRoundEnvironment The environment for the current and previous round of information *@returnIf true is returned, comment types are declared and subsequent handlers are not required to process them; If false is returned, the annotation types are unclaimed and may require subsequent processors to process them */
   @Override
   public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) {
       // Return true if an error was raised in the previous round of processing; Otherwise return false
       boolean b = roundEnvironment.errorRaised();

       // Returns root elements for annotation processing generated in the previous round
       Set<? extends Element> rootElements = roundEnvironment.getRootElements();
       // Returns true if the type generated in this round is not affected by comment processing in subsequent rounds; Otherwise return false.
       boolean b1 = roundEnvironment.processingOver();

       // Returns the element annotated with the given annotation type.
       Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(MyAnnotations.class);
       for (Element element : elements) {
           TypeElement typeElement = (TypeElement) element;

           // Get the value of the annotation
           MyAnnotations annotation = typeElement.getAnnotation(MyAnnotations.class);
           int value = annotation.value();

           // Gets the fully qualified name of the element of this type
           String s = typeElement.getQualifiedName().toString();

           // Get the package path of the current annotation
           PackageElement packageElement = mElementsUtils.getPackageOf(typeElement);
           String packageName = packageElement.getQualifiedName().toString();

       }

       return true;
   }


   /** * The name of the annotation type supported by this processor **@return* /
   @Override
   public Set<String> getSupportedAnnotationTypes(a) {
       / / this processor only handle MyAnnotations annotation
       Set<String> supportedAnnotationTypes = new HashSet<>();
       supportedAnnotationTypes.add(MyAnnotations.class.getCanonicalName());
       return supportedAnnotationTypes;
   }


   /** * Returns the latest source version supported by this comment processor **@return* /
   @Override
   public SourceVersion getSupportedSourceVersion(a) {
       return super.getSupportedSourceVersion();
   }


   @Target(ElementType.PACKAGE)
   @Retention(RetentionPolicy.CLASS)
   private @interface MyAnnotations {
       int value(a); }}Copy the code

RetentionPolicy.RUNTIME

Retentionpolicy. RUNTIME: Annotations are not only saved to the class file, but still exist after the JVM loads the class file.

Annotations provide annotation related operations on various elements, including Class, Method, Field, and so on. Let’s take a look at what annotation-related methods they provide, primarily the AnnotatedElement interface.

    /** * Returns true if an annotation of the specified type exists on this element, false */ otherwise
    default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
        returngetAnnotation(annotationClass) ! =null;
    }

   /** * Returns a comment for this element if the specified comment type exists on it, otherwise null */
    <T extends Annotation> T getAnnotation(Class<T> annotationClass);

    /** * returns a comment that exists on this element. If no annotation exists on this element, the return value is length 0. Callers of this method can modify the array of the returned array at will. It has no effect on arrays returned to other callers. * /
    Annotation[] getAnnotations();

    /** * returns the comment associated with this element. If there is no comment associated with this element, the return value is an array of length 0. * /
    <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) ;

    /** * Returns an annotation for this element of the specified type if such an annotation exists directly, or null otherwise. This method ignores inherited comments. * /
    <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass) ;

    /** * Returns a comment for this element of the specified type if such a comment exists directly or indirectly. * This method ignores inherited comments. The difference between this method and [' getDeclaredAnnotation(Class) ']) * is that it detects if the argument is a repeatable annotation type * */
    <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass);

    /** * returns comments that appear directly on this element. This method ignores inherited comments. If no comment exists directly on this element, an array of length 0 */ is returned
    Annotation[] getDeclaredAnnotations();
Copy the code

The following is a simple example

@AnnotationDemo.TypeRuntimeAnnotation(11)
public class AnnotationDemo {

    private static final String TAG = "AnnotationDemo";


    public static void testAnnotationDemo(a) {
        Log.d(TAG, "testAnnotationDemo() called");
        Class<AnnotationDemo> testAnnotationsClass = AnnotationDemo.class;

        // Find class annotations
        TypeRuntimeAnnotation[] annotations = new TypeRuntimeAnnotation[0];
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
            annotations = testAnnotationsClass.getAnnotationsByType(TypeRuntimeAnnotation.class);
        }
        for (TypeRuntimeAnnotation annotation : annotations) {
            int value = annotation.value();
            Log.d(TAG,"TypeRuntimeAnnotation: " + value);
        }

        // How to find annotations
        Method[] declaredMethods = testAnnotationsClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            MethodRuntimeAnnotation annotation = declaredMethod.getAnnotation(MethodRuntimeAnnotation.class);
            if(annotation ! =null) {
                Log.d(TAG, "declaredMethod: " + declaredMethod.getName());
                boolean value = annotation.value();
                Log.d(TAG, "annotation value: "+ value); }}// Find the annotation field
        Field[] declaredFields = testAnnotationsClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            FieldRuntimeAnnotation annotation = declaredField.getAnnotation(FieldRuntimeAnnotation.class);
            if(annotation ! =null) {
                Log.d(TAG, "declaredMethod: " + declaredField.getName());
                int value = annotation.value();
                Log.d(TAG, "annotation value: "+ value); }}}@FieldRuntimeAnnotation(1)
    public int mValue;


    @MethodRuntimeAnnotation(true)
    public void testMethodRuntimeAnnotation(a) {}/** * method annotation */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MethodRuntimeAnnotation {
        boolean value(a);
    }


    /** * field annotation */
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface FieldRuntimeAnnotation {
        int value(a);
    }


    /** * class, interface annotation */
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface TypeRuntimeAnnotation {
        int value(a); }}Copy the code