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:

  1. Annotation methods take no parameters, such asname().website();
  2. Annotation methods return value types: primitive type, String, Enums, Annotation, and array types of the previous types
  3. Annotation methods can have default values, such asdefault "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.