Talk about Java custom annotations and runtime reflection for annotations

  • 2020-05-17 05:34:17
  • OfStack

java custom annotations

The Java annotation is a meta-information attached to the code, which is used by some tools to interpret and use at compile time and run time.

Annotations do not and cannot affect the actual logic of the code; they are merely instrumental. Included in the java.lang.annotation package.

1. Meta notes

Meta-annotations are annotations of annotations. Including @Retention @Target @Document @Inherited 4.

1.1. @Retention: define the retention policy for annotations


@Retention(RetentionPolicy.SOURCE)  // Annotations exist only in the source code, in class Not included in the bytecode file 
@Retention(RetentionPolicy.CLASS)   //  The default retention policy, the annotations will be in class It's in the bytecode file, but it's not available at runtime, 
@Retention(RetentionPolicy.RUNTIME) //  Annotations can be in class The bytecode file exists and can be retrieved by reflection at run time 

Annotation:


@Retention(RetentionPolicy.RUNTIME) //  Annotations can be in class The bytecode file exists and can be retrieved by reflection at run time 
@Target({ElementType.FIELD,ElementType.METHOD})// Define the purpose of the annotation ** Scope field, enumeration of constants / methods 
@Documented// Indicates that the annotation will be included in javadoc In the 
public @interface FieldMeta {

	/**
	 *  Is it a serial number 
	 * @return
	 */
	boolean id() default false;
	/**
	 *  The field names 
	 * @return
	 */
	String name() default "";
	/**
	 *  Editable or not 
	 * @return
	 */
	boolean editable() default true;
	/**
	 *  Whether to display in the list 
	 * @return
	 */
	boolean summary() default true;
	/**
	 *  The field 
	 * @return
	 */
	String description() default "";
	/**
	 *  Sort field 
	 * @return
	 */
	int order() default 0;
}

Entity class:


public class Anno {

	@FieldMeta(id=true,name=" The serial number ",order=1)
	private int id;
	@FieldMeta(name=" The name ",order=3)
	private String name;
	@FieldMeta(name=" age ",order=2)
	private int age;
	
	@FieldMeta(description=" describe ",order=4)
	public String desc(){
		return "java Reflection to obtain annotation The test of ";
	}
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	
}

Get the help class to the annotation:


public class SortableField {

	public SortableField(){}
	
	public SortableField(FieldMeta meta, Field field) {
		super();
		this.meta = meta;
		this.field = field;
		this.name=field.getName();
		this.type=field.getType();
	}
	
	
	public SortableField(FieldMeta meta, String name, Class<?> type) {
		super();
		this.meta = meta;
		this.name = name;
		this.type = type;
	}


	private FieldMeta meta;
	private Field field;
	private String name;
	private Class<?> type;
	
	public FieldMeta getMeta() {
		return meta;
	}
	public void setMeta(FieldMeta meta) {
		this.meta = meta;
	}
	public Field getField() {
		return field;
	}
	public void setField(Field field) {
		this.field = field;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}

	public Class<?> getType() {
		return type;
	}

	public void setType(Class<?> type) {
		this.type = type;
	}
	
	
}

To get the annotations at runtime, first create a base class:


public class Parent<T> {

	private Class<T> entity;

	public Parent() {
		init();
	}

	@SuppressWarnings("unchecked")
	public List<SortableField> init(){
		List<SortableField> list = new ArrayList<SortableField>();
		/**getClass().getGenericSuperclass() Return represents this  Class  The entity represented (class, interface, base type, or  void ) 
		 *  Of the direct superclass  Type(Class<T> Types in generics ) And then convert it ParameterizedType . 
		 * 	getActualTypeArguments() Returns the value that represents the actual type parameter of this type  Type  An array of objects. 
		 * 	[0] That's the number one in this array 1 A.. 
		 * 	 In short, get the actual type of the generic parameter of the superclass. */
		entity = (Class<T>)((ParameterizedType)this.getClass().getGenericSuperclass())
				.getActualTypeArguments()[0];
//		FieldMeta filed = entity.getAnnotation(FieldMeta.class);
		
		if(this.entity!=null){
			
			/** Returns all fields in the class, including public, protected, default (package) access, and private fields, but not inherited fields 
			 * entity.getFields(); Only all accessible public fields of the class or interface represented by the object are returned 
			 *  in class In the getDeclared**() Methods return fields, methods, and so on for all access rights. 
			 *  To look at API
			 * */
			Field[] fields = entity.getDeclaredFields();
//			
			for(Field f : fields){
				// Gets contained in the field fieldMeta annotations 
				FieldMeta meta = f.getAnnotation(FieldMeta.class);
				if(meta!=null){
					SortableField sf = new SortableField(meta, f);
					list.add(sf);
				}
			}
			
			// Returns all accessible public methods of the class or interface represented by the object 
			Method[] methods = entity.getMethods();
			
			for(Method m:methods){
				FieldMeta meta = m.getAnnotation(FieldMeta.class);
				if(meta!=null){
					SortableField sf = new SortableField(meta,m.getName(),m.getReturnType());
					list.add(sf);
				}
			}
			// The method is new FieldSortCom Class implements Comparator Interface, to rewrite compare Method implementation sort 
//			Collections.sort(list, new FieldSortCom());
			Collections.sort(list, new Comparator<SortableField>() {
				@Override
				public int compare(SortableField s1,SortableField s2) {
					return s1.getMeta().order()-s2.getMeta().order();
//					return s1.getName().compareTo(s2.getName());// You can also use compare To compare 
				}
				
			});
		}
		return list;
		
	}
}

