primers
I recently wrote an article about source code analysis for componentized open source frameworks (portal below). So now componentized little famous JIMU framework, is also my next to share with you the source code analysis article. But because there’s a lot of Java Annotation involved. So I have to do amway here, which is the origin of this article.
Excellent framework source code analysis series (a) make decoupling easier! Multiple processes – ModularizationArchitecture componentized framework
Annotations are ubiquitous in the Java world, but are generally ignored by most people. But when we design the SDK, when we design the base library, we use annotations to simplify the configuration. Anyone familiar with ButterKnife knows that it is implemented by adding Java code at compile time through annotations. If you don’t know how it works, you will after reading this and the next one.
Consumption of route
When learning new information, to master the correct way to eat, the brain must first have an anticipation of the structure of the knowledge, after learning the structure, then review the structure, remember the knowledge according to the structure. This article will explain according to the structure of the map.
Lay a solid foundation
Basics – Four meta-annotations
Meta-annotations are the annotations used to modify the annotations.
@Target(value=ElementType)
The @target is used to indicate the range of objects that this Annotation modifies (that is, where the described Annotation can be used). In Java, annotations can be used in the following places:
- Package, types (Class, interface, enumeration, Annotation type)
- Type members (methods, constructors, member variables, enumerated values)
- Method parameters and local variables (such as loop variables, catch parameters)
The scope of the annotation is specified by the value of Target. Once specified, the @target element must match its value, otherwise the compilation will report an error.
Value Common values are:
ElementType | meaning |
---|---|
CONSTRUCTOR | Used to describe a constructor |
FIELD | Used to describe fields (including enum constants) |
LOCAL_VARIABLE | Used to describe local variables |
METHOD | Used to describe methods |
PACKAGE | Describe packages |
PARAMETER | Describe parameters |
TYPE | Used to describe a class, interface (including annotation type), or enum declaration |
Note: PACKAGE, which is not used in general classes, but in the fixed file package-info.java. I want to emphasize that it must be package-info
As a special note, in previous Versions of Java, developers could only put annotations in declarations. But starting with Java 8, annotations can be written anywhere a type is used, such as declarations, generics, and casts:
@Encrypted String str;
List<@NonNull String> strs;
test = (@Immutable Test) tmpTest;
Copy the code
For this extension, JAVA8 extends the @target value by introducing a new TYPE annotation, namely, ElmentType:
ElementType | meaning |
---|---|
TYPE_PARAMETER | Notation annotations can be used in declarations of type variables (such as class Test {… }) |
TYPE_USE | Presentation annotations can be used in any statement that uses a type (such as declarative statements, generics, and strong conversions) |
Refer to above for an explanation of types.
@Retention(value=RetentionPolicy)
@retention, translated as Retention, indicates the Retention period for which a annotation will be retained. A modified annotation will be retained for one of the three phases specified by its value. If the annotation type declaration does not have Retention annotations, the Retention policy defaults to CLASS:
RetentionPolicy | meaning |
---|---|
RetentionPolicy.SOURCE | Retained only at the source level, it is ignored at compile time |
RetentionPolicy.CLASS | It is retained at compile time, in the class file, but ignored by the JVM |
RetentionPolicy.RUNTIME | Reserved by the JVM, so it can be read and used at run time by the JVM or other code that uses reflection |
@Documented
Documented annotation indicates that the annotation should be Documented by the Javadoc tool. By default, Javadoc does not include annotations. However, if @documented is specified when you declare an annotation, it will be handled by a tool like Javadoc, so the annotation type information will also be included in the resulting document.
@Inherited
When an annotation is used to modify a class and you want it to be Inherited by the sub-class of that class, you can use the @Inherited annotation for the annotation. The above statement highlights two points:
- The inheritability of annotations. When custom annotations want to be Inherited, the @Inherited modifier is used
- Inherited applies only to classes and does not apply to other types
How to write custom annotations -Java Annotation syntax
Custom annotations, defined by the @ symbol and interface keyword. Similar to class, for example:
package com.xm.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Target(ElementType.METHOD)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnno{
String name(a);
String website(a) default "example.com";
int version(a) default 1;
}
Copy the code
When an annotation requires parameters, we need to define the annotation’s method first. The method name is the parameter name. This is similar to the interface definition. If a parameter needs a default value, add default + default value to the end of the method to achieve this.
For example, in this example, we specify three parameters, name, website, and version. Three methods are defined. Name does not specify a default value, so it defaults to null, website specifies a default value of example.com, and version specifies 1 as its default.
Here are three rules to emphasize:
- Annotation methods take no parameters, such as
name()
.website()
; - Annotation methods return value types: primitive type, String, Enums, Annotation, and array types of the previous types
- Annotation methods can have default values, such as
default "example.com"
The default website =”example.com”
When we use it, we need to pass the value to the parameter, which is very simple:
@TestAnno(name="xiaoming", website="example.com", version=1)
Copy the code
The rest of this example will be explained in more detail later.
Essence of efficiency
For the sake of learning, we’ll use the most common Java built-in annotation @Override. Walk through the steps of custom annotation implementation.
1. Write a Java file that defines annotations
To customize an annotation, you first create a Java file named after the annotation and define the annotation using the @interface keyword
Override.java
package java.lang
public @interface Override {
}
Copy the code
2. Determine the scope of custom annotations
This is also easy to understand; the Override annotation modifies methods, so the scope is specified as METHOD.
3. Determine the duration of custom annotations
The retention times of SOURCE, CLASS, and RUNTIME increase in sequence. For Override, this annotation lets us know which methods are being overridden when we write code everyday. This tip, again, is only at the SOURCE level, so use SOURCE instead.
4. Define parameters and methods for custom annotations
Override does not use any parameters or methods, which are also ignored here, as we will focus on in the actual examples later.
Based on the above analysis, we wrote the following definition source code:
package java.lang;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
Copy the code
By following these three steps, we have defined an annotation. When we use it, we simply add the annotation name with the @ symbol. So, what’s the use of annotated elements?
First, we can automatically generate additional code for the element where the annotation is located through compile-time processing. This is true of things like ButterKnife, which helps us to generate bindings between views and components, bind click listeners, and so on. Annotations retained to this time are also called compile-time annotations.
Second, at runtime, we can get annotation information through reflection, and this annotation information is usually some information or configuration that the program writer wants to pass to the runtime to use, which can simplify configuration. Like Spring2.5, which uses a lot of runtime annotations, it has a wide range of applications in implementing AOP. It is important to note that these annotations are RUNTIME rules so that they can be retained at RUNTIME. So we call them runtime annotations.
This article focuses on suggestive annotations and runtime annotations during code writing. Let’s look at two more practical examples.
Practice real Combat boldly (I)
Example suggestive annotations
In Java, we often see other built-in annotations besides Override. For example, SuppressWarnings are used to tell the compiler to ignore certain warnings, such as using raw data types in generics.
package java.lang;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target( { ElementType.TYPE, ElementType.FIELD, ElementType.METHOD,
ElementType.PARAMETER, ElementType.CONSTRUCTOR,
ElementType.LOCAL_VARIABLE })
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
/** * The list of warnings a compiler should not issue. */
public String[] value();
}
Copy the code
Follow the steps we used in the previous section to write custom annotations to break them down:
The scope of annotations
SuppressWarnings can be used for all elements except the annotation type declaration and package name. So the Target here is specified accordingly.
The duration of an annotation
The warnings here are static syntax check type warnings, so this annotation needs to remain at the source level only. The SOURCE.
Annotated parameter methods
SuppressWarning Specifies a String array. It supports multiple string arguments. The value can be the warning type to be suppressed, as shown in the following table:
parameter | meaning |
---|---|
deprecation | Warning when an obsolete class or method is used |
unchecked | A warning when an unchecked transformation has been performed |
fallthrough | Warning when the Switch block enters the next case without breaking |
path | Warning when there is a non-existent path on the classpath, source file path, etc |
serial | Warning when a serializable class lacks a serialVersionUID definition |
finally | A warning when any finally clause does not complete properly |
all | Warnings for all of the above |
When used, the value can be assigned as follows:
@SupressWarning(value={"uncheck"."deprecation"})
Copy the code
Examples of runtime annotations
For our custom annotation TestAnno from the previous section, let’s practice with runtime annotations.
The preparatory work
package com.xm.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Target(ElementType.METHOD)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnno{
String name(a);
String website(a) default "example.com";
int version(a) default 1;
}
Copy the code
Suppose we apply this annotation to the following code block:
package com.xm.annotation;
public class AnnotationDemo {
@AuthorAnno(name="xiaoming", website="example.com", version=1)
public static void main(String[] args) {
System.out.println("I am main method");
}
@SuppressWarnings({ "unchecked"."deprecation" })
@AuthorAnno(name="suby", website="example2.com", version=2)
public void demo(a){
System.out.println("I am demo method"); }}Copy the code
Parsing for annotations
Now, at runtime, we parse the custom annotation @testanno via reflection. The reflection class is in the package java.lang.reflect, which has an interface AnnotatedElement that defines the core annotation methods, as follows:
The return value | methods | meaning |
---|---|---|
T | getAnnotation(Class annotationClass) | Returns the annotation if there is an annotation of the specified type for the element, and NULL otherwise |
Annotation[] | getAnnotations() | Returns all annotations that exist on this element |
Annotation[] | getDeclaredAnnotations() | Returns all annotations that exist directly on this element |
boolean | isAnnotationPresent(Class<? extends Annotation> annotationClass) | Returns true if there is an annotation of the specified type for the element, false otherwise |
In the previous example, we defined a custom annotation that applies to Method. Method extends from AccessibleObject, and AccessibleObject implements the AnnotatedElement interface. Annotationparser.java to parse @testanno.
package com.xm.annotation;
import java.lang.reflect.Method;
public class AnnotationParser {
public static void main(String[] args) throws SecurityException, ClassNotFoundException {
String clazz = "com.xm.annotation.AnnotationDemo";
Method[] demoMethod = AnnotationParser.class
.getClassLoader().loadClass(clazz).getMethods();
for (Method method : demoMethod) {
if (method.isAnnotationPresent(TestAnno.class)) {
AuthorAnno authorInfo = method.getAnnotation(TestAnno.class);
System.out.println("method: "+ method);
System.out.println("name= "+ authorInfo.name() +
" , website= "+ authorInfo.website()
+ " , version= "+authorInfo.version()); }}}}Copy the code
The output of the program:
method: public void com.xm.annotation.AnnotationDemo.demo()
name= suby , website= example2.com , version= 2
method: public static void com.xm.annotation.AnnotationDemo.main(java.lang.String[])
name= xiaoming , website= example.com , version= 1
Copy the code
Thus, we can pass some runtime information to the runtime code through annotations when writing the code to achieve the purpose of information passing and configuration. Spring also makes extensive use of runtime annotations, which greatly facilitates program configuration and development, especially AOP programming.
The next article will focus on compile-time annotations, which can be found in many well-known frameworks or tool-based SDKS and can be used to make development easier in a very elegant and concise way.
Xiaoming produced, must be a boutique
Welcome to pay attention to xNPE technology forum, more original dry goods daily push.