In the past, it was very difficult to understand the various annotations of Spring. It was very difficult to memorize the annotations. I just knew that some classes and methods could be implemented by adding an @ to them and using a conventional value.

  • How do you add this annotation to perform the corresponding function? Who does it
  • Where did the notes come from
  • What kind of fun are annotations

Think impassable, also lazy to think

In a recent review of reflection and class object to deepen impression, after come back to see comments, there is a an Epiphany, a lot of underlying concepts it again just to be able to read, just like the old constipation suddenly open for many years, hence face ruddy, feet also seem quite, can knock on (off) the generation (head) code (send) this road go farther…

Let me just say a few things about what I’m going to talk about, just so you have numbers:

  • What are the general categories of annotations
  • How are annotations defined
  • How do I use custom annotations
  • How does the compiler perform annotations

Classification of annotations

Java built-in annotations

For example: @Override: This annotation stands for Override, but it doesn’t actually need to be added manually to the Override method. The compiler automatically adds @deprecated to indicate a Deprecated method or class when it is compiled. Turn off warnings about methods and classes and simply say, “I know there’s something wrong with the code, but don’t say it out loud.

Annotations provided by third-party frameworks

Spring defines a large number of annotations to make programming easier and more convenient. For example, ** @bean means that the target class needs to be registered in the Spring IOC container, which can be used at any time, @repository marks data access components, @controller marks control layer components, @before means Before method execution, @after ** means After method execution, etc. This type of annotation is also considered custom, but it is defined by the framework

Custom annotations

Annotations created by programmers during actual development to meet some aspect of the business design requirements, as long as they conform to the custom annotation specification.

Yuan notes

That’s the definition in a nutshellAnnotations of annotations are like classes describing classesAs with other types of annotations, you just need to know what they do and know how to use them, but to understand annotations thoroughly you need to start with meta-annotations. To create a custom annotation, a meta-annotation is essential. Here is a brief description of the rules for defining meta-annotations. After understanding the classification of annotations above, the practical use of annotations is mainly divided into three steps:

① Define annotations – “② Use annotations -” ③ parse annotations

How are annotations defined

There is a **@interface syntax that tells the editor that this is an annotation, and then there are two meta annotations: @target and @Retention**

@Target

The same annotation can not be used everywhere. Methods, attributes, and constructors are different. Target specifies where the annotation can be used.

  • Class or interface:ElementType.TYPE
  • Fields:ElementType.FIELD
  • Methods:ElementType.METHOD
  • Construction method:ElementType.CONSTRUCTOR
  • Method parameters:ElementType.PARAMETER

You can’t mess with the location or it won’t pass at compile time

@target ({elementtype. METHOD, elementtype. ANNOTATION_TYPE}) allows the {} symbol to be omitted if it is a single object

@Retention

Defines the life cycle of an annotation. The default is retentionPolicy.class:

  • Compile time only:RetentionPolicy.SOURCEWill not be compiled into a class file by the JVM
  • Class files only:RetentionPolicy.CLASSValid in a class file
  • Runtime:RetentionPolicy.RUNTIMERuntime validity

Some people have no idea how to compile and load a class file because they don’t know the class object.A Java file at compile time becomes a. Class files, class loader loads. The class file, parse into memory (method), the method of zone will store all the information about this class, subsequent instance objects, obtain object constructor member variables must be according to the method in the class information (reflection), There is only one class object for a classIn the picture, it looks something like this: Annotations are another kind, inherited fromjava.lang.annotation.Annotation, so it will also be compiled into the class file, take the shortest life cycle aboveRetentionPolicy.SOURCEThis means that the Annotation is removed by the Annotation Processor ** before it becomes a class file, which means that it will not be compiled into the class file. The second shortest life cycleRetentionPolicy.ClassWill be compiled into the class file, but the type of annotations will be discarded after loading, andRetentionPolicy.RUNTIMENot only will it be compiled into a class file, but it will also be retained after loading, reflecting and reading corresponding methods and variables at runtime.

So the lifecycle size is: retentionPolicy.source (compiled) < retentionPolicy.class (classloaded) < retentionPolicy.runtime (run)

Retentionpolicy.runtime is the most appropriate method to perform operations on a class object that needs to be parsed at RUNTIME. .

Other meta-annotations

There are a few other meta-annotations that I think I can skip because they’re not really used:

  • Repeatable @repeatable custom annotations are Repeatable, which means that after adding this meta annotation, multiple custom annotations can be added to the same method (for example)
  • When a child inherits an annotation defined by its parent, it automatically inherits the annotation
  • Documented includes elements from an annotation into a Javadoc, which is used to generate Javadoc

Attributes of annotations

Having said the basic definition of annotations, read on:In the actual development, you can see that there are many parameters in the annotations defined by the third party framework. How do these attribute values be defined? Define the format of attribute values:

@Target({ElementType.METHOD,ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SignCheck {
    String value(a) default"La la la la";
}
Copy the code

It’s a bit strange to define an argument followed by (). Isn’t that the format of the method? String value() is just a method to be implemented in an interface, and is used in real applications such as reflection to get information about an annotation object:

SignCheck SignCheck = [Class object]. GetAnnotation (signcheck.class);// Get the direct annotation object by reflection
Copy the code

A subclass object that implements the annotation interface is generated in memory,This return looks pretty straightforward:

// The following code is not actually generated
public class SignCheckImpl implements SignCheck{
    public String value(a){
        returnThe value assigned to the annotation; }}Copy the code

If a custom annotation defines another attribute value called value(), then the annotation can be used without this attribute value. However, if more than one attribute value is defined, the corresponding value must be specified:

@SignCheck("lala") SignCheck(value = "lala")
public void annotationtest(a){... }Copy the code

Annotation attribute values support many types. In addition to String, there are eight basic data types, including Class, enumerated classes, and even annotations. The above custom annotations are extended below:

@Target({ElementType.METHOD,ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SignCheck {

    // Basic data type
    int iValue(a);
    double douValue(a);
    long lonValue(a);
    float floValue(a);
    char chValue(a);
    boolean booValue(a);
    short shoValue(a);
    byte byteValue(a);

    // Char is a string
    String stringValue(a);

    / / comment
    ExampleAnnoation annocationValue(a);

    //ClassClass<? > classValue();/ / the enumeration classes
    WeekEnum enumValue(a);

    // There are also some arrays. Here is an example
    int[] iListValue();

}

enum WeekEnum{
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    Sunday
}

@interface ExampleAnnoation{

}
Copy the code

How do I use custom annotations

This is not new, because it is already in use, just add annotations and variables in the right place (target specified class/method/variable, etc.), such as aboveSignCheckAnnotations can be used on methods and classes, so let’s find a random method, add annotations to its header and declare variables:The assignment looks like this:

SignCheck(iValue = 1, douValue = 1.0d, lonValue = 4124L, floValue = 451f, chValue = 'H', booValue = true, ShoValue = 2, byteValue = 120, stringValue = "This is a string ", annocationValue = @exampleannoation, Class,// a random class object enumValue = WeekEnum.Wednesday, iListValue = {1,2,3,4,5})
public void annotationtest(a){}Copy the code

How are annotations parsed

Annotations are defined and used on corresponding methods, but not actually on the annotated programAny impact onIt’s just onetagSince custom annotations are read and executed by the JVM at RUNTIME (retentionPolicy.runtime), they are now defined.There is no code to explain to the JVMthis@SignCheckWhat is the role of annotations? So the first definition annotation is just a half-finished product, the next one ishighlightAnd the most difficult part of moxin 🙁Class objects again. I’ve covered this in reflection, polymorphism, and dynamic proxies, and it’s so damn important.) The Class sourceWill be found inLine 2464Has an internal private classReflectionData(Reflected data)There is aFieldField class instance,MethodMethod class instance,ConstructorConstructor class instances and interface class instances, each attribute is set to describe a class, and these attributes can be used to bypass the object to get the class’s variables/methods/constructors, etcAnd in theFieldMethodConstructorYou can find one calledgetAnnotation()Reflection to obtain annotation information, but of course this is not the only method in use: You can pass if you knowReflection retrieves custom annotation information for methods, variables, classes, and so on corresponding to instances, then the following is easy to do:

Use reflection to retrieve annotation information

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RunBefore {
    String run_name(a) default"This is the default value";
}
Copy the code
public class AnnocationTest {

    public static void main(String[] args) throws NoSuchMethodException {
        Class<AnnocationTest> annocationTestClass = AnnocationTest.class;
        // Reflection gets the testAnnocation Method
        Method method = annocationTestClass.getMethod("testAnnocation");

        // Determine whether an annotation object exists
        boolean isExist = method.isAnnotationPresent(RunBefore.class);
        System.out.println("Is there a target annotation:" + isExist);

        // Reflection gets the target annotation object
        RunBefore runBefore = method.getAnnotation(RunBefore.class);
        System.out.println("Annotation object properties:" + runBefore.run_name());
    }

    @runbefore (run_name = "test annotation run ")
    public void testAnnocation(a) {}}Copy the code

Output:

Is there a target annotation: true Annotation object property: Tests annotation run

You can see from the above statement: ** All custom annotation classes inheritAnnotation**, why do you say that? Take a look at the source code below:

// Reflection gets the target annotation object (received with the custom annotation RunBefore)
RunBefore runBefore = method.getAnnotation(RunBefore.class);
Copy the code

The argument returned by the getAnnotation method requires the Annotation class to inherit the Annotation class, and the actual test code shows that the Annotation classes inherit the Annotation class directly with the RunBefore instance. Does that mean that Annotation classes inherit the Annotation class? Further, annotations are also a class, with class properties, right?

Verify the reflection

To justify setting @Retention to retentionPolice.runtime so that the annotation class can be loaded into memory and be loaded and reflected by the JVM at RUNTIME, we change it to@Retention(RetentionPolicy.CLASS)And see what goes wrong:The programmer failed to find the annotation instance through reflection while running.Life cycle RetentionPolicy.CLASS does not load the annotation CLASS into runtime memory, but only into CLASS bytecode at compile time.

Multiple annotation fetching

The testannocations Method () Constructor Constructor API () is a testannocations Method (). The testannocations Method () is a testannocations Method ().

public void testAnnocation(@NotNull @Range(minlength = 5,maxlength = 10) String name
    ,@CanBeNull int age) {}Copy the code

If a method contains multiple parameters and a parameter contains multiple annotations (this is also the case with constructors), you need to define one2 d arrayTo receive this reflection data. There’s agetParameterAnnotationsMethod, as shown below:That should be received like this:

Class<AnnocationTest> annocationTestClass = AnnocationTest.class;
// Reflection gets the testAnnocation method
Method method = annocationTestClass.getMethod("testAnnocation", String.class, int.class);
// Get the Annotation for all parameters:
Annotation[][] annos = method.getParameterAnnotations();
for (Annotation[] anno : annos) {
    for (Annotation annotation : anno) {
        if (annotation instanceof StringRange) {
            / / StringRange annotation
            System.out.println("StringRange notes found.");
            System.out.println("minlength:" + ((StringRange) annotation).minlength());
            System.out.println("maxlength:" + ((StringRange) annotation).maxlength());
        }
        if (annotation instanceof NotNull) {
            / / NotNull annotation
            System.out.println("No null annotation found.");
        }
        if (annotation instanceof CanBeNull) {
            / / CanBeNull annotation
            System.out.println("CanBeNull annotation found."); }}}Copy the code

Double traversal, first traverses the param parameter of method, then traverses the annotation of params parameter, according to the type of return judgment to find the corresponding annotation and output value:

Find the NotNull annotation find the StringRange annotation minLength :5 MaxLength :10 Find the CanBeNull annotation

Go ahead and code a custom annotation

Suppose we define @agerange as an annotation to restrict method inputs:

@Target({ElementType.PARAMETER}) // Notice this ElementType
@Retention(RetentionPolicy.RUNTIME)
public @interface AgeRange {
    int minValue(a) default 1;// Minimum age 1
    int maxValue(a) default 99;// Maximum age 99
}
Copy the code

There is then a method in the AnnocationTest class that uses this annotation:

public void outputAge(@AgeRange int age) {
    System.out.println("Executive processing age" + age + "Normal business logic :");
}
Copy the code

Write a custom method to read annotations and process them:

public void checkAnnocation(int age) throws Exception {
    Class<AnnocationTest> annocationTestClass = AnnocationTest.class;
    // Reflection gets the testAnnocation method
    Method method = annocationTestClass.getMethod("outputAge".int.class);

    // Reflection gets the target annotation object
    Annotation[][] annotations = method.getParameterAnnotations();

    for (Annotation[] anno : annotations) {
        for (Annotation annotation : anno) {
            if (annotation instanceof AgeRange) {
                System.out.println("Found the AgeRange note... . Start parsing.");
                AgeRange ageRange = (AgeRange)annotation;
                int minValue = ageRange.minValue();
                int maxValue = ageRange.maxValue();
                if (age < ((AgeRange) annotation).minValue()
                    || age > ((AgeRange) annotation).maxValue()) {
                    System.out.println("Non-conforming age should be at + minValue + "And" + maxValue + ", the current value is: + age);
                    // You can define a custom exception, global capture, unified handling and return
                    throw newMyException(ExceptionEntity.UNVAILD_METHOD_PARAMS); }}// Other annotations can be extended here}}}Copy the code

So if you’re callingoutputAgeMethod is executed by the JVMcheckAnnocationThe validation method yields the following result:In the red box is the verification step. I believe you will have two questions when you look at this, which are also missed by most of the cookie-paste annotations on the Internet:

  • checkAnnocationWhy do methods need to pass an age parameter? Can’t we dynamically get the @agerange age value by reflection
  • Why is it necessaryExecute it explicitlyReflection annotation methodcheckAnnocationTo verify, add annotations why notSystem automatic execution

First question: Method parameters (such as type and name) are static. The parameters of a method appear when the method is called, but you can do a dynamic proxy to retrieve the values of these parameters. For example, the JDK dynamic proxy implementation InvocationHandler interface has a core method invoke:

/** * dynamic_proxy: the real proxy object of the proxy class com.sun.dynamic_proxy.$Proxy0 * method: the method object of the real method of the object we want to call * args: refers to the parameters passed by the method of the proxy object */ public Object invoke(Object proxy, Method method, Object[] args) { ... }Copy the code

Object[] args is the method parameter passed by the proxy Object. We can do some work on this args. The idea is to create a proxy Object, This object contains all the methods (including parameters) of the AnnocationTest object, and the invoke method performs custom annotation validation on the parameter value of the proxy object, and throws an exception if it fails. If you don’t understand JDK dynamic proxies, just read this article:

Full text 7000 words, from shallow to deep detailed explanation of JDK dynamic proxy

Second question: why is the custom annotation not automatically executed, we need to manually write a reflection method to fetch, like the Spring framework annotations do not have such code? As mentioned above, annotations are just labels for Java code and have no effect. Custom annotations are only useful when the custom methods on them are parsed and executed.

So an annotation is an implementation, not a definition, which is just a label for the program. ** In spring projects, this execution process can be put in interceptors, put in AOP, and the Spring container finds these custom annotations and executes them automatically. How spring finds these custom annotations and how it parses and executes them will be covered next time, probably with the source code. And there are some concepts that I haven’t quite figured out yet, so that’s the end of the annotations.