I’m sure you’ve all used Lombok in your projects because it simplifies a lot of our code, but it doesn’t have a lot of functionality. So what exactly is Lombok? Lombok is a tool that uses simple annotations to help us simplify and eliminate some of the bloated Java code that we have to have, such as creating a new class and putting a few fields in it, And then usually we have to manually create getters and setters and constructors and stuff like that, and lombok is there to save us the trouble of manually creating that code, and it automatically generates those methods when we compile the source code.

So how does Lombok do this? The bottom line is that compile-time annotations are used.

How do I use Lombok

Lombok is an open source project, and the code is in Lombok. If it is a Gradle project, you can reference it in your project as follows.

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

Copy the code

function

So what does Lombok do? A simple example is the ability to add annotations to automatically generate methods that make our code simpler and easier to understand. Take the following class for example.

 1@Data

2public class TestLombok {

3    private String name;

4    private Integer age;

5

6    public static void main(String[] args) {

7        TestLombok testLombok = new TestLombok();

8        testLombok.setAge(12);

9        testLombok.setName("zs");

10    }

11}

Copy the code

We use Lombok’s Data annotations to use get and set methods even when they are not written. If we look at the compiled class file, we can see that it automatically generates get and set methods for us.

 1public class TestLombok {

2    private String name;

3    private Integer age;

4

5    public static void main(String[] args) {

6        TestLombok testLombok = new TestLombok();

7        testLombok.setAge(12);

8        testLombok.setName("zs");

9    }

10

11    public TestLombok(a) {

12    }

13

14    public String getName(a) {

15        return this.name;

16    }

17

18    public Integer getAge(a) {

19        return this.age;

20    }

21

22    public void setName(String name) {

23        this.name = name;

24    }

25

26    public void setAge(Integer age) {

27        this.age = age;

28    }

29

30}

Copy the code

Lombok does more than that, of course, and there are plenty of other annotations to make it easier to develop. There are plenty of ways to use Lombok on the web, so I won’t bother you here. Normally when we customize annotations in a project, or use @Controller, @Service, etc. in the Spring framework, these are runtime annotations, and runtime annotations are mostly implemented by reflection. Lombok is implemented using compile-time annotations. So what are compile-time annotations?

Compile-time annotations

Annotations (also known as metadata) give us a formalized way to add information to our code that we can easily use at a later point in time. —————— from Thinking in Java

Annotations in Java are divided into runtime annotations and compile-time annotations. Runtime annotations are the ones we often use to reflect information about our annotations while the program is running and then do something with them. What are compile-time annotations? Annotation handlers are used during compilation of the program.

  • Compile time: The Compile time of the Java language is an uncertain process of operation, as it may be the process of theJava 'files into'.classDocumentation process; It can also refer to the process of converting bytecode into machine code; Or it could be directly*.javaThe process of compiling native machine code
  • Runtime: The process of loading bytecode files from the JVM into memory and unloading them when they are finally used is runtime.

Annotation processing tool APT

Annotation Processing Tool apt(Annotation Processing Tool) is a Tool that Sun provides to help with Annotation Processing. Apt is designed to work with Java source files, not compiled classes.

It is a tool of Javac, Chinese for compile-time annotation processor. APT can be used to scan and process annotations at compile time. Through APT, we can obtain information about annotations and annotated objects. After obtaining these information, we can automatically generate some codes according to the requirements, eliminating manual writing. Note that retrieving annotations and generating code are done at compile time, which greatly improves program performance compared to reflection processing annotations at run time. The core of APT is the AbstractProcessor class.

In normal cases, APT tools can only generate some files (not only class files, but also XML files and so on), and can not modify the original file information.

But at this point you might wonder, doesn’t Lombok just add some information to our existing files? As I’ll explain in more detail later, Lombok is modifying the abstract syntax tree AST in Java to modify information about its original classes.

Next we’ll show you how to generate a class file using the APT tool, and then we’ll talk about how Lombok modifies properties in existing classes.

Custom annotation

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

1@Retention(RetentionPolicy.SOURCE) // Annotations are reserved only in the source code

2@Target(ElementType.TYPE) // Used to modify classes

3public @interface GeneratePrint {

4

5    String value();

6}

Copy the code

The Retention annotation has an attribute value that is an enumerated class of type RetentionPolicy, which has three values in it.

1public enum RetentionPolicy {

2

3    SOURCE,

4

5    CLASS,

6

7    RUNTIME

8}

Copy the code
  • SOURCEEmbellished annotations: Embellished annotations, indicating that the information about the annotations is discarded by the compiler and does not remain in the class file; the information about the annotations only remains in the source file
  • CLASSDecorated annotations: Information indicating annotations is retained in class files (bytecode files) when the program is compiled, but not read by the virtual machine at runtime
  • RUNTIMEDecorated annotations: Information indicating annotations is retained in class files (bytecode files) and is retained by the virtual machine at runtime when the program is compiled.So it can be called by reflection, so normal runtime annotations use this parameter

