We are all familiar with annotations, because the first annotation for beginners is @override, which identifies overloaded methods. Annotations are ubiquitous in Java EE development. For example, the classic MVC design pattern uses at least four annotations: @Component, @Repository, @Service and @Controller. Now the question is, why learn annotations? What are its advantages? What problems can it solve? Through reading this article, I believe readers will have a clearer understanding.

An example that comes up all the time

In Java Web development, XML was first used for configuration. For example, when a developer defines the LoginServlet class in a Servlet, the next step would be to add an access map to the LoginServlet class in the web.xml configuration file, perhaps using the following code:

  <servlet>
    <servlet-name>LoginServlet</servlet-name>
    <servlet-class>com.envy.servlet.LoginServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>LoginServlet</servlet-name>
    <url-pattern>/LoginServlet</url-pattern>
  </servlet-mapping>
Copy the code

So when the user access link, such as http://localhost:8080/LoginServlet will be executed this class methods defined in the logic. However, some people think this configuration is too troublesome, so a new configuration method is proposed: Adding the @webServlet annotation to the LoginServlet class with the attribute value value=”/LoginServlet” does the same thing. It is undoubtedly more convenient to use the latter annotation, which is the hero of the day: annotations.

annotations

What are annotations?

Java annotations, introduced in Java1.5, are used to describe meta information about Java code. Annotations do not directly affect code execution, but certain annotations can be used to influence code execution. Beginners may be confused by the phrase, what is meta-information? One moment it does not affect the execution of the code, the next moment it can. In fact, don’t worry, I believe that through the study of this article, you will have a new understanding of this sentence.

Annotations are like special tags in code that can be read at compile time, load time, run time, and execute the corresponding logic.

Definition of annotations

Annotations have not been around as long as classes and interfaces have, but they have grown in stature since their inception. Annotations are also a type, so they deserve to be remembered, along with classes and interfaces.

It’s easy to define an annotation using the @interface keyword declaration. For example, here’s a developer’s own @myAnnotation:

public @interface MyAnnotation {
}
Copy the code

Does it look very similar to an interface, just adding an @ sign in front of it? So how is this annotation used? Define a Hello class and add a custom @MyAnnotation to the Hello class. This completes the simplest annotation use case.

@MyAnnotation
public class Hello {
}
Copy the code

** Since a custom @myAnnotation does not contain any member variables inside it, it is called a tag annotation, and the most common tag annotation is @overried. ** But in practice, member variables need to be defined inside annotations, so there is another concept to learn: meta-annotations. A meta-annotation is an annotation that annotates an annotation and is used to explain the role of the annotation being acted on, so it is a very basic annotation.

Six meta-annotations are defined under the JAVa.lang. annotation package of the JDK: @Documented, @Inherited, @native, @REPEATable, @Retention, and @Target, as shown in the following figure:

The purpose of these six meta-annotations will be explained in turn, and understanding and familiarity with them will be of great help in improving your programming skills.

metadata

But first, take a look at annotations with member variables, commonly known as metadata. To define a member variable in an annotation, the syntax is very similar to that of a method declaration:

public @interface MyAnnotation {
    String username();
    String password();
}
Copy the code

This adds two member variables, username and Password, to our custom MyAnnotation annotation. Note that member variables defined on annotations can only be Strings, arrays, classes, enumerated classes, and annotations.

Member variables are defined in annotations to carry information, and typically in XML they may contain child tags within a tag:

< blog > < author > Yu Si </ author > < website >www.envyzhan.club</ website > </ blog >Copy the code

Child tags are information, and the member variables defined here in the annotation are really child tags.

However, since the member variable defined in the @myAnnotation annotation above does not have a default value, we need to add a default value when we use it:

 @MyAnnotation(username = "envy",password = "1234")
    public void test(){
    }
Copy the code

When an annotation is declared, it is usually given a default value for its member variables to facilitate subsequent use:

public @interface MyAnnotation {
    String username() default "envy";
    String password() default "1234";
}
Copy the code

This allows you to modify the default value of annotations as required:

@MyAnnotation()
public void test(){
}
Copy the code

Note that there is a special case: ** If there is only one member attribute in an annotation and the attribute value is value, the developer can declare the annotation without giving it a default value, and use the annotation without specifying the value attribute. ** For example, there is now an annotation @myAnnotation that has an internal structure of:

public @interface MyAnnotation {
    String value();
}
Copy the code

Then the use can be carried out in the following way:

@MyAnnotation("envy")
    public void test(){
    }
Copy the code

Note that it’s @myAnnotation (“envy”). You can use the generic @myAnnotation (value=”envy”) notation, but it’s not recommended because it’s a syntactic sugar. Does anyone think it’s ok to have only one member attribute in an annotation? Looking down, there is an @OneAnnotation defined, which has an internal structure of:

public @interface OneAnnotation {
    String name();
}
Copy the code

If the value of the attribute is “value”, the value of the attribute is “value”.

Built-in 7 meta annotations

@ Documented annotation

Start by looking at the source information for the @documented annotation, as follows:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
Copy the code

The @documented annotation is an annotation that is used to modify the annotation. An annotation modified by this annotation can be Documented by a tool like Javadoc. Note that this annotation only takes care of the mark and has no member value. Typically javadoc does not include annotations, but when an annotation is Documented, it will be processed by the Javadoc tool, and the annotation type information will then reside in the generated document.

So for example, if you have a custom annotation @myAnnotation, now try to add @documented,

@Documented
public @interface MyAnnotation {

}
Copy the code

Then create a new hello. Java file and use the annotation on the class:

@MyAnnotation
public class Hello {
}
Copy the code

Next, go to the DOS command line, switch to the folder where the annotation is located, and execute javadoc -d doc *.java, which uses the Javadoc tool to generate a description document for all Java files in that directory. After running this command, you can find a doc directory in your current directory. Go to this directory and open the index.html file in your browser:

You can see that a description of the annotation type appears in this document (annotations are actually a type, called annotation type, but generally referred to as annotations).

Now try to delete the doc directory and remove the @documented annotation on the MyAnnotation annotation and run the following command. The generated index.html file has no indication of the annotation type:

And that’s how you use @documented. Go to the next annotation.

@ Native annotations

First take a look at the source information for the @native annotation, as follows:

@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface Native {
}
Copy the code

As you can see, the @native annotation was introduced from JDK1.8. It is used to comment that this field is a constant and its value references Native code. You can see that its retention time is the SOURCE stage, which is not used very much.

@ Target annotation

First take a look at the source information for the @target annotation, as follows:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}
Copy the code

You can see that the @target annotation, introduced in JDK1.5, is used to specify the scope of the modified annotation, that is, to describe where the modified annotation can be used. Defined in the ElementType enumeration class, there are 10 policies (the first 8 existed before 1.8 and the last 2 have been added in JDK1.8) :

Enumerated constants Annotation scope
TYPE Classes, interfaces (including annotation types), and enumerations
FIELD Fields (including enumeration constants)
METHOD methods
PARAMETER Formal parameters
CONSTRUCTOR The constructor
LOCAL_VARIABLE A local variable
ANNOTATION_TYPE Annotation type
PACKAGE package
TYPE_PARAMETER (introduced in JDk1.8) Type parameters
TYPE_USE (introduced in JDk1.8) Use the type

Note that PACKAGE is not used in a generic class, but in a fixed package-info.java file. Note that the name must be package-info and cannot be changed. For example, create a new WeAnnotation annotation with the following code:

@Target({ElementType.PACKAGE,ElementType.METHOD})
@Documented
public @interface WeAnnotation {
}
Copy the code

Create a package-info.java file. Note that the package-info.java file must be created in a similar way to the file creation, otherwise IDEA will not be allowed to create:

