Interviewer: Talk about annotations?

Understand java annotations

​ Annotation is a very important part of java, we use it all the time, especially when using the Spring framework to develop, we will use a lot of strange annotations in the project (@DataScope, @Log, @Override, etc.), Many times we just use these annotations, but we don't know much about the logic and working principles behind the annotations, let alone develop an annotation ourselves. So today I will record my understanding of annotations and how to customize an annotation here to communicate with you.

​ First of all, annotation is a concept introduced in Java 1.5 and belongs to a type. Annotations provide a series of data to modify the program code (classes, methods, fields), but the annotation is not part of the modified code, that is, it has no direct impact on the operation of the code, and the compiler decides which operations to perform

​ Secondly, Annatation (annotation) is an interface, you can get the Annatation object of the element in the specified program through reflection, and then get the metadata information in the annotation through the object

Start with @Override

Presumably, @Override is the most familiar to us in usual coding. Knowing that it is actually used for method rewriting, and the subclass overrides the annotations used by the parent class method. But how does it achieve this function, let's click on the source code to have a look.

import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

You can see that the source code is very short, it is nothing more than importing a jar package, then using the other two annotations, and then using @interface to declare this annotation.

Clicking on @Target and @Retention, we will find that they are always crazy dolls among the three annotations @Documented, @Retention, and @Target. In fact, these three annotations are meta-annotations in the annotations.

Meta annotation

Let’s take a look at these three meta-annotations separately.

@Retention (life cycle)

Indicates how to store the marked annotations (specify the storage level), the popular understanding is the life cycle

  • SOURCE: Source code level, annotations will be discarded by the compiler and will not be kept in the compiled class file
  • CLASS: (default level) class file level, annotations are available in the class file, but will be discarded by the JVM, and will not be loaded into the virtual machine during execution.
  • RUNTIME: Runtime level, which will also be reserved during runtime (JVM), so the content of the annotation can be read through the reflection mechanism. Such as @Controller, @Autowired, @RequestMapping, etc. in SpringMvc.

@Target

Indicates that the marked annotation can be used for the kind of java element (class, interface, attribute, method). There are eight kinds in total.

Among them, ElementType is an enumerated type, which is defined as follows: indicates the possible range of values

public enum ElementType{
     // 标明该注解可以用于类、接口(包括注解类型)或enum声明
    TYPE,

    // 标明该注解可以用于字段(域)声明,包括enum实例 
    FIELD,

    // 标明该注解可以用于方法声明 
    METHOD,

    // 标明该注解可以用于参数声明 
    PARAMETER,

    // 标明注解可以用于构造函数声明 
    CONSTRUCTOR,

    // 标明注解可以用于局部变量声明 
    LOCAL_VARIABLE,

    // 标明注解可以用于注解声明(应用于另一个注解上)
    ANNOTATION_TYPE,

    // 标明注解可以用于包声明 
    PACKAGE,

    //标明注解可以用于类型参数声明(1.8新加入)
    TYPE_PARAMETER,

    // 类型使用声明(1.8新加入)
    TYPE_USE
}

When the annotation is a specified Target value, this annotation can be used on any element, and multiple values ​​are included with {} and separated by commas.

@Documented

Whenever you use the specified annotations, you should use the Javadoc tool to record these elements. (That is, comments will be added to the generated javadoc)

@Inherited

The annotation type can be inherited from the super class, which is only used for the declaration of the class (the interface will not be inherited)

@Repeatable

Introduced in Java SE 8, the annotation that represents the mark can be applied to the same declaration or type usage multiple times.

Classification of annotations

Through the understanding of meta-annotations, I understand that an annotation is modified by these meta-annotations, and we have also gained an important message- annotations can modify annotations

Such an infinite matryoshka will have various annotations, so what annotations are there? Common annotations are roughly divided into the following four categories:

  • Meta-annotation: one type has five types
  • jdk annotation: the common ones are our @Override, @Deprecated: used to indicate and outdated methods or classes, etc.
  • Third-party annotations: It is the annotations of the third-party framework Spring, etc. we are familiar with, @Autowired, etc.
  • Custom annotations: The annotations customized by the developers according to the project requirements are used for the analysis and use of some tools during compilation and runtime, and play the role of explanation and configuration. At the end of the article, we will actually write an annotation.

Annotation processor

An important part of the process of using annotations is to create an annotation processor

  • Example:

    • Definition annotation:
import java.lang.annotation.*;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider{
    public int id() default -1;
    public String name() default "";
    public String address() default "";
}
    • Annotation use:
public class Apple {
    @FruitProvider(id=1,name="zhonghu",address = "china")
    private String appleProvider;

    public String getAppleProvider() {
        return appleProvider;
    }

    public void setAppleProvider(String appleProvider) {
        this.appleProvider = appleProvider;
    }
}
    • Annotation processor:
import java.lang.reflect.Field;


public class FruitInfoUtil {
    public static void getFruitInfo(Class<?> clazz){
        String strFruitProvicer = "供应商信息:";
        Field[] fields = clazz.getDeclaredFields();//通过反射获取处理注解
        for (Field field : fields) {
            if (field.isAnnotationPresent(FruitProvider.class)) {
                FruitProvider fruitProvider = (FruitProvider) field.getAnnotation(FruitProvider.class);
                //注解信息的处理地方
                strFruitProvicer = " 供应商编号:" + fruitProvider.id() + " 供应商名称:"
                        + fruitProvider.name() + " 供应商地址:"+ fruitProvider.address();
                System.out.println(strFruitProvicer);
            }
        }
    }
}
    • Output