Create a subclass to inherit the base class:


public class Child extends Parent<Anno>{

}

The test class:


public class TestAnnotation {

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static void main(String[] args) {
		Parent c = new Child();
		List<SortableField> list = c.init();// Gets the annotations in the generic class 
		// The output 
		for(SortableField l : list){
			System.out.println(" Field name: "+l.getName()+"\t Field type: "+l.getType()+
					"\t Annotation name: "+l.getMeta().name()+"\t Annotated description: "+l.getMeta().description());
		}
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

1. Working principle of Annotation:

Annotations are provided in JDK5.0, allowing developers to define and use their own annotation types. This feature consists of a syntax that defines the annotation type and describes the syntax of an annotation declaration, an API that reads the annotation, an class file that is decorated with annotations, and an annotation processing tool.

Annotation does not directly affect the semantics of the code, but it can be thought of as a tool or library for a program. This in turn has an impact on the semantics of the running program.

Annotation can be read at run time by either flushing the source file, class files, or through a reflection mechanism.

2. @Override note:

java.lang

Annotation type Override
@Target(value=METHOD)
@Retention(value=SOURCE)
public@interface Override means that one method declaration intends to override another method declaration in a superclass. If the method annotates with this annotation type but does not override the superclass method, the compiler generates an error message.

The @Override annotation indicates that the subclass overrides the corresponding method of the parent class.

Override is an Marker annotation, Annotation for identification, and the Annotation name itself represents the information to be given to the utility.

Here is an example using the @Override annotation:


class A {
  private String id;
  A(String id){
    this.id = id;
  }
  @Override
  public String toString() {
    return id;
  }
}

3. @Deprecated notes:

java.lang
Annotation type Deprecated
@Documented
@Retention(value=RUNTIME)

Program elements annotated with @

are discouraged from being used by programmers, usually because they are dangerous or because there are better alternatives. The compiler issues a warning when an unapproved program element is used or when an override is performed in unapproved code.

The @Deprecated annotation method is not recommended.

Deprecated is one Marker annotation.

Here is an example using the @Deprecated annotation:


class A {
  private String id;
  A(String id){
    this.id = id;
  }
  @Deprecated
  public void execute(){
    System.out.println(id);
  }
  public static void main(String[] args) {
    A a = new A("a123");
    a.execute();
  }
}

4. @SuppressWarnings

java.lang
Annotation type SuppressWarnings
@Target(value={TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE})
@Retention(value=SOURCE)

public @interface SuppressWarnings indicates that the specified compiler warning should be undisplayed in the annotation element (and all program elements contained within that annotation element). Note that the set of warnings undisplayed in a given element is a superset of all warnings undisplayed in the element. For example, if you comment 1 class to undisplay one warning and 1 method to undisplay another, both warnings will be undisplayed in this method.

Depending on the style, the programmer should always use this annotation on the innermost nested element, where it is valid. If you want to undisplay a warning in a particular method, you should annotate the method instead of its class.

The @SuppressWarnings annotation indicates a suppression warning.

Here is an example using the @SuppressWarnings annotation:


@SuppressWarnings("unchecked")
public static void main(String[] args) {
  List list = new ArrayList();
  list.add("abc");
}

5. Custom annotations:

When using the @ interface custom annotations, automatic inherited java. lang. annotation. Annotation interface, the compiler automatically other details. When defining annotations, you cannot inherit other annotations or interfaces.

The easiest annotation to customize:


@Retention(RetentionPolicy.RUNTIME) //  Annotations can be in class The bytecode file exists and can be retrieved by reflection at run time 
@Target({ElementType.FIELD,ElementType.METHOD})// Define the purpose of the annotation ** Scope field, enumeration of constants / methods 
@Documented// Indicates that the annotation will be included in javadoc In the 
public @interface FieldMeta {

	/**
	 *  Is it a serial number 
	 * @return
	 */
	boolean id() default false;
	/**
	 *  The field names 
	 * @return
	 */
	String name() default "";
	/**
	 *  Editable or not 
	 * @return
	 */
	boolean editable() default true;
	/**
	 *  Whether to display in the list 
	 * @return
	 */
	boolean summary() default true;
	/**
	 *  The field 
	 * @return
	 */
	String description() default "";
	/**
	 *  Sort field 
	 * @return
	 */
	int order() default 0;
}
0

5.1 add variables:


public @interface MyAnnotation {

  String value1();
}

 Use custom annotations: 

public class AnnotationTest2 {

  @MyAnnotation(value1="abc")
  public void execute(){
    System.out.println("method");
  }
}

When the attribute used in the annotation is called value, the attribute value interface can be written directly without specifying the name of the attribute. Except for value unexpected variable names, name=value.

5.2 add default value:


public @interface MyAnnotation {

  String value1() default "abc";
}

5.3 use enumeration for multiple variables:


@Retention(RetentionPolicy.RUNTIME) //  Annotations can be in class The bytecode file exists and can be retrieved by reflection at run time 
@Target({ElementType.FIELD,ElementType.METHOD})// Define the purpose of the annotation ** Scope field, enumeration of constants / methods 
@Documented// Indicates that the annotation will be included in javadoc In the 
public @interface FieldMeta {

	/**
	 *  Is it a serial number 
	 * @return
	 */
	boolean id() default false;
	/**
	 *  The field names 
	 * @return
	 */
	String name() default "";
	/**
	 *  Editable or not 
	 * @return
	 */
	boolean editable() default true;
	/**
	 *  Whether to display in the list 
	 * @return
	 */
	boolean summary() default true;
	/**
	 *  The field 
	 * @return
	 */
	String description() default "";
	/**
	 *  Sort field 
	 * @return
	 */
	int order() default 0;
}
3

Use custom annotations:


@Retention(RetentionPolicy.RUNTIME) //  Annotations can be in class The bytecode file exists and can be retrieved by reflection at run time 
@Target({ElementType.FIELD,ElementType.METHOD})// Define the purpose of the annotation ** Scope field, enumeration of constants / methods 
@Documented// Indicates that the annotation will be included in javadoc In the 
public @interface FieldMeta {

	/**
	 *  Is it a serial number 
	 * @return
	 */
	boolean id() default false;
	/**
	 *  The field names 
	 * @return
	 */
	String name() default "";
	/**
	 *  Editable or not 
	 * @return
	 */
	boolean editable() default true;
	/**
	 *  Whether to display in the list 
	 * @return
	 */
	boolean summary() default true;
	/**
	 *  The field 
	 * @return
	 */
	String description() default "";
	/**
	 *  Sort field 
	 * @return
	 */
	int order() default 0;
}
4

5.4 array variables:


@Retention(RetentionPolicy.RUNTIME) //  Annotations can be in class The bytecode file exists and can be retrieved by reflection at run time 
@Target({ElementType.FIELD,ElementType.METHOD})// Define the purpose of the annotation ** Scope field, enumeration of constants / methods 
@Documented// Indicates that the annotation will be included in javadoc In the 
public @interface FieldMeta {

	/**
	 *  Is it a serial number 
	 * @return
	 */
	boolean id() default false;
	/**
	 *  The field names 
	 * @return
	 */
	String name() default "";
	/**
	 *  Editable or not 
	 * @return
	 */
	boolean editable() default true;
	/**
	 *  Whether to display in the list 
	 * @return
	 */
	boolean summary() default true;
	/**
	 *  The field 
	 * @return
	 */
	String description() default "";
	/**
	 *  Sort field 
	 * @return
	 */
	int order() default 0;
}
5

Use custom annotations:


@Retention(RetentionPolicy.RUNTIME) //  Annotations can be in class The bytecode file exists and can be retrieved by reflection at run time 
@Target({ElementType.FIELD,ElementType.METHOD})// Define the purpose of the annotation ** Scope field, enumeration of constants / methods 
@Documented// Indicates that the annotation will be included in javadoc In the 
public @interface FieldMeta {

	/**
	 *  Is it a serial number 
	 * @return
	 */
	boolean id() default false;
	/**
	 *  The field names 
	 * @return
	 */
	String name() default "";
	/**
	 *  Editable or not 
	 * @return
	 */
	boolean editable() default true;
	/**
	 *  Whether to display in the list 
	 * @return
	 */
	boolean summary() default true;
	/**
	 *  The field 
	 * @return
	 */
	String description() default "";
	/**
	 *  Sort field 
	 * @return
	 */
	int order() default 0;
}
6

6. Scope of setting annotations:

@Documented
@Retention(value=RUNTIME)
@Target(value=ANNOTATION_TYPE)

public @interface Retention indicates how long to keep comments of annotation types. If the Retention annotation does not exist in the annotation type declaration, the retention policy defaults to RetentionPolicy.CLASS.

The Target meta-annotation is only valid if the meta-annotation type is used directly for annotations. If the meta-annotation type is used as a member of another annotation type, it is invalid.

public enum RetentionPolicy
extends Enum < RetentionPolicy > Comment retention policy. Constants for this enumeration type describe different strategies for preserving annotations. They are used with Retention meta-annotation type 1 to specify how long the annotation should be retained.

CLASS
The compiler records the comments in class files, but VM does not need to keep the comments at run time.

RUNTIME
The compiler will record the comments in a class file, and VM will retain the comments at run time, so they can be read reflectively.

SOURCE
Comments to be discarded by the compiler. The @Retention annotation provides the compiler with a retention policy for the annotation when defining the annotation.

Comments belonging to the CLASS retention policy are @SuppressWarnings, and this annotation information is not stored in the.class file.

6.1 examples of use in custom annotations:


@Retention(RetentionPolicy.CLASS)
public @interface MyAnnotation {

  String[] value1() default "abc";
}

7. Example of using reflection to read Annotation information of RUNTIME retention policy:

java.lang.reflect

Interface AnnotatedElement

All known implementation classes:

AccessibleObject, Class, Constructor, Field, Method, Package represents an annotated element of the program currently running in this VM. This interface allows comments to be read reflectively. All comments returned by methods in this interface are immutable and serializable. The caller can modify the array returned by the accessor of the assigned array enumeration member. This does not have any effect on the array returned by the other caller.

If the annotation returned by a method in this interface (directly or indirectly) contains an assigned Class member that references a class that is not accessible in this VM, an attempt to read the class by calling the method returned by the relevant class on the returned annotation will result in an TypeNotPresentException.

isAnnotationPresent
boolean isAnnotationPresent(Class < ? extends Annotation > annotationClass) returns true if an annotation of the specified type exists on this element, false otherwise. This method is designed primarily for easy access to markup annotations.

Parameters:

annotationClass - corresponds to the Class object of the annotation type

Returns:

If an annotation of the specified annotation type exists on this object, true is returned, otherwise false is returned

Throws:

NullPointerException - if the given annotation class is null

Start with the following version:

1.5

getAnnotation
< T extends Annotation > T getAnnotation(Class < T > annotationClass) returns comments of the specified type for the element if they exist, or null otherwise.

Parameters:

annotationClass - corresponds to an Class object of the annotation type

Returns:

If an annotation of the specified annotation type for the element exists on this object, these annotations are returned, otherwise null is returned

Throws:

NullPointerException - if the given annotation class is null

Start with the following version:

1.5

getAnnotations
Annotation[] getAnnotations() returns all comments that exist on this element. (if the element is not commented, an array of length zero is returned.) The caller of this method can modify the returned array at will; This does not have any effect on the array returned by the other caller.

Returns:

All comments that exist on this element

Start with the following version:

1.5

getDeclaredAnnotations
Annotation[] getDeclaredAnnotations() returns all comments that exist directly on this element. Unlike other methods in this interface, this method ignores inherited annotations. (if no annotation exists directly on this element, an array of length zero is returned.) The caller of this method can modify the returned array at will; This does not have any effect on the array returned by the other caller.

Returns:

All comments that exist directly on this element

Start with the following version:

1.5

Here is an example of using reflection to read Annotation information for the RUNTIME retention policy:

Custom comments:


@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {

  String[] value1() default "abc";
}

Use custom annotations:


@Retention(RetentionPolicy.RUNTIME) //  Annotations can be in class The bytecode file exists and can be retrieved by reflection at run time 
@Target({ElementType.FIELD,ElementType.METHOD})// Define the purpose of the annotation ** Scope field, enumeration of constants / methods 
@Documented// Indicates that the annotation will be included in javadoc In the 
public @interface FieldMeta {

	/**
	 *  Is it a serial number 
	 * @return
	 */
	boolean id() default false;
	/**
	 *  The field names 
	 * @return
	 */
	String name() default "";
	/**
	 *  Editable or not 
	 * @return
	 */
	boolean editable() default true;
	/**
	 *  Whether to display in the list 
	 * @return
	 */
	boolean summary() default true;
	/**
	 *  The field 
	 * @return
	 */
	String description() default "";
	/**
	 *  Sort field 
	 * @return
	 */
	int order() default 0;
}
9

Read the information in the annotation:


public static void main(String[] args) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
  AnnotationTest2 annotationTest2 = new AnnotationTest2();
  // To obtain AnnotationTest2 the Class The instance 
  Class<AnnotationTest2> c = AnnotationTest2.class;
  // Gets the method that needs to be processed Method The instance 
  Method method = c.getMethod("execute", new Class[]{});
  // Determine whether the method contains MyAnnotation annotations 
  if(method.isAnnotationPresent(MyAnnotation.class)){
    // Gets the value of this method MyAnnotation Annotation instance 
    MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
    // Execute the method 
    method.invoke(annotationTest2, new Object[]{});
    // To obtain myAnnotation
    String[] value1 = myAnnotation.value1();
    System.out.println(value1[0]);
  }
  // Gets all the annotations on the method 
  Annotation[] annotations = method.getAnnotations();
  for(Annotation annotation : annotations){
    System.out.println(annotation);
  }
}

8. Limited use of annotations:

Qualified annotations use @Target.

@Documented
@Retention(value=RUNTIME)
@Target(value=ANNOTATION_TYPE)

public @interface Target indicates the type of program element to which annotation types apply. If the Target meta-annotation does not exist in the annotation type declaration, the declared type can be used on any one of the program elements. If such a meta-annotation exists, the compiler enforces the specified usage restrictions. For example, this meta-annotation indicates that the declared type is itself, the meta-annotation type. It can only be used for annotation type declarations:


@Target(ElementType.ANNOTATION_TYPE)
  public @interface MetaAnnotationType {
    ...
  }

This meta-annotation indicates that the declared type can only be used as a member type in a complex annotation type declaration. It cannot be used directly for comments:


@Target({}) 
  public @interface MemberType {
    ...
  }

This is a compile-time error that indicates that an ElementType constant appears more than once in the Target annotation. For example, the following meta-annotation is illegal:


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

extends Enum < ElementType > Program element type. Constants of this enumeration type provide a simple classification of the elements declared in the Java program.

These constants are used with Target meta-annotation type 1 to specify when it is legal to use an annotation type.

ANNOTATION_TYPE
Annotation type declaration
CONSTRUCTOR
Constructor declaration
FIELD
Field declarations (including enumeration constants)
LOCAL_VARIABLE
Local variable declaration
METHOD
Method statement
PACKAGE
Package declaration
PARAMETER
Parameter declarations
TYPE
Class, interface (including annotation types), or enumeration declarations

Examples of usage restrictions for annotations:


@Target(ElementType.METHOD)
public @interface MyAnnotation {