@WeAnnotation
package com.envy.annotation;
Copy the code

This is a successful use of the annotation. Note that the member variable defined in the @target annotation is an array, so it must be of the form @target ({elementtype.package}) :

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}
Copy the code

@ Retention annotations

First take a look at the @Retention annotation’s source information, as follows:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    RetentionPolicy value();
}
Copy the code

As you can see, the @Retention annotation was introduced in JDK1.5 to indicate the Retention time of the modified annotations. There are three policies defined in the RetentionPolicy enumeration CLASS: SOURCE, CLASS, and RUNTIME.

@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { /** *  Returns the retention policy. * @return the retention policy
     */
    RetentionPolicy value();
}
Copy the code

SOURCE, as the name implies, is the SOURCE stage, this annotation only exists in the.Java SOURCE file, when. When a Java file is compiled into a. Class bytecode file, the annotation is discarded and ignored by the compiler. CLASS, as the name implies, is the bytecode phase, and this annotation is reserved for the.class bytecode file, which is discarded when it is loaded by the JVM, which is the default lifecycle. RUNTIME annotations are not only saved to the.class bytecode file, but they still exist after being loaded by the JVM, so there is a very powerful technique called reflection.

The three life cycles correspond to three phases:.java source files >.class bytecode files > in-memory bytecode. SOURCE< CLASS< RUNTIME = SOURCE< CLASS< RUNTIME = SOURCE< CLASS< RUNTIME

In general, if developers want to do some checking, such as method overloading, disallow compiler warnings, etc., they can choose the SOURCE lifecycle. If you want to do some pre-processing at compile time, such as generating some auxiliary code, you can choose the CLASS life cycle. If you want to dynamically retrieve annotation information at RUNTIME, you must select the RUNTIME life cycle.

** Reflection technology demo demonstrates the use of @Retention and @target annotations. ** The reflection technique mentioned above is to get information about a Class dynamically at runtime. In this article, we introduce how to use @Retention and @target annotations to get more information about a Class, such as Class, Field, and Method.

** Step 1, create 3 custom annotations. ** Create a new MyAnnotation package and create three custom runtime annotations with Class, Field, and Method in the same order:

//ClassAnno.java
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface ClassAnno {
    String value();
}

//MethodAnno.java
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodAnno {
    String name() default "envy";
    String data();
    int age() default 22;
}

//FieldAnno.java
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface FieldAnno {
    int[] value();
}
Copy the code

** Step 2, create a new test class RunTimeTest. ** Create a new RunTimeTest test class with the following code:

@ClassAnno("RunTime")
public class RunTimeTest {
    @FieldAnno(value = {66,88})
    public String word = "hello";
    @FieldAnno(value = {1024})
    public int number = 2018;
    @MethodAnno(data = "good",name="envy", age = 22)
    public static void sayHello(){
        System.out.println("this is sayHello method."); }}Copy the code

** Step 3: Get internal information for annotations. ** Create a new GetRunTimeTest class to get the internal information of the annotation at runtime.

