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 Data
without writing methods. We look at its compiled file, and we can see that it automatically generates methods for us.get、set
get、set
class
get、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 Spring
framework. 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
*.java
files into*.class
files; it may also refer to the process of converting bytecodes into machine code; it may also directly convert the*.java
cost 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 Lombok
isn'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 Lombok
is only by modifying the abstract syntax tree in Java to AST
modify the information of its original class.
Next, we will demonstrate how to APT
generate a class file with a tool, and then we will talk about Lombok
how 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 RetentionPolicy
an enumeration class of the type, RetentionPolicy
and 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 fileCLASS
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 runtimeRUNTIME
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 ElementType
an 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 process
write 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. MyGetter
Classes are annotation classes, MyGetterProcessor
but 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 processor
parameters 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
- The almighty APT! The magic 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 - modify syntax tree at compile time
- AOP last piece of the puzzle | AST abstract syntax tree - the most lightweight AOP method
- Java Annotation Processing and Creating a Builder
- Java Abstract Syntax Tree AST Analysis and Use