Java annotations are a new feature introduced in JDK5, and given that most frameworks today (such as Spring) use annotations to simplify code and improve coding efficiency, it is imperative for a Java engineer to have a good grasp of annotations and a deep understanding of them.

Understanding Java Annotations

In fact, Java annotations are not used much differently from normal modifiers (public, static, void, etc.). Here are some common annotations:

public class AnnotationDemo { @Test public static void A(){ System.out.println("Test....." ); } @Deprecated @SuppressWarnings("uncheck") public static void B(){ } }

By using the @Test annotation on a method, the Test framework automatically recognizes the method and calls it separately when the method is run. The @Test annotation is actually a tag annotation that acts as a tag and the runtime tells the Test framework that the method is a Test method. For @Deprecated and @SuppressWarns (” Uncheck “), these are Java’s built-in annotations. You’ll see them a lot in your code, but that’s not a good thing. After all, when a method or class has an @Deprecated annotation on it, @SuppressWarnings means to ignore a specific warning, such as @SuppressWarnings(” Uncheck “). This is the easiest way to use an annotation, so let’s look at the basic syntax of an annotation definition

The basic grammar

Declare annotations and meta annotations

Let’s take a look at how the previous Test annotation declares it:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {}

We declared the Test annotation using the @interface annotation and passed in the elementType.method parameter using the @target annotation to indicate that @test can only be used on methods. @Retention(RetentionPolicy. Runtime) is used to indicate that the annotation lifetime is RUNTIME. The definition of the annotation code looks a lot like the definition of the interface, and it does, after all, generate a test.class file after compilation. For @Target and @Retention, meta-annotations are provided by Java. Meta-annotations are annotations that mark other annotations, which are described below

@target is used to constrain where an annotation can be applied (such as a method, class, or field), where elementType is an enumerated type, defined as follows, and also represents a range of possible values

Public enum elementType {/** indicates that the annotation can be used for a class, an interface, or an enum declaration */ TYPE. /** indicates that the annotation can be used for a field declaration. Include enum instances */ FIELD, /** to indicate that the annotation can be used for METHOD declarations */ METHOD, /** to indicate that the annotation can be used for PARAMETER declarations */ PARAMETER, /** indicates that the annotation can be used for CONSTRUCTOR declarations */ CONSTRUCTOR. /** indicates that the annotation can be used for local variable declarations */ LOCAL_VARIABLE. /** annotation can be used for annotation declarations (applied to another annotation)*/ ANNOTATION_TYPE, /** annotation can be used for PACKAGE declarations */ PACKAGE, /** * indicates that the annotation can be used for type parameter declarations (1.8 new) * @since 1.8 */ TYPE_PARAMETER, /** ** type usage declarations (1.8 new) * @since 1.8 */ TYPE_USE}

Note that when an annotation does not specify a Target value, the annotation can be used on any element, and multiple values are contained within {} and separated by commas, as follows:

@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})

@Retention is used to constrain the lifetime of an annotation with three values: source, class, or runtime.

  • Source: Annotations are discarded by the compiler (this type of annotation information is only kept in the SOURCE code. After the SOURCE code has been compiled, the annotation information is discarded and not kept in the compiled class file).
  • CLASS: Annotations are available in class files, but discarded by the JVM (annotation information of this type is retained in the source code and class files, and is not loaded into the virtual machine during execution). Note that when annotations do not have a Retention value defined, the default value is Class, such as Java built-in annotations. @Override, @Deprecated, @SuppressWarnning, etc
  • The RUNTIME: Annotations are also retained at runtime (JVM), so they can be read by reflection (source code, class files, and at execution time), such as @Controller, @Autowired, @RequestMapping in SpringMVC, etc.
Annotate elements and their data types

@Test annotation annotation annotation annotation annotation annotation annotation annotation annotation annotation annotation annotation annotation annotation annotation annotation annotation annotation annotation annotation annotation annotation annotation annotation annotation annotation annotation annotation annotation This will be seen in the following example:

/** * Created by Wuzejian on 2017/5/18. */ @Target(elementType.type) @Retention(RetentionPolicy. Runtime) public @interface DBTable { String name() default ""; }

This defines an annotation called DBTable, which is used primarily for mapping database tables to Bean classes (a full case study will be shown later). Unlike the Test annotation, we declare a String name element, which is null by default. However, it is important to note that the declaration for any element should be a method declaration, with the option to use default to provide a default value. @DBTable can be used as follows:

@DBTable(name = "MEMBER")
public class Member {
   //
}

In addition to the String data types mentioned above, the following data types are also supported

All basic types (int, float, Boolean, byte, double, char, long, short)

String

Class

enum

Annotation

An array of the above types

If use other data types, the compiler will throw out a compiler error, note that declare annotation elements can use basic types but not allowed to use any packing type, at the same time should also be noticed that annotations can also act as the type of element, which is nested comments, the following code illustrates the use of the above type process:

@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface Reference{ boolean next() default false; } public @interface AnnotationElementDemo { enum Status {FIXED,NORMAL}; Status status() default Status.FIXED; boolean showSupport() default false; String name()default ""; Class<? > testCase() default Void.class; Reference reference() default @Reference(next=true); long[] value(); }
Compiler restrictions on default values

The compiler is a little picky about the default value of the element. First, elements cannot have indeterminate values. That is, the element must either have a default value or provide the element’s value when the annotation is used. Secondly, for the basic types of elements, whether the statement in the source code, or in the annotation interface defined in the default values, can with null as the value, this is the limit, there is no use to speak of, but due to the existence of an element or missing, because each statement annotations, all elements are present, and has the value of the corresponding To get around this limitation, you can define only special values, such as an empty string or a negative number, to indicate that an element does not exist.

Annotations do not support inheritance

Annotations are not support inheritance, so you can’t use keyword extends to inherit a @ interface, but annotation in the compiled, the compiler will automatically inherit the Java. Lang. The annotation. The annotation interface, here we decompiling the previously defined DBTable annotation

package com.zejian.annotationdemo;
import java.lang.annotation.Annotation;
public interface DBTable extends Annotation{
    public abstract String name();
}

Although it turns out that the DBTable Annotation inherits the Annotation interface after decompile, keep in mind that even though Java’s interfaces can implement multiple inheritance, the extends keyword cannot be used to inherit @Interface when defining an Annotation.

A shortcut

The shortcut is when an element named value is defined in the annotation, and if that element is the only one that needs to be assigned to the annotation, then instead of using the key=value syntax, you can simply give the value required by the value element in brackets. This can be applied to elements of any legal type, and remember that this restricts the element name to value. A simple example is as follows

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface IntegerVaule{
    int value() default 0;
    String name() default "";
}
public class QuicklyWay {

    
    @IntegerVaule(20)
    public int age;

    
    @IntegerVaule(value = 10000,name = "MONEY")
    public int money;

}
Java built-in annotations and other meta-annotations

Override: A method used to indicate that this method overrides a method of a parent class. The source code is as follows

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

@deprecated: Indicates an outdated method or class. The source code is as follows. Analyzed about @Documented later:

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

SuppressWarnnings: Optionally disable compiler warnings on classes, methods, member variables, and variable initializations. The implementation source code is as follows:

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

Inside it is a String array that accepts the following main values:

Deprecation: Warning when an undesirable class or method is used Unchecked: Warning when an unchecked conversion is performed, such as not specifying the type held by the collection when using the collection; Fallthrough: Warning when the Switch block moves directly to the next condition without a Break; Path: Warning if a path does not exist in the classpath, source file path, etc. Serial: Warning when the serialVersionUID definition is missing on a serializable class; Finally: Any finally clause does not complete properly; All: A warning about all of the above.

These three annotations are relatively simple, just look at a simple case:

@Deprecatedclass A{ public void A(){ } @Deprecated() public void B(){ } } class B extends A{ @Override public void A() {  super.A(); } @SuppressWarnings({"uncheck","deprecation"}) public void C(){ } @SuppressWarnings("uncheck") public void D(){ } }

We analyzed the two meta-annotations, @Target and @Retension. In addition to these two meta-annotations, Java provides two other meta-annotations, @Documented and @Inherited. These are described below:

The annotated @Documented annotation is generated into the Javadoc

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

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

@DocumentA
@DocumentB
public class DocumentDemo {
    public void A(){}
}

Generate documentation using the javadoc command:

As follows:

You can see that an annotation defined using the @Documenta meta-annotation (@Documenta) will be generated in a Javadoc, whereas @Documentb will not appear in a Doc document. That’s what the meta-annotation @Documented is for.

@inherited can be Inherited, but this isn’t really Inherited. By using @inherited, we can getAnnotations from Class objects using getAnnotations(), as follows:

@Inherited @Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface DocumentA {} @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface DocumentB {} @DocumentA class A{ } class  B extends A{ } @DocumentB class C{ } class D extends C{ } public class DocumentDemo { public static void main(String...  args){ A instanceA = new B(); System.out.println(" @inherited :"+Arrays.toString(InstanceA.getClass ().getAnnotations())); C instanceC = new D(); System.out.println(" @inherited :"+Arrays.toString(Instance.getClass ().getannotations ())); } / * * * run results: already use @ Inherited annotations: [@ com. Zejian. Annotationdemo. DocumentA ()] didn't use the @ Inherited note: [] * /}

Annotations and reflection mechanisms

After the previous decompilation, we know that all Java annotations inherit the Annotation interface, which means Java uses the Annotation interface to represent the Annotation element, which is the parent interface for all Annotation types. Meanwhile, in order to accurately obtain relevant information of annotations at runtime, Java adds AnnotatedElement interface under the java.lang.Reflect package, which is mainly used to represent the elements that have used annotations in the program currently running in JVM. The Constructor Class, Field Class, Method Class, Package Class, and Class Class implement the AnnotatedElement interface. It briefly means the following (see the deeper understanding of Java type information (Class objects) and reflection mechanism for more details) :

Field () : Field () : Method () : Method () : Method () : Method () : Method () : Method () : Method () : Method () : Method (

The following are the related API methods in AnnotatedElement that each of the five classes implement

The return value Method names instructions
A extends Annotation getAnnotation(Class annotationClass) The element returns annotations of the specified type if they exist, otherwise null.
Annotation[] getAnnotations() Returns all annotations that exist on this element, including those inherited from the parent class
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) Returns true if an annotation of the specified type exists on this element, or false otherwise.
Annotation[] getDeclaredAnnotations() Returns all annotations that exist directly on this element. Note that annotations of the parent class are not included. The caller is free to modify the returned array at will. This has no effect on the array returned by the other callers, otherwise an array of length 0 is returned

A simple example is shown below:

@DocumentA class A{ } @DocumentB public class DocumentDemo extends A{ public static void main(String... args){ Class<? > clazz = DocumentDemo.class; DocumentA documentA=clazz.getAnnotation(DocumentA.class); System.out.println("A:"+documentA); Annotation[] an= clazz.getAnnotations(); System.out.println("an:"+ Arrays.toString(an)); Annotation[] an2=clazz.getDeclaredAnnotations(); System.out.println("an2:"+ Arrays.toString(an2)); boolean b=clazz.isAnnotationPresent(DocumentA.class); System.out.println("b:"+b); /** * Result: A:@com.zejian.annotationdemo.DocumentA() an:[@com.zejian.annotationdemo.DocumentA(), @com.zejian.annotationdemo.DocumentB()] an2:@com.zejian.annotationdemo.DocumentB() b:true */ } }

Runtime annotation handler

Now that you’ve learned about the API for annotations and reflection, let’s take a look at an example (adapted from Tinkin Java by the author) to demonstrate the process of assembling a database SQL build statement using runtime annotations

/** * Created by Wuzejian on 2017/5/18. */ @Target(elementType.type) @Retention(RetentionPolicy.Runtime) public @interface DBTable { String name() default ""; } /** * Created by Wuzejian on 2017/5/18. * @Target(elementType.field) @Retention(RetentionPolicy.RUNTIME) public @interface SQLInteger { String name() default ""; Constraints constraint() default @Constraints; } /** * Created by Wuzejian on 2017/5/18. * String * /@Target(elementType.field) @Retention(RetentionPolicy.RUNTIME) public @interface SQLString { String name() default ""; int value() default 0; Constraints constraint() default @Constraints; } /** * Created by Wuzejian on 2017/5/18. * Constraints */ @Target(elementType.field) @Retention(RetentionPolicy. Runtime) public @interface Constraints { boolean primaryKey() default false; boolean allowNull() default false; boolean unique() default false; } /** * Created by Wuzejian on 2017/5/18. */ @DBTable(name = "Member ") public class Member {** Created by Wuzejian on 2017/5/18. @SQLString(name = "ID",value = 50, constraint = @Constraints(primaryKey = true)) private String id; @SQLString(name = "NAME" , value = 30) private String name; @SQLInteger(name = "AGE") private int age; @SQLString(name = "DESCRIPTION" ,value = 150 , constraint = @Constraints(allowNull = true)) private String description; }

The above defines four annotations, @DBTable(on the class), @Constraints(on the field), @SQLString(on the field), and uses these annotations in the Member class. The purpose of these annotations is to help the annotation handler generate the build statement that creates the database table MEMBER. Note that we use the nested annotation @Constraints, which is used to determine if a field is null or unique. It is important to recognize that the annotation provided above must have a @Retention(RetentionPolicy.Runtime) lifetime so that you can use reflection to retrieve its information. With the above comments and use, is to write the rest of the annotation processor, we talked a lot in front of the annotations, its processor are either Java itself has to provide, or, has provided the framework of our own are not involved in the writing of the annotation processor, but the above definition processing SQL annotation, the processor must be written by our own, as follows

public class TableCreator { public static String createTableSql(String className) throws ClassNotFoundException { Class<? > cl = Class.forName(className); DBTable dbTable = cl.getAnnotation(DBTable.class); if(dbTable == null) { System.out.println( "No DBTable annotations in class " + className); return null; } String tableName = dbTable.name(); if(tableName.length() < 1) tableName = cl.getName().toUpperCase(); List<String> columnDefs = new ArrayList<String>(); for(Field field : cl.getDeclaredFields()) { String columnName = null; Annotation[] anns = field.getDeclaredAnnotations(); if(anns.length < 1) continue; if(anns[0] instanceof SQLInteger) { SQLInteger sInt = (SQLInteger) anns[0]; if(sInt.name().length() < 1) columnName = field.getName().toUpperCase(); else columnName = sInt.name(); columnDefs.add(columnName + " INT" + getConstraints(sInt.constraint())); } if(anns[0] instanceof SQLString) { SQLString sString = (SQLString) anns[0]; if(sString.name().length() < 1) columnName = field.getName().toUpperCase(); else columnName = sString.name(); columnDefs.add(columnName + " VARCHAR(" + sString.value() + ")" + getConstraints(sString.constraint())); } } StringBuilder createCommand = new StringBuilder( "CREATE TABLE " + tableName + "("); for(String columnDef : columnDefs) createCommand.append("\n " + columnDef + ","); String tableCreate = createCommand.substring( 0, createCommand.length() - 1) + ");" ; return tableCreate; Private static String getConstraints(Constraints) {private static String getConstraints(Constraints) {private static String getConstraints(Constraints) {private static String getConstraints(Constraints) {private static String getConstraints(Constraints) {private static String getConstraints(Constraints) constraints = ""; if(! con.allowNull()) constraints += " NOT NULL"; if(con.primaryKey()) constraints += " PRIMARY KEY"; if(con.unique()) constraints += " UNIQUE"; return constraints; } public static void main(String[] args) throws Exception { String[] arg={"com.zejian.annotationdemo.Member"}; for(String className : arg) { System.out.println("Table Creation SQL for " + className + " is :\n" + createTableSql(className)); * * *} / output: the Table Creation SQL for com. Zejian. Annotationdemo. The Member is: CREATE TABLE MEMBER( ID VARCHAR(50) NOT NULL PRIMARY KEY, NAME VARCHAR(30) NOT NULL, AGE INT NOT NULL, DESCRIPTION VARCHAR(150) ); * /}}

If you are familiar with reflection, the above code is simple, we pass the full path of Member by class.forName () method to get the Member of the Class object, and then use the method in the Class object to get all the Member fields Field, Finally using field. GetDeclaredAnnotations () through each field of annotation by annotation type judgment to build to build table SQL statement. This is a simple processor model that uses annotations combined with reflection to build SQL statements. Remember Hibernate?

Annotations enhancements in Java 8

Yuan notes @ the Repeatable

The meta annotation @Repeatable, which is new to JDK1.8, means that the same annotation is repeated in the same place. Without the annotation, it is generally impossible to use the same annotation on the same type

@FilterPath("/web/update")
@FilterPath("/web/add")
public class A {}

Before Java8, if we wanted to implement something similar, we would have defined an array element to receive multiple values when we defined the @FilterPath annotation as follows

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface FilterPath {
    String [] value();
}

@FilterPath({"/update","/add"})
public class A { }

But after Java8 added the @Repeatable annotation, it can be defined and used in the following way

package com.zejian.annotationdemo;
import java.lang.annotation.*;
/**
 * Created by zejian on 2017/5/20.
 */
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(FilterPaths.class)
public @interface FilterPath {
    String  value();
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface FilterPaths {
    FilterPath[] value();
}

@FilterPath("/web/update")
@FilterPath("/web/add")
@FilterPath("/web/delete")
class AA{ }

We can simply understand that after using @Repeatable, the @FilterPaths annotation will be used as the container to receive repeated annotations on the same type, and each @FilterPath will be responsible for saving the specified path string. In order to deal with the new annotation, Java8 AnnotatedElement interface added getDeclaredAnnotationsByType () and getAnnotationsByType () two methods and provides a default implementation in the interface, When specifying @Repeatable annotations, information about the annotations can be obtained through these two methods. Note, however, that getDeclaredAnnotation() and getAnnotation() in the old API do not handle @repeatable annotations (unless the annotation is not repeated on the same declaration). Note getDeclaredAnnotationsByType method to obtain the annotation does not include the parent class, in fact, when getAnnotationsByType () method is invoked, its internal performed getDeclaredAnnotationsByType method first, GetAnnotationsByType () continues to look from the parent class only if the specified annotation does not exist in the current class, but note that it is still not available if @FilterPath and @FilterPaths do not use @Inherited. Here is the code to demonstrate:

public @interface FilterPath { String value(); } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface FilterPaths { FilterPath[] value(); } @FilterPath("/web/list") class CC { } @FilterPath("/web/update") @FilterPath("/web/add") @FilterPath("/web/delete") class AA extends CC{ public static void main(String[] args) { Class<? > clazz = AA.class; FilterPath[] annotationsByType = clazz.getAnnotationsByType(FilterPath.class); FilterPath[] annotationsByType2 = clazz.getDeclaredAnnotationsByType(FilterPath.class); if (annotationsByType ! = null) { for (FilterPath filter : annotationsByType) { System.out.println("1:"+filter.value()); } } System.out.println("-----------------"); if (annotationsByType2 ! = null) { for (FilterPath filter : annotationsByType2) { System.out.println("2:"+filter.value()); }} System.out.println(" The result of using getAnnotation :"+clazz. GetAnnotation (filterPath.class)); / * * * the execution result (the current class owns the annotations FilterPath, from CC a parent class is not looking for) 1:1: / web/update/web/add 1: / web/delete -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 2: / web/update 2:/web/add 2:/web/delete the result of using getAnnotation :null */}}

If the current class has the annotation @FilterPath, then the GetAnnotationsByType method will not look for it from the CC parent class. Let’s look at another case where there is no @FilterPath annotation on class AA

public @interface FilterPath { String value(); } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Inherited @interface FilterPaths { FilterPath[] value(); } @FilterPath("/web/list") @FilterPath("/web/getList") class CC { } class AA extends CC{ public static void main(String[] args) { Class<? > clazz = AA.class; FilterPath[] annotationsByType = clazz.getAnnotationsByType(FilterPath.class); FilterPath[] annotationsByType2 = clazz.getDeclaredAnnotationsByType(FilterPath.class); if (annotationsByType ! = null) { for (FilterPath filter : annotationsByType) { System.out.println("1:"+filter.value()); } } System.out.println("-----------------"); if (annotationsByType2 ! = null) { for (FilterPath filter : annotationsByType2) { System.out.println("2:"+filter.value()); }} System.out.println(" The result of using getAnnotation :"+clazz. GetAnnotation (filterPath.class)); / * * * the execution result (the current class without @ FilterPath getAnnotationsByType method from CC a parent class) 1:1: / web/list/web/getList -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- The result of using getAnnotation :null */}}

Notice when we define @FilterPath and @FilterPath we have to specify @inherited, GetAnnotationsByType or we still can’t get @FilterPath annotations from the parent class. Why is that? Check out the getAnnotationsByType method’s source code:

default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) { T[] result = getDeclaredAnnotationsByType(annotationClass); if (result.length == 0 && this instanceof Class && AnnotationType.getInstance(annotationClass).isInherited()) { Class<? > superClass = ((Class<? >) this).getSuperclass(); if (superClass ! = null) { result = superClass.getAnnotationsByType(annotationClass); } } return result; }
Two new elementTypes

In Java8, elementType adds two enumerated members, TYPE_PARAMETER and TYPE_USE. Before Java8, annotations can only be annotated on a single declaration (such as field, class, method). After Java8, elementType adds two enumerated members, TYPE_PARAMETER and TYPE_USE. The new TYPE_PARAMETER can be used to annotate type parameters, while TYPE_USE can be used to annotate any type (not including class). As shown below.

class D<@Parameter T> { }

class Image implements @Rectangular Shape { }

new @Path String("/usr/bin")

String path=(@Path String)input;

if(input instanceof @Path String)

public Person read() throws @Localized IOException.List<@ReadOnly ? extends Person>List<? extends @ReadOnly Person>

@NotNull String.class 
import java.lang.@NotNull String

The TYPE_USE annotation is used to support strong type checking in Java programs, in conjunction with third-party plug-in tools (such as the Checker Framework). Can detect the runtime at compile-time error (such as UnsupportedOperationException, NullPointerException), avoid exceptions to the run-time discovered, so as to improve the quality of the code, this is the main function of type annotations. In summary, Java 8 adds two new annotation element types, elementType.type_use and elementType.type_parameter, with which we can apply annotations to a variety of new situations.

OK ~, about annotations for the time being this, in fact, there is a large piece of knowledge did not discuss in detail, the source code level annotation processor, this topic blogger is going to open another analysis.

Unoriginal!!
fromhttps://www.cnblogs.com/tuanz…