public class GetRunTimeTest {
    public static void outInfo(){ StringBuilder builder = new StringBuilder(); Class<? > aClass = RunTimeTest.class; /** Get ClassAnno comment */ builder.append("ClassAnno notes:").append("\n");
        ClassAnno classAnno = aClass.getAnnotation(ClassAnno.class);
        if(classAnno! =null){ builder.append(Modifier.toString(aClass.getModifiers())).append("")
                    .append(aClass.getSimpleName()).append("\n");
            builder.append("Comment values are:").append(classAnno.value()).append("\n\n"); } /** Get MethodAnno annotation information */ builder.append("MethodAnno note:").append("\n");
        Method[] methods = aClass.getDeclaredMethods();
        for(Method method:methods){
            MethodAnno methodAnno = method.getAnnotation(MethodAnno.class);
            if(methodAnno ! =null){ builder.append(Modifier.toString(method.getModifiers())).append("")
                        .append(method.getReturnType().getSimpleName()).append("")
                        .append(method.getName()).append("\n");
                builder.append("Comment values are:").append("\n");
                builder.append("name--->").append(methodAnno.name()).append("\n");
                builder.append("data--->").append(methodAnno.data()).append("\n");
                builder.append("age--->").append(methodAnno.age()).append("\n\n"); }} /** Get FieldAnno annotation information */ builder.append("Notes FieldAnno:").append("\n");
        Field[] fields = aClass.getDeclaredFields();
        for(Field field:fields){
            FieldAnno fieldAnno = field.getAnnotation(FieldAnno.class);
            if(fieldAnno ! = null){ builder.append(Modifier.toString(field.getModifiers())).append("")
                        .append(field.getType().getSimpleName()).append("")
                        .append(field.getName()).append("\n");
                builder.append("Comment values are:").append(Arrays.toString(fieldAnno.value())).append("\n\n"); } } System.out.println(builder.toString()); } public static void main(String[] args){ outInfo(); }}Copy the code

After running the method, the result is as follows:

Static void sayHello public RunTimeTest RunTime MethodAnno public static void sayHello Name -->envy data-- >good age-- >22 FieldAnno: public String word: [66, 88] public int numberCopy the code

You can see that using reflection, you successfully get the information in the annotation dynamically at runtime. In fact, the @Autowried annotation in the Spring framework does just that, injecting the beans needed at runtime into the Spring container for subsequent program calls.

@ Inherited annotations

First, look at the @Inherited source information, as follows:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}
Copy the code

As you can see, the @Inherited annotation was introduced in JDK1.5. As the name implies, the @Inherited annotation allows a child class to inherit annotations from its parent. For example, if you modify the custom @Heyannotation annotation with the @Inherited annotation, if the custom @Heyannotation annotation modifs a Book class and another TechBook class inherits the Book class, By default, the TechBook class will also have your own custom @heyannotation annotation.

The code in the Heyannotations.java annotation:

@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface HeyAnnotation {
}
Copy the code

Note that the subsequent reflection is used to determine if the @heyannotation annotation exists in a subclass. The default Retention time for annotations is CLASS, which prevents the annotation from entering RUNTIME, so @retention (retentionPolicy.runtime) must be added. Here is the code in the book.java file:

@HeyAnnotation
public class Book {
}
Copy the code

Then there is the code in the Book subclass TechBook class:

public class TechBook extends Book { public static void main(String[] args){ System.out.println(TechBook.class.isAnnotationPresent(HeyAnnotation.class)); }}Copy the code

You can then run the main method and see that the output is true.

@ the Repeatable annotations

First take a look at the source information for the @REPEATable annotation as follows:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
    Class<? extends Annotation> value();
}
Copy the code

As you can see, the @REPEATable annotation was introduced from JDK1.8. Repeatable means Repeatable, so annotations modified by this annotation can be applied to the same statement or type multiple times. So what annotations can be used more than once? Of course, the value of the annotation can be multiple.

For example, a man may be a father, a son, or a husband. First define a @persons annotation:

public @interface Persons {
    Person[] value();
}
Copy the code

Note that the member variable Person is also an annotation. The code in this annotation is:

@Repeatable(Persons.class)
public @interface Person {
    String role() default "";
}
Copy the code

Repeatable(persons.class) @repeatable (persons.class) is used on this comment. This is just like a container comment. Then specify a Male class to use the @person annotation with the corresponding code:

@Person(role = "husband")
@Person(role = "son")
@Person(role = "father")
public class Male {
}
Copy the code

Go back and look at the code inside @Persons:

public @interface Persons {
    Person[] value();
}
Copy the code

You can see that its member variable is an annotation decorated with @repeatable (persons.class) annotation. Note that it is an array.

Here’s a picture to summarize the six meta-annotations:

The basic annotation

