In the past, “XML” was the favorite of all the big frameworks, doing almost all the configuration in the framework in a loosely coupled way, but as projects got bigger and bigger, the content of “XML” became more complex and expensive to maintain.
Hence the idea of “annotations”, a symbolic, highly coupled configuration. You can annotate methods, you can annotate classes, you can annotate field properties, you can annotate almost anything you need to configure.
There has been a debate for many years about the two different configuration modes of “annotations” and “XML”, each with its own pros and cons. Annotations provide greater convenience, are easy to maintain and modify, but are highly coupled, while XML is the opposite of annotations.
The pursuit of low coupling is to abandon high efficiency, and the pursuit of efficiency is bound to encounter coupling. The purpose of this article is not to distinguish between the two, but to introduce the basic content of annotation in the simplest language.
The nature of annotations
“Java. Lang. The annotation. The annotation” the interface have so a word, used to describe the “annotation”.
The common interface extended by all annotation types
All Annotation types inherit from this ordinary Annotation interface.
This sentence is a little abstract, but illustrates the essence of annotation. Let’s look at the definition of a built-in JDK annotation:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
Copy the code
This is the definition of the @override annotation, which essentially says:
public interface Override extends Annotation{
}
Copy the code
Yes, an Annotation is essentially an interface that inherits the Annotation interface. At this point, you can decompile any annotation class and you’ll get results.
An annotation is simply a special annotation, and without the code to parse it, it can be worse than a comment.
Annotations that parse a class or method usually take two forms: a direct compile-time scan and a run-time reflection. We’ll talk about reflection later, but a compiler scan is when the compiler detects that a class or method has been modified with some annotations during the compilation of Java code bytecode, and then it does something with those annotations.
The typical example is the @Override annotation. Once the compiler detects that a method has been modified by the @Override annotation, the compiler checks to see if the method signature of the current method actually overrides a method of the parent class, that is, if there is an identical method signature in the parent class.
This kind of circumstance is only applicable to the compiler has been known as the annotation type, such as the JDK several built-in annotations, and your custom annotations, the compiler is don’t know you this annotation, also don’t know how to deal with, of course, is often just can choose according to the scope of the annotation is compiled into bytecode file, that’s all.
Yuan notes
A meta-comment is a comment that modifies a comment, usually in the definition of a comment, for example:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
Copy the code
This is our definition of the @Override annotation. You can see that the @target and @Retention annotations are what we call “meta-annotations”. Meta-annotations are usually used to specify information about an annotation’s life cycle and purpose.
There are several meta annotations in JAVA:
- @target: The Target of the annotation
- @Retention: The life cycle of annotations
- Documented: Whether an annotation should be included in a JavaDoc document
- @Inherited: Whether a child class is allowed to inherit this annotation
Where @Target is used to specify the Target of the modified annotation, i.e., is your annotation intended to modify the method? Modify class? Also used to modify field properties.
@target is defined as follows:
We can pass the value as follows:
@Target(value = {ElementType.FIELD})
Copy the code
Annotations modified by the @target annotation will only apply to member fields, not to methods or classes. Where ElementType is an enumerated type with the following values:
- Elementtype. TYPE: Allows modified annotations to be applied to classes, interfaces, and enumerations
- Elementtype. FIELD: Allows action on attribute fields
- Elementtype. METHOD: Allows action on methods
- Elementtype. PARAMETER: Allows action on method parameters
- Elementtype. CONSTRUCTOR: Allows action on a CONSTRUCTOR
- Elementtype. LOCAL_VARIABLE: Allows action on local variables
- Elementtype. ANNOTATION_TYPE: Allows annotation
- Elementtype. PACKAGE: Allows action on packages
@retention is used to specify the lifetime of the current annotation, which is basically defined as follows:
Again, it has a value attribute:
@Retention(value = RetentionPolicy.RUNTIME
Copy the code
Here RetentionPolicy is still an enumeration type, and it has the following enumeration values:
- Retentionpolicy. SOURCE: The current annotation is visible at compile time and is not written to the class file
- Retentionpolicy. CLASS: discarded during CLASS loading and written to the CLASS file
- Retentionpolicy. RUNTIME: permanently saved and can be retrieved by reflection
The @Retention annotation specifies the lifetime of the annotation to be modified, either by being visible only at compile time and discarded afterwards, or by being compiled into a class file by the compiler. Classes, methods, and even fields have property sheets. The JAVA virtual machine also defines several annotation property tables to store annotation information, but this visibility cannot be carried to the method area and is discarded when the class is loaded. The last one is permanent visibility.
The remaining two types of annotations are not commonly used and are relatively simple. They will not be introduced in detail here. You only need to know their respective functions. Documented An annotation that will be saved to aDoc document when we perform JavaDoc document packaging and discarded when we perform JavaDoc document packaging. Inherited annotations are inheritable. This means that our annotations modify a class, and the children of that class automatically inherit the annotations from the parent class.
There are three built-in annotations in JAVA
In addition to the above four meta-annotations, the JDK predefined three other annotations for us:
- @Override
- @Deprecated
- @SuppressWarnings
The @override annotation is a familiar one, defined as follows:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
Copy the code
It doesn’t have any attributes, so it can’t store any other information. It only works on methods and is discarded after compilation.
So you see, it is a kind of typical “tag type annotation”, only by the compiler, the compiler to compile the Java file into a byte code in the process, once detected on a method to modify the annotations, whether she will go to the parent class horse has a same method signature function, if not, naturally can not compile.
The basic definition of @deprecated is as follows:
It is still a permanent “markup annotation” that can modify all types. The effect is that marking the current class or method or field is no longer recommended and may be removed in the next JDK release.
Of course, the compiler doesn’t force you to do anything, it just tells you that the JDK no longer recommends using the current method or class and suggests you use an alternative.
SuppressWarnings @SuppressWarnings is used to suppress Java warnings. Its basic definition is as follows:
It has a value attribute that you need to actively pass a value, and what does that value stand for? That value stands for the type of warning that needs to be suppressed. Such as:
public static void main(String[] args) {
Date date = new Date(2018, 7, 11);
}
Copy the code
With such a piece of code, the program starts with a warning from the compiler.
Warning:(8, 21) Java: Java.util.Date Date(int,int,int) in java.util
If we don’t want the compiler checking for outdated methods in our code when the program starts, we can suppress the compiler’s checking by using the @SuppressWarnings annotation and passing a parameter value to its value attribute.
@SuppressWarning(value = "deprecated")
public static void main(String[] args) {
Date date = new Date(2018, 7, 11);
}
Copy the code
You’ll notice that the compiler no longer checks for stale method calls under main, suppressing the compiler’s ability to check for such warnings.
Of course, there are many warning types in JAVA, all of which correspond to a string. You can suppress the check for these warning types by setting the value of the value attribute.
You can use syntax like the following to define a custom annotation.
public @interface InnotationName{
}
Copy the code
Of course, you can optionally use meta-annotations when customizing annotations, so that you can specify the life cycle and scope of your annotations more specifically.
Annotations and reflections
We’ve covered the details of Annotation usage and briefly stated that “annotations are essentially an interface that inherits the Annotation interface.” Now let’s look at the nature of annotations at the virtual machine level.
First, we define a custom annotation type:
Here we specify that the Hello annotation can only modify fields and methods, and that the annotation is permanent so that we can reflect the fetch.
As we said earlier, the VIRTUAL machine specification defines a set of annotation-related property sheets, meaning that fields, methods, or classes themselves, if modified by annotations, can be written into bytecode files. There are several types of property tables:
- Visible annotation RuntimeVisibleAnnotations: runtime
- Invisible annotation RuntimeInVisibleAnnotations: runtime
- Annotations RuntimeVisibleParameterAnnotations: visible at runtime parameters are given
- RuntimeInVisibleParameterAnnotations: runtime invisible method parameter annotation
- AnnotationDefault: The default value for annotation class elements
The goal of showing you the attributes associated with these annotations for the virtual machine is to build up a basic impression of how annotations are stored in the bytecode file as a whole.
So, for a Class or interface, the Class Class provides the following methods for reflecting annotations.
- GetAnnotation: Returns the specified annotation
- IsAnnotationPresent: Determines whether the current element is annotated
- GetAnnotations: Returns all the annotations
- GetDeclaredAnnotation: Returns the specified annotation for this element
- GetDeclaredAnnotations: Returns all annotations for this element, excluding those inherited from its parent class
Methods, fields, and related reflection annotations are similar and will not be covered here. Let’s look at a complete example below.
First, set a virtual machine startup parameter to capture JDK dynamic proxy classes.
-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
And then main.
As we said, annotations are essentially interfaces that inherit the Annotation interface, and when you get an instance of an Annotation class using reflection, our getAnnotation method, the JDK actually generates a proxy class that implements our annotations (interfaces) using dynamic proxies.
When we run the program, we see a proxy class in the output directory that looks like this after decompilation:
The proxy class implements the interface Hello and overwrites all of its methods, including the Value method and methods that interface Hello inherits from the Annotation interface.
And who is the key InvocationHandler instance?
AnnotationInvocationHandler is dedicated to handling in JAVA annotation Handler, the design of this class is also very interesting.
Here is a memberValues, which is a Map key-value pair. The key is the name of our annotation property, and the value is the original value assigned to the property.
The invoke method is interesting. Notice that our proxy class proxies all methods in the Hello interface, so calls to any method in the proxy class are referred to here.
Var2 refers to the method instance being called, and here the variable var4 is used to get the concise name of the method, and then the switch structure determines who is currently calling the method, and if it’s one of the four largest methods in the Annotation, it assigns var7 to a specific value.
If the current is the method called toString, equals, hashCode, annotationType, AnnotationInvocationHandler instance already predefined good implementations of these methods, can be called directly.
So if var7 does not match these four methods, it means that the current method calls a custom annotation byte declaration method, such as the value method of our Hello annotation. In this case, the value for this annotation attribute will be retrieved from our annotation map.
In fact, annotation design in JAVA personally feels a little anti-human, obviously is a property operation, must use methods to implement. Of course, if you have a different opinion, feel free to leave a comment.
Finally, let’s summarize how reflection annotations work:
First, we can assign values to annotation properties in the form of key-value pairs, like this: @hello (value = “Hello”).
Next, you modify an element with an annotation, and the compiler scans the annotation on each class or method at compile time, does a basic check to see if your annotation is allowed in its current position, and writes the annotation information to the element’s property sheet.
Then, when you make reflection, virtual machine will all life cycle in the RUNTIME of annotation on a map, and create a AnnotationInvocationHandler instance, the map is passed to it.
Finally, the virtual machine uses the JDK dynamic proxy mechanism to generate a target annotation proxy class and initialize the handler.
Then, an annotation instance is created, it is essentially a proxy class, you should be good to understand AnnotationInvocationHandler invoke method in the implementation of the logic, it is the core. In a nutshell, the annotation property value is returned by method name.
All the code, images and files in this article are stored in the cloud on my GitHub:
(https://github.com/SingleYam/overview_java)
Welcome to follow the wechat official account: OneJavaCoder. All articles will be synchronized on the official account.