What does the annotation processor do

A Probe into Annotation Processor

    There is a very useful plug-in in the project, called lombok. It provides some simple annotations, which can be used to generate javabean and some getter/setter methods, which improves the efficiency of development and saves development time.
Today we will come Take a look at what method lombok uses to achieve this operation. In fact, lombok uses the annotation processor, which is a new feature added in jdk1.5. Like @Getter is just an annotation, and its real processing part
is in the annotation processor Realized inside. Official reference link .

Background introduction

    The full name of the annotation processor is actually Pluggable Annotation Processing API, a plug-in annotation processor. It is an implementation of the JSR269 proposal. For details, you can see the content in the link , the JSR269 link .
How does it work? You can refer to the following figure:

1. parse and enter: parse and enter, the java compiler will parse the source code to generate an AST (Abstract Syntax Analysis Tree)
at this stage 2.annotation processing: the annotation processor stage, the annotation processor will be called at this time, and the code can be verified at this time , Generate new files, etc. (you can loop to the first step after processing)
3.analyse and generate: analyze and generate, at this time, after the first two steps are completed, generate bytecode (understand sugar at this stage, such as type erasure)
These are actually just to give everyone a superficial impression of how it is implemented.

practice

    After reading the above information, you should have a general impression in your brain. Now let's write a simple example and practice it.
To use the annotation processor requires two steps:
1. Customize an annotation
2. Inherit AbstractProcessor And implement the process method

Let's write a very simple example, which is to add @InterfaceAnnotation to a class, and generate an interface class of "I" + class name when compiling.
First of all, I have defined two moudles, one for writing Annotations and handlers, the other is used to call annotations.

Step 1: Customize an annotation

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface InterfaceAnnotation {
}

1.@Target: indicates what this annotation is used on, here ElementType.TYPE refers to the use of the annotation on the class
2.@Retention: indicates the stage of retention, here RetentionPolicy.SOURCE is the source code stage, compile There is no such annotation on the latter class

Step 2: Inherit AbstractProcessor and implement the process method

@SupportedAnnotationTypes(value = {"com.example.processor.InterfaceAnnotation"})
@SupportedSourceVersion(value = SourceVersion.RELEASE_8)
public class InterfaceProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Messager messager = processingEnv.getMessager();
        messager.printMessage(Diagnostic.Kind.NOTE, "进入到InterfaceProcessor中了~~~");
        // 将带有InterfaceProcessor的类给找出来
        Set<? extends Element> clazz = roundEnv.getElementsAnnotatedWith(InterfaceAnnotation.class);
        clazz.forEach(item -> {
            // 生成一个 I + 类名的接口类
            String className = item.getSimpleName().toString();
            className = "I" + className.substring(0, 1) + className.substring(1);
            TypeSpec typeSpec = TypeSpec.interfaceBuilder(className).addModifiers(Modifier.PUBLIC).build();

            try {
                // 生成java文件
                JavaFile.builder("com.example.processor", typeSpec).build().writeTo(new File("./src/main/java/"));
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
        return true;
    }
}

1.@SupportedAnnotationTypes: Indicates what annotations this processor class will take effect
2.@SupportedSourceVersion: Indicates the supported java version
3.annotations: The required annotations are the annotations corresponding to @SupportedAnnotationTypes
4.roundEnv: Stores the current and previous rounds Processing environment information
5. TypeSpec may be a bit unintelligible, it is a class in javaPoet, javaPoet is a third-party plug-in used by java to generate java files is very useful, so this class is used here To generate java files, in
fact , java files can also be generated by using the PrintWriter and other input and output streams that come with java. There are many ways to generate files. The link to javaPoet . The javaPoet usage guide .
6. Messager is used to print output information, System .out.println is actually also possible;
7.If process returns true, the subsequent annotation processor will not process this annotation again, if it is false, in the next round of processing, other annotation processors will also process the modified annotation.

After writing, you need to specify the processor here, META-INF/services/javax.annotation.processing.Processor and write com.example.processor.InterfaceProcessor. If you don’t know what this is, you can read my other blog (Strength Promote XD) What is SPI?
We are compiling the annotation processor, and the plug-in settings in maven:

<plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.7.0</version>
        <configuration>
              <source>1.8</source>
              <target>1.8</target>
              <!-- 不加这一句编译会报找不到processor的异常-->
              <compilerArgument>-proc:none</compilerArgument>
        </configuration>
</plugin>

The directory structure at this time is like this:

.
├── HELP.md
├── pom.xml
├── processor.iml
└── src
    └── main
        ├── java
        │   └── com
        │       └── example
        │           └── processor
        │               ├── InterfaceAnnotation.java
        │               └── InterfaceProcessor.java
        └── resources
            └── META-INF
                └── services
                    └── javax.annotation.processing.Processor

Then mvn clean install.

Step 3: use annotations

Before use, if the annotation processor is compiled, introduce the jar package of the annotation processor.
Add @InterfaceAnnotation to the test class

@InterfaceAnnotation
public class TestProcessor {
}

maven specifies the annotation processor used during compilation.

<plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.7.0</version>
        <configuration>
              <source>1.8</source>
              <target>1.8</target>
              <encoding>UTF-8</encoding>
              <annotationProcessors>
                  <annotationProcessor>
                        com.example.processor.InterfaceProcessor
                  </annotationProcessor>
              </annotationProcessors>
        </configuration>
</plugin>

The directory structure at this time is

.
├── HELP.md
├── pom.xml
├── src
│   └── main
│       ├── java
│       │   └── com
│       │       └── example
│       │           └── test
│       │               └── TestProcessor.java
│       └── resources
└── test.iml

Then mvn compile, the java file is generated, and the directory structure is:

.
├── HELP.md
├── pom.xml
├── src
│   └── main
│       ├── java
│       │   └── com
│       │       └── example
│       │           ├── processor
│       │           │   └── ITestProcessor.java  // 这里就是生成的java文件
│       │           └── test
│       │               └── TestProcessor.java
│       └── resources
├── target
│   ├── classes
│   │   └── com
│   │       └── example
│   │           └── test
│   │               └── TestProcessor.class
│   ├── generated-sources
│   │   └── annotations
│   └── maven-status
│       └── maven-compiler-plugin
│           └── compile
│               └── default-compile
│                   ├── createdFiles.lst
│                   └── inputFiles.lst
└── test.iml

See the generated java file and you're done~

to sum up:

1.Java annotation processor can be used in many places, practical applications such as lombok, Android fragment generation, etc., using only one annotation can save a lot of code and improve efficiency;
2. This article just lists a very simple example, many The APIs in the annotation processor are not used. Readers who are interested can study by themselves, and there are APIs related to abstract syntax trees;
3. Annotation processors can be used to generate new classes to complete certain functions, but they cannot be directly Modify the current class.

Reference materials:

1.https://docs.oracle.com/javase/8/docs/api/javax/annotation/processing/Processor.html
2.https://jcp.org/aboutJava/communityprocess/final/jsr269/index.html
3.https://github.com/square/javapoet
4.https://www.cnblogs.com/throwable/p/9139908.html
5.http://notatube.blogspot.com/2010/11/project-lombok-trick-explained.html(介绍处理过程)
6.https://www.baeldung.com/java-annotation-processing-builder
7.http://hannesdorfmann.com/annotation-processing/annotationprocessing101

Guess you like

Origin blog.csdn.net/sc9018181134/article/details/95911937