Java annotation understanding

A word about annotations first introduced into the official document: Java annotations are used to provide metadata for Java code. As metadata, annotations do not directly affect the execution of your code, but there are some types of annotations that can actually be used for this purpose. Java annotations have been added to Java since Java5. After reading this sentence, you may still be confused. Next, I will learn about annotations again from the definition of annotations, meta annotations, annotation attributes, custom annotations, and annotation analysis of the annotations provided by the JDK.

Definition of annotation

  • In the daily development of new Java classes, we use class and interface more, and annotations, like them, are also a type of class. The modifier used is @interface

How to write annotations

  • We create a new annotation MyTestAnnotation
public @interface MyTestAnnotation {

}
  • Then we can apply the annotation we just created on the class or method
@MyTestAnnotation
public class test {
   @MyTestAnnotation
   public static void main(String[] args){
   }
}
  • Above, we just learned how to write annotations, but we haven't written any code in the annotations we defined. Now this annotation is meaningless. How to make the annotation work? Next we move on to understand meta annotations.

Meta annotation

  • Meta-annotation, as its name implies, can be understood as an annotation of an annotation, which acts in an annotation to facilitate us to use an annotation to achieve the desired function. There are five types of meta annotations: @Retention, @Target, @Document, @Inherited, and @Repeatable (added in JDK1.8).

@Retention

  • The English meaning of Retention has the meaning of retention and retention. It means that the annotation existence stage is reserved in the source code (compilation period), bytecode (class loading) or runtime (running in the JVM). Use the enumeration RetentionPolicy in the @Retention annotation to indicate the annotation retention period
  • @Retention(RetentionPolicy.SOURCE), the annotation only exists in the source code, not included in the class bytecode file
  • @Retention(RetentionPolicy.CLASS), the default retention policy, annotations will exist in the class bytecode file, but cannot be obtained at runtime
  • @Retention(RetentionPolicy.RUNTIME), the annotation will exist in the class bytecode file and can be obtained through reflection at runtime
  • If we are custom annotations, through the previous analysis, our custom annotations will not work if they are only stored in the source code or bytecode file, and the annotations can be obtained during the runtime to achieve our purpose, so custom annotations @Retention(RetentionPolicy.RUNTIME) must be used in
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTestAnnotation {

}

@Target

  • Target means target in English, which is also very easy to understand. Using @Target element annotation to express the scope of our annotation is more specific. It can be a class, method, method parameter variable, etc., and it is also expressed through the enumeration class ElementType. Types of
  • @Target(ElementType.TYPE) function interface, class, enumeration, annotation
  • @Target(ElementType.FIELD) Function of attribute field and enumerated constant
  • @Target(ElementType.METHOD) function method
  • @Target(ElementType.PARAMETER) function method parameter
  • @Target(ElementType.CONSTRUCTOR) function constructor
  • @Target(ElementType.LOCAL_VARIABLE) acts on local variables
  • @Target(ElementType.ANNOTATION_TYPE) acts on annotations (@Retention annotations use this attribute)
  • @Target(ElementType.PACKAGE) acts on packages
  • @Target(ElementType.TYPE_PARAMETER) acts on type generics, namely generic methods, generic classes, generic interfaces (jdk1.8 added)
  • @Target(ElementType.TYPE_USE) type use. Can be used to annotate any type except class (jdk1.8 added)
  • Generally more commonly used is the ElementType.TYPE type
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyTestAnnotation {

}

@Documented

  • Document in English means document. Its function is to be able to include the elements in the annotations into the Javadoc.

@Inherited

  • Inherited means inheritance in English, but this inheritance is similar to what we usually understand. An annotation annotated by @Inherited modifies a parent class. If its subclass is not modified by other annotations, then its subclasses also inherit The annotation of the parent class.
  • Let's look at an example of @Inherited annotation
/**自定义注解*/
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyTestAnnotation {
}
/**父类标注自定义注解*/
@MyTestAnnotation
public class Father {
}
/**子类*/
public class Son extends Father {
}
/**测试子类获取父类自定义注解*/
public class test {
   public static void main(String[] args){

      //获取Son的class对象
       Class<Son> sonClass = Son.class;
      // 获取Son类上的注解MyTestAnnotation可以执行成功
      MyTestAnnotation annotation = sonClass.getAnnotation(MyTestAnnotation.class);
   }
}

