Java custom annotation

Posted by ngoweb on Wed, 26 Jan 2022 11:33:03 +0100

concept

Note: also known as metadata, it provides a formal method for us to add information to the code, so that we can easily use the data at a later time. (in a practical sense, annotation is just a special kind of annotation. If you don't parse its code, it may have no effect.)
Tag annotation: there is no annotation defining the element inside.
Single value annotation: only one element is defined internally. The name is value. When used, the value is passed directly. There is no need to specify the element name.

grammar

Define annotation

The format of the custom annotation is as follows: @ interface is used to declare an annotation. When using @ interface to customize the annotation, it will automatically inherit Java lang.annotation. Annotation interface, and the compiler automatically completes other details, which can be viewed through decompilation. See the following for the content of meta annotation and annotation elements.

Meta annotation
 Modifier  @interface Annotation name {   
    Declaration of annotation element 1 
	Declaration of annotation element 2 
	...  
}

Customize a MyAnnotation annotation and declare an annotation element named value:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyAnnotation {
	String value() default "test";
}

Decompilation result of myannotation annotation:

Compiled from "MyAnnocation.java"
public interface com.bbu.annotation.MyAnnotation extends java.lang.annotation.Annotation {
  public abstract java.lang.String value();
}

It can be seen from the decompilation results that Annotation is essentially an interface, which inherits the Annotation interface by default.

Meta annotation

Java provides four meta annotations, which are mainly used by programmers when customizing annotations.

@Target: indicates where the annotation can be used

  • ElementType.TYPE: only used for classes, interfaces and enumerations
  • ElementType.FIELD: for attributes only
  • ElementType.METHOD: for method only (not construction method)
  • ElementType.CONSTRUCTOR: for construction methods only
  • ElementType.PARAMETER: parameters for methods only
  • ElementType.LOCAL_VARIABLE: for local variables only
  • ElementType.ANNOTATION_TYPE: for annotation only
  • ElementType.PACKAGE: for packages only

@Retention: indicates the life cycle of the annotation (compile,. class, run)

  • RetentionPolicy.SOURCE: the annotation will be discarded by the compiler, and only the source file (. java) stage is valid;
  • RetentionPolicy.CLASS: annotations are available in the class file, but will be discarded by the VM (source file (. java) stage and bytecode file stage (. Class) are valid);
  • RetentionPolicy.RUNTIME: the VM will also retain annotations during runtime, so the annotation information can be read through the reflection mechanism (source file, bytecode and runtime are valid)

@Documented: include this annotation in the Javadoc. When the API document is generated by Javadoc command, the annotation will also be generated into the document; By default (when @ documented is not written), annotations are not generated into the document.

@Inherited: allows subclasses to inherit annotations from the parent class.

Java 1.8 adds a meta annotation:
@Repeatable: indicates that the annotation is reusable; Using the same annotation in the same place will report an error. With this meta annotation, you can use the same annotation in the same place.

Annotation element

  • All basic types (four types and eight types: byte, short, int, long, float, double, char, boolean)
  • String
  • Class
  • enum
  • Annotation
  • Array corresponding to all types above

In addition to the above types, if other types are used, the compiler will report an error. Note that no packaging type is allowed, but this is not a limitation due to the existence of automatic packaging.

public enum Status {// enumeration
	SUCCESS,
	FAILURE
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyAnnotation {
	int id() default -1;// Basic type
	String value() default "";// String
	Class<?> clz() default String.class;// Class
	Status enu() default Status.SUCCESS;// Enum
	String[] values();// array
	// Integer id() default -1;//  Invalid type integer for the annotation attribute myannotation.ids; only primitive type, string, class, annotation, enumeration are allowed or 1-dimensional arrays thereof
}

Annotations can also be used as the type of elements, that is, annotations can be nested:

package com.bbu.annocation;

import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
@Target(value={ElementType.TYPE, ElementType.FIELD})
@Inherited
public @interface Alias {
	// Annotation as the type of element
	MyAnnotation annotation() default @MyAnnotation(values = { "" });
}

Annotation element setting rules:
First, it can only be modified with public or default access rights;
Second, if there is only one parameter member, it is better to set the parameter name to "value". In this way, value can be omitted when writing value in the annotation;
Third, first of all, annotation elements cannot have uncertain values. That is, the element must either have a default value or provide an element value when using annotations. Secondly, for non basic type elements, null cannot be used as the value when declaring in the source code or defining the default value in the annotation interface. This constraint makes it difficult for the processor to express the existence or absence of an element, because all elements exist in each annotation declaration and have corresponding values. In order to bypass this constraint, You can only define special values, such as empty strings or negative numbers, to indicate that an element does not exist.

Java built-in annotation

Starting from Java SE5, three standard annotations are built in, which are defined in Java In Lang:

  • @Override: indicates that the current method will override the method in the parent class; If method signature does not match overridden method, compiler will issue an error message.
  • @Deprecated: if an element is modified by the annotation and the programmer uses the element, the compiler will issue a warning message.
  • @SuppressWarnings: turn off improper compiler warnings.
    Usage:
    @SuppressWarnings("")
    @SuppressWarnings({"", ""})
    @SuppressWarnings(value={"", ""})

Jdk1. A new note was added after 8:

  • @Functional interface: used to declare an interface as a functional interface

The source code of Java built-in annotation in No. 4 middle school is as follows:

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

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

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

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

It can be seen that there are no annotation elements in @ Override, @ Deprecated and @ FunctionalInterface annotations, which is a tag annotation@ The annotation element in SuppressWarnings is a String array. The more common values are rawtypes (ignoring that the corresponding type is not specified when using generics), unchecked (suppressing warnings without type checking operation) and all (suppressing all warnings).

usage

Annotation and reflection

Common methods of reflection operation annotation are defined in Class:

  • Public < a extends annotation > a getannotation (class < a > annotationclass): returns the specified annotation
  • Public Boolean isannotationpresent (class <? Extends annotation > annotationclass): determines whether the current element is modified by the specified annotation
  • public Annotation[] getAnnotations(): returns all annotations
  • Public < a extends annotation > a getdeclaraedannotation (class < a > annotationclass): returns the specified annotation of this element
  • Public annotation [] getdeclaraedannotations(): returns all annotations of this element, excluding those inherited from the parent class

After the annotation instance is obtained through the above methods, we can directly obtain the value set in the corresponding annotation through the annotation element method, such as @ myannotation ("user_id") annotation. We can obtain the annotation instance through the getAnnotation method and then annotation Value() gets the value of "user_id".

Generate SQL cases by reflecting and parsing annotations

First, customize an annotation named MyAnnotation, internally set an annotation element value, and set it to be valid at runtime through @ Retention meta annotation. In this way, you can dynamically obtain annotation instances using reflection. Set classes, interfaces, enumerations and attributes through @ Target meta annotation.

package com.bbu.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Custom annotation MyAnnotation
 * @author code_now
 *
 */
@Retention(RetentionPolicy.RUNTIME)// Setting to runtime is also valid
@Target({ElementType.TYPE, ElementType.FIELD})// You can set classes, interfaces, enumerations and properties using
public @interface MyAnnotation {
	String value() default "";// Annotation element value
}

Customize a User class to correspond the table names and column names in the corresponding database through the @ myannotation annotation.

package com.bbu.model;

import com.bbu.annocation.MyAnnotation;

/**
 * Custom entity class 
 * @author code_now
 *
 */
@MyAnnocation("t_user")
public class User {

	@MyAnnocation("user_id")
	private Integer userId;
	
	@MyAnnocation("user_name")
	private String userName;
	
	@MyAnnocation("age")
	private Integer age;
	
	@MyAnnocation("home_address")
	private String homeAddress;
	
	private String desc;

	public Integer getUserId() {
		return userId;
	}

	public void setUserId(Integer userId) {
		this.userId = userId;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	public String getHomeAddress() {
		return homeAddress;
	}

	public void setHomeAddress(String homeAddress) {
		this.homeAddress = homeAddress;
	}

	public String getDesc() {
		return desc;
	}

	public void setDesc(String desc) {
		this.desc = desc;
	}

	@Override
	public String toString() {
		return "User [userId=" + userId + ", userName=" + userName + ", age="
				+ age + ", homeAddress=" + homeAddress + ", desc=" + desc + "]";
	}
	
}

Get the corresponding table name and column name on the class and attribute through reflection, and then spell out the final query SQL.

package com.bbu.test;

import java.lang.reflect.Field;
import com.bbu.annocation.MyAnnotation;

/**
 * Parsing annotations generates SQL statements through reflection 
 * @author code_now
 *
 */
public class GenerateSQL {

	public static void main(String[] args) throws Exception {

        Class<?> clz = Class.forName("com.bbu.model.User");// Gets the Class object of the User Class
        StringBuffer sb = new StringBuffer();// For splicing sql
        sb.append("select ");
        String temp1 = "";
        Field[] declaredFields = clz.getDeclaredFields();// Get all properties in the User class
        for (Field field : declaredFields) {
        	field.setAccessible(true);
			MyAnnotation annotation = field.getAnnotation(MyAnnotation.class);// Gets the corresponding annotation instance on the attribute
			if(annotation != null) {
				String value = annotation.value();
				sb.append(temp1+value);
				temp1 = ",";
			}
		}
        
        sb.append(" from ");
        MyAnnotation annotation = clz.getAnnotation(MyAnnotation.class);// Get the corresponding annotation instance on the class
        if(annotation != null) {
        	String value = annotation.value();
        	sb.append(value);
        }
        
        System.out.println(sb.toString());
    }

}

Final generated SQL:

select user_id,user_name,age,home_address from t_user

reference resources

Java programming ideas (4th Edition)

Topics: Java Back-end