The nature of annotations
There is a word in the "java.lang.annotation.annotation" interface that describes "annotations".
The common interface extended by all annotation types
All annotation types are inherited from this common interface (Annotation)
This sentence is a bit abstract, but it is the essence of the annotation. Let's look at the definition of a JDK built-in annotation:
@Target(ElementType.METHOD)@Retention(RetentionPolicy.SOURCE)public @interface Override {}复制代码
This is the definition of the annotation @Override, in fact it is essentially:
public interface Override extends Annotation{ }复制代码
Yes, the essence of annotations is an interface that inherits the Annotation interface. For this, you can decompile any of the annotation classes and you will get the results.
An annotation is, in the exact sense, just a special kind of comment that, if it is not parsed, may not even be commented.
And the interpretation of a class or method of annotations tend to have two forms, one is the compile-time direct scan, one is the run-time reflection. Reflection of things we'll say later, and the compiler's scan refers to the compiler in the process of compiling bytecode to Java code will detect a class or method is some annotation decoration, then it will be some processing of these annotations.
The typical annotation @Override, once the compiler detects that a method has been decorated with @Override annotations, the compiler checks whether the method signature of the current method actually overrides a method of the parent class, that is, whether the parent class has the same method signature.
This applies only to annotations that are already well-known to the compiler, such as several annotations built into the JDK, and your custom annotations, the compiler does not know what your annotations do, and of course, does not know how to handle them, often just based on the scope of the annotation to choose whether to compile into the bytecode file, that's all.
Meta annotations
Meta annotations are annotations that are used to modify annotations and are typically used in the definition of annotations, for example:
@Target(ElementType.METHOD)@Retention(RetentionPolicy.SOURCE)public @interface Override {}复制代码
This is the definition of our @Override annotations, and you can see the @Target, @Retention two annotations are what we call "meta annotations", and "meta annotations" are used to specify information such as the life cycle of an annotation and the purpose of the action.
There are several "meta annotations" in JAVA:
- @Target: The purpose of the annotation
- @Retention: The life cycle of annotations
- @Documented: Whether annotations should be included in the JAVADOC document
- @Inherited: Whether subclasses are allowed to inherit the annotation
Where is the @Target used to indicate to whom the modified annotations will ultimately function, that is to say, what is your annotation used to modify the method? Decorated class? is also used to modify the properties of a field.
The @Target is defined as follows:
We can pass the value for this value in the following way:
@Target(value = {ElementType.FIELD})复制代码
Annotations decorated with this @Target annotation will only work on member fields and cannot be used for cosmetic methods or classes. Where ElementType is an enumeration type with some of the following values:
- Elementtype.type: Allow modified annotations to function on classes, interfaces, and enumerations
- Elementtype.field: Allow action on attribute fields
- Elementtype.method: Allow action on the method
- Elementtype.parameter: Allow action on method parameters
- Elementtype.constructor: Allow action on the constructor
- Elementtype.local_variable: Allow local local variables to function
- Elementtype.annotation_type: Allow action on annotations
- Elementtype.package: Allowed to function on the package
@Retention is used to indicate the life cycle of the current annotation, and its basic definition is as follows:
Similarly, it also has a Value property:
@Retention(value = RetentionPolicy.RUNTIME复制代码
Here the Retentionpolicy is still an enumeration type, it has the following enumeration values to be desirable:
- Retentionpolicy.source: The current annotation compilation period is visible and will not be written to the class file
- Retentionpolicy.class: Class load stage discarded, write class file
- Retentionpolicy.runtime: Permanently saved, can be reflected to get
@Retention annotations Specify the life cycle of the modified annotations, one that is visible only at compile time, discarded after compilation, and compiled into a class file by the compiler, whether it is a class or method, or even a field, and they all have attribute tables, and JAVA The virtual machine also defines several annotation attribute tables for storing annotation information, but this visibility cannot be taken to the method area, which is discarded when the class is loaded, and the last is the visibility of permanent presence.
The remaining two types of annotations we do not use a lot of daily, but also relatively simple, here is no longer detailed introduction, you just need to know their respective role can. Annotations that @Documented annotation adornments are saved in the doc document when we execute the JAVADOC document package, and otherwise are discarded when packaged. @Inherited annotation-decorated annotations are inheritable, and our annotations decorate a class, and subclasses of that class automatically inherit that annotation from the parent class.
JAVA built-in three big annotations
In addition to the four meta annotations above, the JDK also predefined three additional annotations for us, which are:
- @Override
- @Deprecated
- @SuppressWarnings
@Override annotations must be familiar to everyone, and it is defined as follows:
@Target(ElementType.METHOD)@Retention(RetentionPolicy.SOURCE)public @interface Override {}复制代码
It does not have any properties, so it cannot store any other information. It can only be used on methods, and will be discarded when the compilation is finished.
So you see, it is a typical "tagged annotation", only by the compiler, the compiler in the process of compiling a Java file into bytecode, once detected a method is decorated with the annotation, it will go to the parent class whether the same method signature function, if not, nature can not be compiled.
The basic definition of @Deprecated is as follows:
is still a "tagged annotation", permanent, can be modified all types, the role is to mark the current class or method or field, etc. is no longer recommended to use, the next version of the JDK may be deleted.
Of course, the compiler does not force you to do anything, just to tell you that the JDK is no longer recommending the use of the current method or class, it is recommended that you use a substitute.
@SuppressWarnings is primarily used to suppress Java warnings, and its basic definition is as follows:
It has a value property that requires you to take the initiative, and this value represents what it means, and this value represents the type of warning that needs to be suppressed. For example:
public static void main(String[] args) { Date date = new Date(2018, 7, 11);}复制代码
This code, the compiler will report a warning when the program starts.
Warning: (8, +) date (int,int,int) in Java:java.util.Date is obsolete
And if we don't want the program to start, and the compiler checks for obsolete methods in the code, you can suppress the compiler's check by using the @SuppressWarnings annotation and passing a parameter value to its Value property.
"deprecated")public static void main(String[] args) { Date date = new Date(2018, 7, 11);}复制代码
This way you will find that the compiler no longer checks for outdated method calls under the main method, and suppresses the compiler's checking for such warnings.
Of course, there are many types of warnings in JAVA, they all correspond to a string, and by setting the value of the Value property, you can suppress the check for this type of warning.
The relevant content of the custom annotations is no longer mentioned, it is relatively simple, you can customize a note by using a syntax similar to the following.
public @interface InnotationName{ }复制代码
Of course, custom annotations can also be optionally decorated with meta-annotations, so you can more specifically specify the life cycle of your annotations, scope of action, and so on.
Annotations and Reflections
We introduce the details of the use of annotations, and simply mention that "the essence of annotations is an interface that inherits the Annotation interface", and now we're going to look at the nature of the annotations from the virtual machine level.
First, we customize an annotation type:
Here we specify that the note can only be used to modify fields and methods, and that the annotations are permanently alive so that we reflect the fetch.
As we said before, the virtual machine specification defines a series of attribute tables related to annotations, that is, whether the field, method, or class itself, if the annotation is decorated, it can be written into a bytecode file. The property sheet has the following types:
- Runtimevisibleannotations: Annotations visible at run time
- Runtimeinvisibleannotations: Annotations that are not visible at run time
- Runtimevisibleparameterannotations: Method parameter annotations visible at run time
- Runtimeinvisibleparameterannotations: Method parameter annotations that are not visible at run time
- Annotationdefault: Default value for annotation class elements
The purpose of these annotations of the virtual machines is to give you a basic impression of how the annotations are stored in the bytecode file.
Therefore, for a class or interface, the following methods are available in the class class for reflection annotations.
- Getannotation: Returns the specified annotation
- Isannotationpresent: Determines whether the current element is specified by a callout adornment
- Getannotations: Return all annotations
- Getdeclaredannotation: Returns the specified annotation for this element
- Getdeclaredannotations: Returns all annotations of this element, without inheriting from the parent class.
Method, the method of related reflection annotation in the field is basically similar, here no longer repeat, we see a complete example below.
First, set up a virtual machine startup parameter to capture the JDK dynamic proxy class.
-dsun.misc.proxygenerator.savegeneratedfiles=true
Then the main function.
As we said, annotations essentially inherit the interface of the Annotation interface, and when you get an instance of the annotation class through reflection, the Getannotation method here, the JDK actually generates a proxy class that implements our annotations (interfaces) through the dynamic proxy mechanism.
After we run the program, we will see that there is a proxy class in the output directory, after the decompile:
The proxy class implements the interface hello and overrides all its methods, including the value method and the method that interface hello inherits from the Annotation interface.
And who is this key Invocationhandler instance?
Annotationinvocationhandler is a Handler that is specifically designed to handle annotations in JAVA, and the design of this class is very interesting.
Here is a membervalues, which is a Map key value pair, the key is our annotation property name, the value is the value that the property was originally assigned.
And this invoke method is very interesting, you see, our proxy class proxy The Hello interface all the methods, so for any method in the proxy class call will be transferred here.
Var2 points to the invoked method instance, and here first uses the variable VAR4 to get the concise name of the method, and then the switch structure determines who the current calling method is, and if it is the four methods in Annotation, the VAR7 assigns a specific value.
If the method currently called is Tostring,equals,hashcode,annotationtype, the implementation of these methods is already predefined in the Annotationinvocationhandler instance and can be called directly.
So if VAR7 doesn't have a match on these four methods, it means that the current method calls the custom annotation byte declaration method, such as the value method of our Hello annotation. In this case, the value corresponding to this annotation property will be obtained from our annotation map.
In fact, the annotation design in JAVA personally feel a bit anti-human, obviously is the operation of attributes, not to use the method to achieve. Of course, if you have a different opinion, welcome to discuss the message.
Finally, let's summarize how the entire reflection annotation works:
First, we can assign a value to the annotation property in the form of a key-value pair, like this: @Hello (value = "Hello").
Then, you decorate an element with annotations, and the compiler scans the annotations on each class or method at compile time, making a basic check whether your annotations are allowed to function in the current position, and then writing the annotation information to the attribute table of the element.
Then, when you do the reflection, the virtual machine takes all the life-cycle annotations out of the RUNTIME into a map and creates a Annotationinvocationhandler instance to pass this map to.
Finally, the virtual machine will use the JDK dynamic agent mechanism to generate a proxy class for the target annotations, and initialize the good processor.
So, an example of an annotation is created, which is essentially a proxy class, and you should understand the implementation logic of the Invoke method in Annotationinvocationhandler, which is the core. In a nutshell, the value of the annotation property is returned by the method name .
Fundamentals of JAVA Annotations