The Target annotation also has an attribute value, which is an enumeration of type ElementType. Is used to modify where the annotation is applied.

 1public enum ElementType {

2    TYPE,

3

4    FIELD,

5

6    METHOD,

7

8    PARAMETER,

9

10    CONSTRUCTOR,

11

12    LOCAL_VARIABLE,

13

14    ANNOTATION_TYPE,

15

16    PACKAGE,

17

18    TYPE_PARAMETER,

19

20    TYPE_USE

21}

Copy the code

Defining annotation handlers

To define an annotation processor, we need to extend the AbstractProcessor class. The basic framework type after inheritance is as follows

 1@SupportedSourceVersion(SourceVersion.RELEASE_8)

2@SupportedAnnotationTypes("aboutjava.annotion.MyGetter")

3public class MyGetterProcessor extends AbstractProcessor {

4    @Override

5    public synchronized void init(ProcessingEnvironment processingEnv) {

6    super.init(processingEnv);

7    }

8

9    @Override

10    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

11        return true;

12    }

13}

Copy the code

We can see that in the subclass there are two annotations above. The annotations are described below

  • @SupportedSourceVersion: Indicates the supported Java version
  • @SupportedAnnotationTypes: represents the annotation to be processed by the handler

Two methods that inherit from the parent class are described below

  • Init method: Mainly to get some environment information at compile time
  • Process method: A method executed by the compiler at compile time. That’s where we write the concrete logic

We are demonstrating how to generate classes at compile time by inheriting AbstractProcessor classes, so we write our generated class code in the process method. As shown below.

 1@Override

2public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

3    StringBuilder builder = new StringBuilder()

4            .append("package aboutjava.annotion; \n\n")

5            .append("public class GeneratedClass {\n\n"// open class

6            .append("\tpublic String getMessage() {\n"// open method

7            .append("\t\treturn \"");

8    // for each javax.lang.model.element.Element annotated with the CustomAnnotation

9    for (Element element : roundEnv.getElementsAnnotatedWith(MyGetter.class)) {

10        String objectType = element.getSimpleName().toString();

11        // this is appending to the return statement

12        builder.append(objectType).append(" says hello! \\n");

13    }

14    builder.append("\"; \n"// end return

15            .append("\t}\n"// close method

16            .append("}\n"); // close class

17    try { // write the file

18        JavaFileObject source = processingEnv.getFiler().createSourceFile("aboutjava.annotion.GeneratedClass");

19        Writer writer = source.openWriter();

20        writer.write(builder.toString());

21        writer.flush();

22        writer.close(a);

23    } catch (IOException e) {

24        // Note: calling e.printStackTrace() will print IO errors

25        // that occur from the file already existing after its first run, this is normal

26    }

27    return true;

28}

Copy the code

Define classes that use annotations (test classes)

The above two classes are the basic utility classes, one defines the annotation, one defines the annotation handler, and next we define a test class (testano.java). We add our own annotation class to the class.

1@MyGetter

2public class TestAno {

3

4    public static void main(String[] args) {

5        System.out.printf("1");

6    }

7}

Copy the code

So we can generate files at compile time, so I’m going to show you how to generate files at compile time, so don’t rush into javac compilation right now, MyGetter class is the annotation class and MyGetterProcessor is the processor for the annotation class, The handler will be triggered when we compile the TestAnoJava file. So these two classes cannot compile together.

So let me show you my directory structure

1aboutjava

2    -- annotion

3        -- MyGetter.java

4        -- MyGetterProcessor.java

5        -- TestAno.java

Copy the code

So let’s first compile the annotation class and the annotation handler class

1javac aboutjava/annotion/MyGett*

Copy the code

Next, we compile our test class by adding the Processor parameter to specify the relevant annotation processing class.

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

Copy the code

As you can see in the motion picture, Java files are automatically generated.

The code address

conclusion

A second part of this article will explain Lombok’s principles and how to modify the content of existing classes. This article provides a primer on what an annotation handler is and how to use it to do things you can only do at compile time. I hope you can try it out on this machine yourself, and feel free to point out any questions you have with this article.

Interested can pay attention to my new public number, search [program ape 100 treasure bag]. Or just scan the code below.

reference

  • Versatile APT! The power of compile-time annotations
  • Compile time processing using annotation processor
  • Java Annotation Processing and Creating a Builder
  • Lombok principle analysis and function implementation
  • Java annotation processor – Modifies the syntax tree at compile time
  • The last piece of the puzzle | AOP AST abstract syntax tree — most lightweight methods of AOP
  • Java Annotation Processing and Creating a Builder
  • Java Abstract Syntax tree AST analysis and use