public class FruitRun {
    public static void main(String[] args) {
        FruitInfoUtil.getFruitInfo(Apple.class);
    }
}
  • result:
    image

The role of annotations

  • The format is checked at compile time. Such as@Override
  • Tracking code dependencies and realizing the function of replacing configuration files. Generate code and XML files by processing annotation information.
  • Some comments can be checked at runtime

Start commenting

Before the actual combat begins, we also need to understand the rules for writing custom annotations

rule

  • The definition of annotation is @interfacethat all annotations will automatically inherit java.lang.Annotationthis interface, and can no longer inherit other classes or interfaces

Annotation does not support inheritance

  • Parameter members can only be modified with publicor default(默认)access authority characters
  • Eight parameter member only with the basic data types, String, Enum, Class, annotationsand other data types, and these types of arrays

No packaging type is allowed, annotations can also be used as element types, that is, nested annotations

  • To get the annotation information of class methods and fields, it must be obtained through the java reflection mechanism

At the same time, in order to accurately obtain the relevant information of the annotations at runtime, java has added the AnnotatedElement interface under the java.lang.reflect reflection package, which is used to indicate the elements that have been annotated in the program currently running on the JVM. This interface provides The method can use reflection technology to read the annotated information.

  • The annotation can also have no defined members (only for identification)

Write annotations

​ Above we introduced the use of annotations and the information of annotations. Below, let's mark a field of annotations to mark whether the object should include this field when it is serialized into JSON

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface JsonField {
    public String value() default "";
}
  • Explanation:

    • The life cycle of JsonField annotation is RUNTIME, which is valid at runtime
    • The target of JsonField annotation modification is FIELD, which is for the field
    • The @interface keyword is needed to create an annotation
    • JsonField annotation has only one parameter, the name is value, the type is String, and the default value is an empty string

The parameter name is value, allowing the user of the annotation to provide a parameter without specifying a name. That is, we can use @JsonField (value="塚狐") on a field, or omit value= and become @JsonField ("塚狐")

    • default "" allows us to use @JsonField directly on a field without specifying the name and value of the parameter

Use annotations

​ Create a class, containing three fields: age, name, address, the last two are required to serialize the field

public class People {
    private  int age ;

    @JsonField("writeName")
    private String name;

    @JsonField
    private String address;

    public People(int age,String name,String address){
        this.age=age;
        this.name=name;
        this.address=address;
    }

    @Override
    public String toString() {
        return "People{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

among them:

  • The @JsonField annotation on the name provides the displayed string value
  • The @JsonField annotation on address uses the default value

Next we write the serialization class JsonSerializer:

public class JsonSerializer {
    public static String serialize(Object object) throws IllegalAccessException {
        Class<?> objectClass = object.getClass();
        Map<String, String> jsonElements = new HashMap<>();
        for (Field field : objectClass.getDeclaredFields()) {
            field.setAccessible(true);
            if (field.isAnnotationPresent(JsonField.class)) {
                jsonElements.put(getSerializedKey(field), (String) field.get(object));
            }
        }
        return toJsonString(jsonElements);
    }

    private static String getSerializedKey(Field field) {
        String annotationValue = field.getAnnotation(JsonField.class).value();
        if (annotationValue.isEmpty()) {
            return field.getName();
        } else {
            return annotationValue;
        }
    }

    private static String toJsonString(Map<String, String> jsonMap) {
        String elementsString = jsonMap.entrySet()
                .stream()
                .map(entry -> "\"" + entry.getKey() + "\":\"" + entry.getValue() + "\"")
                .collect(Collectors.joining(","));
        return "{" + elementsString + "}";
    }
}

Let's take a look at their respective meanings and functions:

  • serialize()The method is used to serialize an object, and it receives a parameter of type Object. objectClass.getDeclaredFields()Get all the fields declared by the object by reflection, and then perform a for loop traversal. In a for loop, first by field.setAccessible(true)the reflecting object accessibility set to true, for the sequence of use (without this step, then, private field can not be obtained, throws IllegalAccessException exception); again by isAnnotationPresent()determining whether the field decorated JsonFieldannotation, and if so, calls the getSerializedKey()method, and thus obtain the value field indicates the object, and placed in jsonElements.
  • getSerializedKey() The method is used to get the value of the annotation on the field. If the value of the annotation is empty, the field name is returned.
  • toJsonString() The method returns the formatted JSON string by means of Stream.

Test notes

public class JsonFileTest {
    public static void main(String[] args) throws IllegalAccessException{
        People cmower = new People(18,"冢狐","中国");
        System.out.println(JsonSerializer.serialize(cmower));
    }
}

  • result:

{"WriteName":"Mound Fox","address":"China"}

  • analysis

    • First, the age field is not annotated by @JsonField so it is not serialized
    • The name modifies the @JsonField annotation and shows that the string writerName is specified, so it becomes writeName after serialization
    • The address field is decorated with the @JsonField annotation, but the specified value is not displayed, so it is still the address after serialization

At last

  • If you feel that you are rewarded after reading it, I hope to give me a thumbs up. This will be the biggest motivation for me to update. Thank you for your support.
  • Welcome everyone to pay attention to my public account [Java Fox], focusing on the basic knowledge of java and computer, I promise to let you get something after reading it, if you don’t believe me, hit me
  • If you have different opinions or suggestions after reading, please comment and share with us. Thank you for your support and love.

——I am Chuhu, and I love programming as much as you.

image

Guess you like

Origin blog.csdn.net/issunmingzhi/article/details/112000502