With meta-annotations behind you, let’s take a look at the five basic annotations that are provided by default in Java under the java.lang package: @Deprecated, @FunctionalInterface, @Override, @Safevarargs and @SuppressWarnings.

Note not to be confused with the six meta-annotations in the java.lang. Annotation package:

@ Deprecated annotations

First take a look at the source information for the @deprecated annotation, as follows:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
Copy the code

You can see that the @deprecated annotation, introduced in JDK1.5, is used to mark obsolete elements and is often encountered in everyday development. If the compiler encounters this annotation at compile time, it automatically issues a reminder warning to tell the developer that an obsolete element is being called, such as an obsolete method, an obsolete class, an obsolete member variable, etc. (see its Target annotation).

For example, create a new class, Envy, with code like:

public class Envy {
    @Deprecated
    public void hello(){
        System.out.println("hello");
    }
    public void world(){
        System.out.println("world"); } public static void main(String[] args){ new Envy().hello(); new Envy().world(); }}Copy the code

As you can see, we have added the @deprecated annotation to the Hello method hill. Then we call the Hello method with a new Envy object inside the main method. We can see that the compiler automatically adds a line in the middle of the hello method:

Note that this effect is added by the compiler, not by the program. Note that outdated methods are not unusable, but not recommended.

@ FunctionalInterface annotations

First take a look at the source information for the @FunctionalInterface annotation, as follows:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}
Copy the code

You can see that the @functionalinterface annotation, introduced in jdk1.8, is a FunctionalInterface annotation, which is a new feature in 1.8. A Functional Interface is simply a normal Interface that has only one method. The Runnable interface is a functional interface that contains only the run method:

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}
Copy the code

At this point you may be wondering what functional interfaces are for, as you will see in the next article on Lambda expressions.

@ Override annotations

First look at the source information for the @Override annotation, as follows:

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

You can see that the @Override annotation, introduced in JDK1.5, is a rewrite annotation and is the most used annotation. When the @Override annotation is added to a method, the compiler checks to see if the method implements the parent class, avoiding low-level errors such as misspelled words. For example, here’s a Movie class:

public class Movie {
    public void watch(){
        System.out.println("watch"); }}Copy the code

Then there is the ActionMovie class, which inherits the Movie class and implements the watch method, but because watch was carelessly written as wath, the compiler throws a message:

From this information you can tell that the error was caused by a misspelling of the method word.

@ SafeVarargs annotation

First take a look at the source information for the @Safevarargs annotation, as follows:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
public @interface SafeVarargs {}
Copy the code

As you can see, the @safevarargs annotation was introduced in jdk1.7. It is a parameter safe type annotation, also known as the Java 7 “heap contamination” warning. Heap contamination is when a non-generic collection is assigned to a generic collection. Using the @Safevarargs annotation will warn developers not to do anything unsafe with arguments, and its presence will prevent the compiler from generating such warnings. We don’t use this very much.

@ SuppressWarnings annotation

First take a look at the source information for the @SuppressWarnings annotation as follows:

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}
Copy the code

As you can see, the @SuppressWarnings annotation, introduced in JDK1.5, is a suppression compiler warning annotation. As mentioned earlier, the compiler will issue a warning when calling a method that is Deprecated with the @SuppressWarnings annotation. If developers want to ignore this warning, they can do so by adding the @SuppressWarnings annotation to the call:

public class Envy {
    @Deprecated
    public void hello(){
        System.out.println("hello");
    }
    public void world(){
        System.out.println("world");
    }

    @SuppressWarnings("deprecation") public static void main(String[] args){ new Envy().hello(); new Envy().world(); }}Copy the code

This way the editor does not detect whether the Hello method is expired. Again, a picture summarizes the five basic notes:

Use of custom annotations

Inject custom information into the method

After seeing how to get information from annotations at run time, you’ll see how to inject custom information into methods. Instead of customizing the methods to get the information in the annotations, use the built-in methods. Create a custom @envyThink annotation with the following code:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
public @interface EnvyThink {
    String name() default "envy";
    int age() default 22;
}
Copy the code

Create a new class, Envy, with the code:

public class Envy {
    @EnvyThink()
    public void hey(String name,int age){
        System.out.println("name--->"+name+"\n"+ "age--->"+age); }}Copy the code

Create a new test class called EnvyTest.

Public class EnvyTest {public static void main(String[] args){try {** > aClass = Envy.class; Method method = aClass.getDeclaredMethod("hey",String.class,int.class); EnvyThink = method.getannotation (envythink.class); String name = envyThink.name(); int age = envyThink.age(); /** Insert the annotation information into the method */ Object Object = aclass.newinstance (); method.invoke(object,name,age); } catch (Exception e) { e.printStackTrace(); }}}Copy the code

Then run the method, the result of the method can be known as:

name--->envy
age--->22
Copy the code

Inject a custom object into a method

As you can see, you injected the custom member attributes inside the annotation. Now try injecting the custom object into the method. First explain what it means to inject a custom object into a method. As an example, we first define the Book entity class with the following code:

public class Book {
    private String name;
    private double price;
    public Book() {}; public Book(String name,double price){ this.name=name; this.price=price; } //getter/setter/toString method}Copy the code

Next, define an Author class with the following code:

public class Author {
    private Book book;

    public Book getBook() {return book;
    }
    public void setBook(Book book){ this.book = book; }}Copy the code

You must know that the book object in the setBook method is actually passed in from the outside, so if you want to call this method you must do something like this:

public class AuthorTest {
    public static void main(String[] args){
        new Author().setBook(new Book(Romance of The Three Kingdoms,128));
    }
}
Copy the code

Now you want to annotate the Book object into the Author as follows:

@BookAnno(name = Romance of The Three Kingdoms, price = 128)
public void setBook(Book book){
    this.book = book;
}
Copy the code

How do you do that? Let’s start with an @bookanno annotation that defines two properties name and price (note that the number and name of the properties must be exactly the same as Book).

@Retention(RetentionPolicy.RUNTIME)
public @interface BookAnno {
    String name() default Romance of The Three Kingdoms;
    double price() default 128;
}
Copy the code

Note that the @Retention(retentionPolicy.runtime) annotation must be added to this annotation, otherwise it cannot be reflected.

Next, define an AuthorAnnoTest class that implements the setBook method that injects the Book object into the Author. In this class, you need Introspector for default processing of JavaBean class attributes and events. There will be a special article about introspection. The code in the AuthorAnnoTest class is:

public class AuthorAnnoTest { public static void main(String[] args) { try { /** 1. Use introspection to get the property you want to inject */ /** The first parameter is the property to inject, and the second parameter is the class to which the property is attached */ PropertyDescriptor = new PropertyDescriptor("book", Author.class); /** 2setThe Person Method (write) * / Method Method = descriptor. GetWriteMethod (); Annotation Annotation = method.getannotation (BookAnno. Class); / / Annotation Annotation = method.getannotation (BookAnno. Class); */ Method[] methods = annotation.getClass().getmethods (); */ Method[] methods = annotation.getClass().getmethods (); / * * 5. Obtaining into attribute instance object * / Book Book = (Book) descriptor. GetPropertyType (). The newInstance (); /** 6. Add the information on the annotation to the book object */for(Method meth : methods) { /** 7. Get the name of the attribute in the annotation (note that name is not just a name attribute, but an alias for the attribute) */ String name = meth.getName(); /** 8.1.1 Assuming that there is a setter for each property of the Book object, Then use introspection to get the corresponding setter method */ PropertyDescriptor descriptor1 = new PropertyDescriptor(name, book.class); / * * 8.1.2 obtain the properties written corresponding setter methods * / Method method1. = descriptor1 getWriteMethod (); /** 8.1.3 Get the value */ Object o = meth. Invoke (annotation, null); Invoke (Book, o); / / invoke(Book, o); / / invoke(Book, o); } catch (Exception e) {/** 8.2 If there is no setter for each property in the Book object, the loop is skipped and the annotation */ continuescontinue; Setter = new author ();} /** method.invoke(author, book); */ system.out.println (author.getbook ().getName())); System.out.println(author.getBook().getPrice()); } catch (Exception e) { e.printStackTrace(); }}}Copy the code