@Repeatable

  • Repeatable in English means repeatable. As the name suggests, the annotation modified by this meta-annotation can act on an object multiple times at the same time, but each time the annotation is acted on can represent different meanings.
  • Let's look at an example of a person playing a game
/**一个人喜欢玩游戏,他喜欢玩英雄联盟,绝地求生,极品飞车,尘埃4等,则我们需要定义一个人的注解,他属性代表喜欢玩游戏集合,一个游戏注解,游戏属性代表游戏名称*/
/**玩家注解*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface People {
    Game[] value() ;
}
/**游戏注解*/
@Repeatable(People.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Game {
    String value() default "";
}
/**玩游戏类*/
@Game(value = "LOL")
@Game(value = "PUBG")
@Game(value = "NFS")
@Game(value = "Dirt4")
public class PlayGame {
}
  • Through the above example, you may have a question, what is the variable in the parentheses in the game comment, in fact, this corresponds to the attribute defined in the game comment. Next we continue to learn the attributes of annotations.

Annotated attributes

  • Through the @Repeatable annotation example in the previous section, we talked about the properties of annotations. The attributes of the annotations are actually similar to the variables defined in the class, except that the variables in the annotations are all member variables (attributes), and there are no methods in the annotations, only member variables, and the variable names are the corresponding parameters in the annotation brackets. Name, variable return value annotation corresponding parameter type in parentheses. I believe this will give you a deeper understanding of the above example. The variable type in the @Repeatable annotation is the generic Class corresponding to Annotation (interface).
/**注解Repeatable源码*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
    /**
     * Indicates the <em>containing annotation type</em> for the
     * repeatable annotation type.
     * @return the containing annotation type
     */
    Class<? extends Annotation> value();
}

The essence of annotation

  • The essence of annotation is an Annotation interface
/**Annotation接口源码*/
public interface Annotation {

    boolean equals(Object obj);

    int hashCode();

    Class<? extends Annotation> annotationType();
}
  • Through the above source code, we know that the annotation itself is a sub-interface of the Annotation interface, that is to say, the annotation can actually have attributes and methods, but the attributes in the interface are static final, which has no meaning for the annotation, and we define the interface The method is equivalent to the attribute of the annotation, which corresponds to the reason why the annotation only has the attribute member variable, in fact, it is the method of the interface, which is why the member variable has parentheses , which is different from the interface we can give in the parentheses of the annotation Member variable assignment.

Annotation attribute type

  • Annotation attribute types can have the types listed below
  • 1. Basic data types
  • 2.String
  • 3. Enumeration type
  • 4. Annotation type
  • 5.Class type
  • 6. One-dimensional array types of the above types

Annotate member variable assignment

  • If the annotation has multiple attributes, you can use "," in the annotation brackets to separate the corresponding attributes to assign values, as in the following example, the annotation assigns attributes in the parent class
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyTestAnnotation {
    String name() default "mao";
    int age() default 18;
}

@MyTestAnnotation(name = "father",age = 50)
public class Father {
}

Get annotation attributes

  • Earlier we talked about how to define a lot of annotations and where to put them. Now we can start to learn the extraction of annotation attributes. This is the key to using annotations. Obtaining the value of the attribute is the purpose of using annotations.
  • If you get the annotation attributes, of course it is reflection, there are three basic methods
/**是否存在对应 Annotation 对象*/
  public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
        return GenericDeclaration.super.isAnnotationPresent(annotationClass);
    }

 /**获取 Annotation 对象*/
    public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
        Objects.requireNonNull(annotationClass);

        return (A) annotationData().annotations.get(annotationClass);
    }
 /**获取所有 Annotation 对象数组*/   
 public Annotation[] getAnnotations() {
        return AnnotationParser.toArray(annotationData().annotations);
    }    

Combining the previous example, let’s get the annotation attributes. Before getting the annotations, we must use the meta annotation @Retention(RetentionPolicy.RUNTIME)

