JAVA06--Annotation

Annotation An
annotation is a special kind of "comment" placed before the classes, methods, fields, and parameters of the Java source code:

// this is a component:@Resource("hello")public class Hello {
    @Inject
    int n;

    @PostConstruct
    public void hello(@Param String name) {
        System.out.println(name);
    }

    @Override
    public String toString() {
        return "Hello";
    }
}

The comment will be ignored by the compiler directly, and the comment can be packaged into the class file by the compiler. Therefore, the comment is a kind of "metadata" used as a label.

The role of annotations

From the perspective of the JVM, the annotation itself has no effect on the code logic, and how to use the annotation is entirely determined by the tool.
Java annotations can be divided into three categories: the
first category is annotations used by the compiler, for example:

  • @Override: Let the compiler check whether the method correctly implements the override;
  • @SuppressWarnings: Tell the compiler to ignore the warnings generated by the code here.
    Such annotations will not be compiled into the .class file, they will be thrown away by the compiler after compilation.
    The second category is the annotations used by tools to process .class files. For example, some tools will dynamically modify the class when loading the class to implement some special functions. Such annotations will be compiled into the .class file, but will not exist in memory after loading. Such annotations are only used by some low-level libraries, generally we don't have to deal with them by ourselves.
    The third type is the annotations that can be read during the program runtime. They are always present in the JVM after loading, which is also the most commonly used annotation. For example, a method configured with @PostConstruct will be automatically called after calling the constructor (this is the function implemented by Java code reading the annotation, and the JVM will not recognize the annotation).
    When defining an annotation, you can also define configuration parameters. Configuration parameters can include:
  • All basic types;
  • String;
  • Enumeration type
  • Array of basic types, String and enumeration.
    Because the configuration parameters must be constants, the above restrictions ensure that the value of each parameter is already determined when the annotation is defined.
    Annotated configuration parameters can have default values. When a configuration parameter is missing, the default value will be used.
    In addition, most of the annotations will have a configuration parameter called value. To assign a value to this parameter, you can just write a constant, which is equivalent to omitting the value parameter.
    If you only write comments, it is equivalent to using the default values.
public class Hello {
    @Check(min=0, max=100, value=55)
    public int n;

    @Check(value=99)
    public int p;

    @Check(99) // @Check(value=99)
    public int x;

    @Check
    public int y;
}

@Check is an annotation. The first @Check (min = 0, max = 100, value = 55) clearly defines three parameters, and the second @Check (value = 99) only defines one value parameter, which is actually the same as @Check (99 ) Is exactly the same. The last @Check means that all parameters use default values.

Definition annotation

The Java language uses the @interface syntax to define annotations. Its format is as follows:

public @interface Report {
    int type() default 0;
    String level() default "info";
    String value() default "";
}

The annotated parameters are similar to the parameterless method, you can use default to set a default value (strongly recommended). The most commonly used parameter should be named value.

Meta-annotation

There are some annotations that can modify other annotations. These annotations are called meta annotations. The Java standard library has defined some meta-annotations, we only need to use meta-annotations, usually do not need to write meta-annotations.

@Target

The most commonly used meta-annotation is @Target. Use @Target to define where Annotation can be applied to the source code:

  • Class or interface: ElementType.TYPE;
  • Field: ElementType.FIELD;
  • Method: ElementType.METHOD;
  • Construction method: ElementType.CONSTRUCTOR;
  • Method parameters: ElementType.PARAMETER.
    For example, to define the annotation @Report can be used on the method, you must add a @Target (ElementType.METHOD):
@Target(ElementType.METHOD)public @interface Report {
    int type() default 0;
    String level() default "info";
    String value() default "";
}

The definition annotation @Report can be used on a method or field, and the @Target annotation parameter can be turned into an array {ElementType.METHOD, ElementType.FIELD}:

@Target({
    ElementType.METHOD,
    ElementType.FIELD
})public @interface Report {
    ...
}

In fact, the value defined by @Target is an ElementType [] array. When there is only one element, the writing of the array can be omitted.

@Retention

Another important meta-annotation @Retention defines the life cycle of Annotation:

  • Compile only: RetentionPolicy.SOURCE;
  • Class files only: RetentionPolicy.CLASS;
  • Runtime: RetentionPolicy.RUNTIME.
    If @Retention does not exist, the Annotation defaults to CLASS. Because the custom Annotation is usually RUNTIME, it is necessary to add the meta annotation @Retention (RetentionPolicy.RUNTIME):
@Retention(RetentionPolicy.RUNTIME)public @interface Report {
    int type() default 0;
    String level() default "info";
    String value() default "";
}

@Inherited

Use @Inherited to define whether the subclass can inherit the Annotation defined by the parent class. @Inherited is only valid for @Target (ElementType.TYPE) type annotation, and only for class inheritance, not for interface inheritance:

@Inherited@Target(ElementType.TYPE)public @interface Report {
    int type() default 0;
    String level() default "info";
    String value() default "";
}

In use, if a class uses @Report:
@Report (type = 1) public class Person {
}
then its subclass also defines this annotation by default:
public class Student extends Person {
}

How to define Annotation

Summarize the steps to define Annotation: The
first step is to define annotations with @interface:
public @interface Report {
} The
second step is to add parameters and default values:
public @interface Report {
int type () default 0;
String level () default "Info";
String value () default "";
}
The most commonly used parameter is defined as value (). It is recommended to set the default value for all parameters as much as possible.
The third step is to configure annotations with meta-annotations:
@Target (ElementType.TYPE) @Retention (RetentionPolicy.RUNTIME) public @interface Report {
int type () default 0;
String level () default “info”;
String value () default "";
}
Among them, you must set @Target and @Retention, @Retention is generally set to RUNTIME, because custom annotations usually require reading at runtime. In general, you do not have to write @Inherited and @Repeatable.

Processing annotations
Java annotations themselves have no effect on the code logic. According to the configuration of @Retention:

  • SOURCE type annotations are discarded at compile time;
  • CLASS type annotations are only saved in the class file, they will not be loaded into the JVM;
  • Annotations of type RUNTIME are loaded into the JVM and can be read by the program during runtime.
    How to use annotations is completely determined by the tool. SOURCE type annotations are mainly used by compilers, so generally only used, not written. CLASS type annotations are mainly used by the underlying tool library, which involves class loading, and are rarely used. Only RUNTIME type annotations are not only used, but often need to be written.
    Therefore, only discuss how to read RUNTIME type annotations.
    Because annotations are also a class after definition, all annotations are inherited from java.lang.annotation.Annotation, so to read annotations, you need to use the reflection API.
    The method provided by Java to read Annotation using reflection API includes:
    judging whether a certain annotation exists in Class, Field, Method or Constructor:
  • Class.isAnnotationPresent(Class)
  • Field.isAnnotationPresent(Class)
  • Method.isAnnotationPresent(Class)
  • Constructor.isAnnotationPresent (Class)
    uses reflection API to read Annotation:
  • Class.getAnnotation(Class)
  • Field.getAnnotation(Class)
  • Method.getAnnotation(Class)
  • Constructor.getAnnotation (Class)
    Using annotations
    How to use annotations is entirely determined by the program. For example, JUnit is a testing framework that automatically runs all methods marked @Test.

Let's look at a @Range annotation and use it to define the rules of a String field: the field length meets the parameter definition of @Range:

@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface Range {
    int min() default 0;
    int max() default 255;
}

In a JavaBean, you can use this annotation:

public class Person {
    @Range(min=1, max=20)
    public String name;

    @Range(max=10)
    public String city;
}

However, the annotations are defined and have no effect on the program logic itself. You must write your own code to use annotations. Here, write a check method for the Person instance, which can check whether the String field length of the Person instance meets the definition of @Range:

void check(Person person) throws IllegalArgumentException, ReflectiveOperationException {
    // 遍历所有Field:
    for (Field field : person.getClass().getFields()) {
        // 获取Field定义的@Range:
        Range range = field.getAnnotation(Range.class);
        // 如果@Range存在:
        if (range != null) {
            // 获取Field的值:
            Object value = field.get(person);
            // 如果值是String:
            if (value instanceof String) {
                String s = (String) value;
                // 判断值是否满足@Range的min/max:
                if (s.length() < range.min() || s.length() > range.max()) {
                    throw new IllegalArgumentException("Invalid field: " + field.getName());
                }
            }
        }
    }
}

Through @Range annotation, with the check () method, you can complete the inspection of the Person instance. Note that the inspection logic is completely written by yourself, and the JVM does not automatically add any additional logic to the annotation.

Published 23 original articles · praised 7 · 1002 views

Guess you like

Origin blog.csdn.net/qq_34356768/article/details/105184996