Javac compilation custom annotation and analysis annotation implementation of Lombok

1, Ask questions with Lombok

1.1 INTRODUCTION

1. Open settings (shortcut key: ctrl+alt+s) in idea, search plugin, search lombok in plugins, and install

2. Introduce lombok dependencies into the project

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.24</version>
</dependency>
Copy code

1.2 advantages and disadvantages

Lombok is a Java library that can automatically insert editors and build tools to simplify java development. By adding annotations, you do not need to write getter s or r eques ts methods for classes, and you can automate log variables. Official website link

Advantages: simplify Java development and reduce many duplicate codes.

Disadvantages:

  • Reduce the readability and integrity of the source code;
  • It may destroy encapsulation, because some attributes do not need to be exposed;
  • Reduce the debuggability;
  • Lombok will help us generate a lot of code automatically, but these codes are generated during compilation, so these codes may be "lost" in the development and debugging stages, which brings great inconvenience to debugging code.

If you don't consider it so rigorous, I think it's still necessary to use it, because I'm lazy.

1.3 use

Write a class to analyze:

A JavaBean written by ourselves

/**
 * @description:
 * @author: Yihui Wang
 * @date: 2022 At 20:23 on July 6
 */

public class Student {

    private String name;

    private String age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }
}
Copy code

JavaBean s annotated with lombok

/**
 * @description:
 * @author: Yihui Wang
 * @date: 2022 At 20:23 on July 6
 */
@Data
public class StudentLombok {
    private String name;
    private String age;
}
Copy code

Let's compile it. In Idea, click Build on the top menu and select Recompile from the drop-down menu to see what kind of class files they generate.

It is obvious that after using the @Setter and @Getter annotations, the compiled result is the same as that of the Java code written manually.

It directly helps us generate these methods. Who did these steps? Can we also write such annotations by ourselves?

2, Lombok principle analysis

In fact, the compile time weaving technology of AOP programming is used, which is to modify the final class file during compilation.

Most of the program code will follow the steps shown in the following figure from the beginning of compilation to the final conversion into the object code of the physical machine or the instruction set executed by the virtual machine:

Javac compilation process

To sum up, it mainly consists of the following three processes:

  • Analysis and input to symbol table
  • Annotation Processing
  • Semantic analysis and generation of class files

Lombok is implemented by annotation processing. Lombok uses the JSR 269: Pluggable Annotation Processing API (annotation processor at compile time) implemented by JDK 6, which allows annotation processing at compile time, reading, modifying, and adding content in the abstract syntax tree.

In fact, at this point, we only know that it is dealt with in this step, but we still know nothing about how to deal with it.

Later, we will manually implement the @Getter and @Setter annotations in Lombok. Here, we will explain the knowledge that may be involved in advance.

  • The main tools used are jdk source code tools JA bag
  • The api used is mainly under the com.sun.tools.javac package
  • Abstract syntax JCTree usage

It doesn't matter if I don't understand it. I don't understand it very well. Haha, I just came to explore it because of curiosity

The most important one is the AbstractProcessor Abstract annotation processing class involved, and the api related to JCTree. I don't use these words much, and I don't dare to speak casually.

To implement the annotation processor, the first thing to do is to inherit the abstract class javax.annotation.processing Abstractprocessor, and then rewrite its process() method, which is the process to be executed by javac compiler when executing annotation processor code.

/**
An abstract annotation processor, designed to be a convenient superclass for most concrete annotation processors.
 */
public abstract class AbstractProcessor implements Processor {
    /**
     * Processing environment providing by the tool framework.
     */
    protected ProcessingEnvironment processingEnv;
    private boolean initialized = false;


    /**
   If the processor class annotates with SupportedOptions, it returns a set that cannot be modified, which is the same as the string set of the annotation.
   If the class does not have such a comment, an empty set is returned
     */
    public Set<String> getSupportedOptions() {
        SupportedOptions so = this.getClass().getAnnotation(SupportedOptions.class);
        if  (so == null)
            return Collections.emptySet();
        else
            return arrayToSet(so.value());
    }

    /**
If the processor class is annotated with SupportedAnnotationTypes, an immutable collection is returned,
This set has the same set of strings as the annotation. If the class does not have such a comment, an empty set is returned.
return: 
The name of the annotation type supported by this processor. If not, it is an empty set
     */
    public Set<String> getSupportedAnnotationTypes() {
            SupportedAnnotationTypes sat = this.getClass().getAnnotation(SupportedAnnotationTypes.class);
            if  (sat == null) {
                if (isInitialized())
                    processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING,
                                                             "No SupportedAnnotationTypes annotation " +
                                                             "found on " + this.getClass().getName() +
                                                             ", returning an empty set.");
                return Collections.emptySet();
            }
            else
                return arrayToSet(sat.value());
        }

    /**
If the processor class is annotated with SupportedSourceVersion, the source version is returned in the annotation.
If the class does not have such a comment, sourceversion.release is returned_ six 
     */
    public SourceVersion getSupportedSourceVersion() {
        SupportedSourceVersion ssv = this.getClass().getAnnotation(SupportedSourceVersion.class);
        SourceVersion sv = null;
        if (ssv == null) {
            sv = SourceVersion.RELEASE_6;
            if (isInitialized())
                processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING,
                                                         "No SupportedSourceVersion annotation " +
                                                         "found on " + this.getClass().getName() +
                                                         ", returning " + sv + ".");
        } else
            sv = ssv.value();
        return sv;
    }

    /**
This method has two parameters, "annotations" indicates the annotation set to be processed by this processor;
"roundEnv" Represents the syntax tree node in the current Round,
Each syntax tree node represents an Element (javax.lang.model.element.ElementKind can view the relevant elements).
     */
    public abstract boolean process(Set<? extends TypeElement> annotations,
                                    RoundEnvironment roundEnv);

  
}
Copy code

In addition, there are two notes for matching:

@SupportedAnnotationTypes("*")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
Copy code

@SupportedAnnotationTypes indicates which annotations the annotation processor is interested in, "*" indicates that it is interested in all annotations@ SupportedSourceVersion indicates the highest version of Java code that this annotation processor can handle.

3, Simple Lombok implementation

Brief description

First, let's talk about what we want to implement. For simple understanding, I only discuss the get and set methods here. In fact, the implementations are similar. If we want to say something different, it is that the APIs of javac called are different.

Wrote two comments: @MyGetter and @MySetter and their processor MyAnnotationProcessor

Annotation processor, as the name suggests, is used to process annotations.

Project structure:

As it is a maven project, com.sun Tools, so you need to add it to Maven's pom file, so that when you use Maven to package, you won't report an error.

<dependency>
    <groupId>com.sun</groupId>
    <artifactId>tools</artifactId>
    <version>1.8</version>
    <scope>system</scope>
    <systemPath>jdk route/lib/tools.jar</systemPath>
 </dependency>
Copy code

Here, we use Java SPI to load the custom annotator to generate a jar package, similar to Lombok, so that once other applications reference this jar package, the custom annotator can take effect automatically.

SPI is a service discovery standard provided by java. Please see SPI introduction But every time we need to create our own services directory and configuration files, google's autoservice can help us save this step.

    <dependency>
        <groupId>com.google.auto.service</groupId>
        <artifactId>auto-service</artifactId>
        <version>1.0-rc5</version>
    </dependency>
Copy code

If you use Processor (javax.annotation.processing.Processor), and your metadata file is included in a jar package, and the jar package is under the classpath of javac (java compilation), javac will automatically execute the implementation class of Processor injected in this way to realize the extension of relevant data in the project.

Using AutoService will automatically generate meta-inf./services/javax.annotation.processing Processor file, and the content in the file is the class we dynamically inject into.

Then, when compiling, the corresponding extension method will be executed and the file will be written at the same time.

Tags: Java intellij-idea programming language

Posted by jf3000 on Tue, 09 Aug 2022 11:59:23 +0300