public class test {
   public static void main(String[] args) throws NoSuchMethodException {

        /**
         * 获取类注解属性
         */
        Class<Father> fatherClass = Father.class;
        boolean annotationPresent = fatherClass.isAnnotationPresent(MyTestAnnotation.class);
        if(annotationPresent){
            MyTestAnnotation annotation = fatherClass.getAnnotation(MyTestAnnotation.class);
            System.out.println(annotation.name());
            System.out.println(annotation.age());
        }

        /**
         * 获取方法注解属性
         */
        try {
            Field age = fatherClass.getDeclaredField("age");
            boolean annotationPresent1 = age.isAnnotationPresent(Age.class);
            if(annotationPresent1){
                Age annotation = age.getAnnotation(Age.class);
                System.out.println(annotation.value());
            }

            Method play = PlayGame.class.getDeclaredMethod("play");
            if (play!=null){
                People annotation2 = play.getAnnotation(People.class);
                Game[] value = annotation2.value();
                for (Game game : value) {
                    System.out.println(game.value());
                }
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
}

operation result:

Annotations provided by JDK

annotation effect Precautions
@Override It is used to describe that the current method is an overridden method, and the method is checked during the compilation phase In jdk1.5, it can only describe the rewriting in inheritance, and in jdk1.6, it can describe the rewriting of interface implementation and the rewriting of inheritance of classes.
@Deprecated It is used to describe the current method is an outdated method no
@SuppressWarnings Remove the warning in the program. no

Annotation and application

  • Now we look back again at the description of the official document at the beginning

Java annotations are used to provide metadata for Java code. As metadata, annotations do not directly affect the execution of your code, but there are some types of annotations that can actually be used for this purpose.

  • After our previous understanding, annotations are actually a very convenient thing. It's time to live and the area of ​​action can be easily set by you. It's just a matter of what you use annotations for.

Use annotations for parameter configuration

  • Let's look at an example of bank transfer. Assuming that the bank has a transfer service, the transfer limit may change according to changes in the exchange rate. We can use annotations to flexibly configure the transfer limit instead of modifying our business code every time.
/**定义限额注解*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface BankTransferMoney {
    double maxMoney() default 10000;
}
/**转账处理业务类*/
public class BankService {
    /**
     * @param money 转账金额
     */
    @BankTransferMoney(maxMoney = 15000)
    public static void TransferMoney(double money){
        System.out.println(processAnnotationMoney(money));

    }
    private static String processAnnotationMoney(double money) {
        try {
            Method transferMoney = BankService.class.getDeclaredMethod("TransferMoney",double.class);
            boolean annotationPresent = transferMoney.isAnnotationPresent(BankTransferMoney.class);
            if(annotationPresent){
                BankTransferMoney annotation = transferMoney.getAnnotation(BankTransferMoney.class);
                double l = annotation.maxMoney();
                if(money>l){
                   return "转账金额大于限额,转账失败";
                }else {
                    return"转账金额为:"+money+",转账成功";
                }
            }
        } catch ( NoSuchMethodException e) {
            e.printStackTrace();
        }
        return "转账处理失败";
    }
    public static void main(String[] args){
        TransferMoney(10000);
    }
}

operation result:

  • Through the above example, as long as the exchange rate changes, we can directly change the current maximum limit by changing the configuration value of the annotation.

Application of third-party framework

  • As an Android developer, the third-party frameworks ButterKnife, Retrofit2, Dagger2, etc. that we usually use have annotated applications. If we want to understand the principles of these frameworks, the basic knowledge of annotations is essential.

The role of annotations

  • Provide information to the compiler: The compiler can use annotations to detect errors or warnings and print out logs.
  • Processing during the compilation stage: Software tools can be used to automatically generate code, documents or other corresponding automatic processing using annotation information.
  • Run-time processing: Some annotations can accept code extraction when the program is running, and automatically perform corresponding operations.
  • As stated in the official document, annotations can provide metadata. In the transfer example, the process of obtaining annotation values ​​is the annotation extraction logic written directly by our developers. The code for processing extraction and processing of Annotation is collectively called APT (Annotation Processing Tool). ) . The processAnnotationMoney method in the above transfer example can be understood as an APT tool class.

Guess you like

Origin blog.csdn.net/sun124608666/article/details/112969537