Running the method, the output result can be obtained as follows:

Romance of The Three Kingdoms 128.0Copy the code

To summarize the steps: 1. Use introspection to get the properties you want to inject. 2. Get the write method of this attribute; 3, get write method annotation; 4. Get the information on the annotation (note that it must be a method because the annotation member variable is defined as a method); 5. Get the instance object of the attribute to be injected; Add the information from the annotation to the object. 7. Call the property’s write method to inject the object with added data into the method; Verify that the object was successfully injected into the method.

Spring’s built-in @Autowried annotation does something similar to enable users to use objects directly in methods.

Inject a custom object into a property

Injecting custom objects into properties is also a common usage scenario, and from the previous section, developers can try to do just that. That is, when developers use:

public class Author {
    @BookAnno(name = Romance of The Three Kingdoms, price = 128)
    private Book book;

    public Book getBook() {return book;
    }

    public void setBook(Book book){ this.book = book; }}Copy the code

When configured, we can still get the value of the annotation in the Author object as before. Next, define an AuthorAnnoFiledTest class that implements injecting the Book object into the Book property of Author. It is important to note that reflection is used first to obtain the corresponding attribute, and introspection is used second to obtain the corresponding write method. The code in the AuthorAnnoFiledTest class is:

public class AuthorAnnoFiledTest { public static void main(String[] args){ try { /** 1. Using reflection mechanism for want to inject properties * / Field Field = Author. Class. GetDeclaredField ("book"); Annotation Annotation = field. GetAnnotation (BookAnno. Class); */ Method[] methods = annotation.getClass().getmethods (); */ Book Book = (Book) field.getType().newinstance (); /** 5. Add the information on the annotation to the book object */for(Method meth : methods) { /** 6. Get the name of the attribute in the annotation (note that name is not just a name attribute, but an alias for the attribute) */ String name = meth.getName(); /** 7.1.1 Assuming that a setter exists, Then use introspection to get the corresponding setter method */ PropertyDescriptor = New PropertyDescriptor(name, book.class); / * * 7.1.2 obtain the properties written corresponding setter methods * / Method method1. = descriptor getWriteMethod (); /** 7.1.3 Get the value */ Object o = meth. Invoke (annotation, null); Method1.invoke (Book, o); /** invoke(Book, o); } catch (Exception e) {/** 7.2 If there is no setter for each property in the Book object, the loop is skipped and the annotation */ continuescontinue; Setter = new author (); setter = new author (); setter = new author (); field.setAccessible(true); field.set(author,book); */ system.out.println (author.getbook ().getName())); System.out.println(author.getBook().getPrice()); } catch (Exception e) { e.printStackTrace(); }}}Copy the code

Running the method, the output result can be obtained as follows:

Romance of The Three Kingdoms 128.0Copy the code

To summarize the steps: 1. Use reflection to get the properties you want to inject. 2. Get an annotation for this property; 3. Get the information on the annotation (note that it must be a method because the annotation member variable is defined as a method); 4. Get the instance object of the attribute to be injected; 5. Add the information from the annotation to the object. Call the property’s write method to inject the object with added data into the method; 7. Verify that the object was successfully injected into the method.

Annotations are summarized

To conclude, annotations serve two purposes :(1) to inject data into methods, member variables, classes, or other components when appropriate; (2) Let the compiler examine the code and issue a prompt. This is the end of annotations, and a follow-up article will summarize common annotations in the Spring framework.

For more content, please pay attention to your personal wechat public account: Yu Si Blog, or scan the following QR code on wechat to directly follow: