Lombok is often used, but do you know how it works?

I believe that everyone has used Lombok in the project, because it can simplify a lot of our code, but there are not many functions. So what exactly is lombok? Lombok is a tool that can help us simplify and eliminate some must-have but bloated Java code in the form of simple annotations. In simple terms, for example, we create a new class, and then write in it A few fields, and then usually we need to manually create getter and setter methods, constructors, etc. The role of lombok is to save us the trouble of manually creating these codes, it can be used when we compile the source code. Automatically generate these methods for us.

So how exactly does Lombok do this? In fact, the bottom layer is to use the function of compile-time annotation.

How to use Lombok

Lombok is an open source project, the code is in lombok , if it is a gradle project, it can be directly referenced in the project as follows.

compile ("org.projectlombok:lombok:1.16.6")

Function

So what does Lombok do? In fact, it is very simple. One of the simplest examples is to automatically generate some methods by adding annotations to make our code more concise and easy to understand. For example the following class.

@Data
public class TestLombok {
    private String name;
    private Integer age;

    public static void main(String[] args) {
        TestLombok testLombok = new TestLombok();
        testLombok.setAge(12);
        testLombok.setName("zs");
    }
}

We use the annotations provided by Lombok , and we can use its methods Datawithout writing methods. We look at its compiled file, and we can see that it automatically generates methods for us.get、setget、setclassget、set

public class TestLombok {
    private String name;
    private Integer age;

    public static void main(String[] args) {
        TestLombok testLombok = new TestLombok();
        testLombok.setAge(12);
        testLombok.setName("zs");
    }

    public TestLombok() {
    }

    public String getName() {
        return this.name;
    }

    public Integer getAge() {
        return this.age;
    }

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

    public void setAge(Integer age) {
        this.age = age;
    }

}

Of course, the functions of Lombok are more than that. There are many other annotations to help us develop easily. There are many ways to use Lombok on the Internet, so I won't repeat them here. Under normal circumstances, we customize annotations in the project, or use such annotations in the Springframework. Most of the runtime annotations are implemented through reflection. Instead , it is implemented using compile-time annotations. So what are compile-time annotations?@Controller、@Service运行时注解Lombok

compile-time annotations

> Annotations (also known as metadata) provide a formal way for us to add information to our code, making it very convenient for us to use that data at a later point in time. ——————Excerpted from "Thinking in Java"

Annotations in Java are divided into runtime annotations and compile-time annotations . Runtime annotations are the information we often use to get our annotations through reflection when the program is running, and then do some operations. And what are compile-time annotations? It is processed by the annotation processor during the compilation time of the program.

  • Compilation period: The compilation period of the Java language is an uncertain operation process, because it may be the process of converting *.javafiles into *.classfiles; it may also refer to the process of converting bytecodes into machine code; it may also directly convert the *.javacost of compilation process of machine code
  • Runtime: The process from JVM loading bytecode files into memory to unloading after final use belongs to the scope of runtime.

Annotation processing tool apt

> Annotation Processing Tool apt (Annotation Processing Tool), which is a tool provided by Sun to help the annotation processing process, apt is designed to operate Java source files, not compiled classes.

It is a tool of javac, which in Chinese means compile-time annotation processor. APT can be used to scan and process annotations at compile time. Information about annotations and annotated objects can be obtained through APT. After obtaining this information, we can automatically generate some codes according to requirements, eliminating the need for manual writing. Note that obtaining annotations and generating code are all done at code compilation time, which greatly improves program performance compared to reflection processing annotations at runtime. The core of APT is the AbstractProcessor class.

Under normal circumstances, using the APT tool can only generate some files ( not only the class files we imagine, but also xml files, etc. ), and cannot modify the original file information.

But it is estimated that there will be doubts at this time, so Lombokisn't it just adding some information to our original file? I will give a detailed explanation later. Here is a brief introduction. In fact, it Lombokis only by modifying the abstract syntax tree in Java to ASTmodify the information of its original class.

Next, we will demonstrate how to APTgenerate a class file with a tool, and then we will talk about Lombokhow to modify the properties in an existing class.

define annotations

First of all, of course we need to define our own annotations

@Retention(RetentionPolicy.SOURCE) // 注解只在源码中保留
@Target(ElementType.TYPE) // 用于修饰类
public @interface GeneratePrint {

    String value();
}

Retention There is an attribute value on the annotation, which is RetentionPolicyan enumeration class of the type, RetentionPolicyand there are three values ​​in the enumeration class.

public enum RetentionPolicy {

    SOURCE,

    CLASS,

    RUNTIME
}

  • SOURCE Modified annotation: Modified annotation, indicating that the information of the annotation will be discarded by the compiler and will not remain in the class file, and the information of the annotation will only remain in the source file
  • CLASS Decorated annotations: the information indicating the annotations is kept in the class file (bytecode file) when the program is compiled, but not read by the virtual machine at runtime
  • RUNTIME Decorated annotations: The information indicating the annotations is retained in the class file (bytecode file) when the program is compiled, and will be retained by the virtual machine at runtime. So it can be called through reflection, so the annotations use this parameter at normal runtime

Target There is also an attribute value on the annotation, which is ElementTypean enumeration of the type. It is used to modify where this annotation is used.

public enum ElementType {
    TYPE,

    FIELD,

    METHOD,

    PARAMETER,

    CONSTRUCTOR,

    LOCAL_VARIABLE,

    ANNOTATION_TYPE,

    PACKAGE,

    TYPE_PARAMETER,

    TYPE_USE
}

Defining Annotation Processors

If we want to define annotation processors, we need to inherit AbstractProcessor classes. After inheritance, the basic frame types are as follows

@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes("aboutjava.annotion.MyGetter")
public class MyGetterProcessor extends AbstractProcessor {
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
	super.init(processingEnv);
    }

    @Override
    public boolean process(Set<!--? extends TypeElement--> annotations, RoundEnvironment roundEnv) {
        return true;
    }
}

We can see that there are two annotations above in the subclass, the annotations are described as follows

  • @SupportedSourceVersion : Indicates the supported Java version
  • @SupportedAnnotationTypes : Indicates the annotation to be processed by this processor

Inherited two methods of the parent class, the methods are described as follows

  • init method: mainly to obtain some environmental information during compilation
  • process method: At compile time, the method executed by the compiler. This is where we write the specific logic

We are demonstrating how to AbstractProcessor generate classes at compile time by inheriting classes, so we processwrite the code for our generated classes in the method. As follows.

@Override
public boolean process(Set<!--? extends TypeElement--> annotations, RoundEnvironment roundEnv) {
    StringBuilder builder = new StringBuilder()
            .append("package aboutjava.annotion;\n\n")
            .append("public class GeneratedClass {\n\n") // open class
            .append("\tpublic String getMessage() {\n") // open method
            .append("\t\treturn \"");
    // for each javax.lang.model.element.Element annotated with the CustomAnnotation
    for (Element element : roundEnv.getElementsAnnotatedWith(MyGetter.class)) {
        String objectType = element.getSimpleName().toString();
        // this is appending to the return statement
        builder.append(objectType).append(" says hello!\\n");
    }
    builder.append("\";\n") // end return
            .append("\t}\n") // close method
            .append("}\n"); // close class
    try { // write the file
        JavaFileObject source = processingEnv.getFiler().createSourceFile("aboutjava.annotion.GeneratedClass");
        Writer writer = source.openWriter();
        writer.write(builder.toString());
        writer.flush();
        writer.close();
    } catch (IOException e) {
        // Note: calling e.printStackTrace() will print IO errors
        // that occur from the file already existing after its first run, this is normal
    }
    return true;
}

Defining classes using annotations (test classes)

The above two classes are the basic tool classes, one defines annotations, the other defines annotation processors, and then we define a test class (TestAno.java). We add our custom annotation class to the class.

@MyGetter
public class TestAno {

    public static void main(String[] args) {
        System.out.printf("1");
    }
}

In this way, we can generate files at compile time. Next, we will demonstrate generating files at compile time. At this time, don't rush to compile javac directly. MyGetterClasses are annotation classes, MyGetterProcessorbut they are processors of annotation classes. Then we are compiling TestAno Java . When the file is opened, the handler is triggered. So the two classes cannot be compiled together.

Let me show you my directory structure first

aboutjava
	-- annotion
		-- MyGetter.java
		-- MyGetterProcessor.java
		-- TestAno.java

So we first compile the annotation class and the annotation processor class

javac aboutjava/annotion/MyGett*

Next, compile our test class. At this time, we need to add processorparameters at compile time to specify the relevant annotation processing class.

javac -processor aboutjava.annotion.MyGetterProcessor aboutjava/annotion/TestAno.java

As you can see in the dynamic graph, the Java file is automatically generated.

code address

Summarize

There will be a second article in this article to explain the principle of Lombok and how to modify the content of the original class. As a prerequisite knowledge, this article briefly introduces what an annotation processor is and how to use an annotation processor to do things that we can only do at compile time. I hope you can test it on your own machine. If you have any questions in this article, please point out.

refer to

{{o.name}}
{{m.name}}

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=324057582&siteId=291194637