  String[] value1() default "abc";
}

9. Add notes to the help document:

To make JavaDoc file to add annotations to API file at the same time, can use java. lang. annotation. Documented.

Declare build annotation documents in custom annotations:


@Documented
public @interface MyAnnotation {

  String[] value1() default "abc";
}

Use custom annotations:


public class AnnotationTest2 {

  @MyAnnotation(value1={"a","b"})
  public void execute(){
    System.out.println("method");
  }
}

10. Using inheritance in annotations:

By default comments will not be inherited to the subclasses, can add a java. When custom annotations lang. annotation. Inherited annotation statement using inheritance.

@Documented
@Retention(value=RUNTIME)
@Target(value=ANNOTATION_TYPE)
public @interface Inherited indicates that annotation types are automatically inherited. If an Inherited meta-annotation exists in an annotation type declaration, and the user queries for that annotation type in a class 1 declaration that does not have an annotation of that type, the annotation type is automatically queried in the superclass of that class. This process is repeated until a comment of this type is found or the top level of the class hierarchy (Object) is reached. If no superclass has an annotation of that type, the query indicates that the current class has no such annotation.

Note that this meta-annotation type is invalid if you use anything other than an annotation class. Also note that this meta-annotation only contributes to inheriting annotations from a superclass; The annotation on the implemented interface is invalid.


Related articles: