Introduction to common methods and principles of Lombok

A tool that simplifies source code and improves programming efficiency, and is used to generate commonly used code.

Two packages:

  • lombok

  • lombok.experimental (experimental properties)

how to use lombok

Introduce dependencies 

            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
            </dependency>

install plugin 

code used in

@Data
public class People {
    private String name;
}

Summary of common annotations

@val

When declaring a variable, the variable type can be inferred by itself, and it comes with a final attribute,

@Data

@ToString、@EqualsAndHashCode、@Getter、@Setter和@RequiredArgsConstrutor

@Slf4j

generate log object

@Value

Similar to @Data, with the following two differences:

  • A constructor with full parameters is generated;

  • only getter methods, no setter methods;

@Getter

Getter methods for all properties

@Setter

Setter methods for all properties

@Builder

Generate code for chained constructors

@Cleanup

Safely release or close resources, the most common scenario is the operation of closing streams in IO

@NonNull

getter : If the obtained property is null, throw NPE

setter : If the value passed in is null when setting the property, throw NPE

@ToString

Generate toString() method

@SneakyThrows

Eat the thrown exception and reduce some unnecessary try catch code

@NoArgsConstructor

Generate a no-argument constructor

@AllArgsConstructor

Add a Constructor with all properties

@EqualsAndHashCode

Generate equals() and hashcode() methods

@RequiredArgsConstrutor

A constructor that contains constants and variables marked NotNull will be generated

the case

The commented part is the code that lombok will help us generate automatically.

@Slf4j

@Slf4j
public class Test {
    // private static final Logger log=LoggerFactory.getLogger(Test.class);
    public static void main(String[] args) {
        log.info("Hello world");
    }
}

@Builder

        Test test = Test.builder()
                .id(id)
                .page(page)
                .build();

@Data
@Builder
public class Test {

    private String id;

    private String page;
    
    /**
     @java.beans.ConstructorProperties({"id", "page"})
    Test(Long id, int page) {
        this.id = id;
        this.page = page;
    }

    public static TestBuilder builder() {
        return new TestBuilder();
    }

    public TestBuilder toBuilder() {
        return new TestBuilder().id(this.id).page(this.page);
    }

    public static class TestBuilder {
        private Long id;
        private int page;

        TestBuilder() {}

        public TestBuilder id(Long id) {
            this.id = id;
            return this;
        }

        public TestBuilder page(int page) {
            this.page = page;
            return this;
        }

        public Test build() {
            return new Test(id, page);
        }

        public String toString() {
            return "Test.TestBuilder(id=" + this.id + ", page="
                + this.page")";
        }
    */
}

@SneakyThrows

    @Test(expected = RuntimeException.class)
    @SneakyThrows
    public void test_throw_exception() {
        when(HttpClientUtil.get(anyString()).thenThrow(new RuntimeException());
        api.test("nice");
    }

@Data

@Data
public class User {

    private Long id;

    private String username;

    private String password;
    
    
    /**
    public User() {}

    public Long getId() {return this.id;}

    public String getUsername() {return this.username;}

    public String getPassword() {return this.password;}

    public void setId(Long id) {this.id = id; }

    public void setUsername(String username) {this.username = username; }

    public void setPassword(String password) {this.password = password; }

    public boolean equals(Object o) {
        if (o == this) { return true; }
        ...
        return true;
    }

    public int hashCode() {
        final int PRIME = 59;
        int result = 1;
        final Object $id = this.getId();
        result = result * PRIME + ($id == null ? 43 : $id.hashCode());
        final Object $username = this.getUsername();
        ...
        return result;
    }

    protected boolean canEqual(Object other) {return other instanceof User;}

    public String toString() {
        return "User(id=" + this.getId() + ...+ ")";
    }
    */
	}
}

@Value

@Value
public class Test {

    (private final) Long id;

    (private final) String page;
    
    /**
    @java.beans.ConstructorProperties({"id", "page"})
    public Test(Long id, String page) {
        this.id = id;
        this.page = page;
    }

    public Long getId() {return this.id;}

    public String getPage() {return this.page;}

    public boolean equals(Object o) {
        if (o == this) { return true; }
          ...
        return true;
    }

    public int hashCode() {
        final int PRIME = 59;
        int result = 1;
        final Object $id = this.getId();
        result = result * PRIME + ($id == null ? 43 : $id.hashCode());
        ...
        return result;
    }

    public String toString() {
            return "Test.TestBuilder(id=" + this.id + ", page="
                + this.page")";
        }
    */
}

Java code convention

  • [Recommendation] For immutable entity classes with too many fields or too many constructor parameters, it is recommended to use lombok to generate the builder

  • [Recommendation] Before Record type data is supported, it is recommended that entity class fields use lombok annotations to generate setters and getters, and decide whether to use only @Getter, @Setter or @Data according to the principle of minimization

  • [Recommendation] In contract programming, it is recommended to use the non-empty annotation of IDEA IntelliJ > Spring > Lombok's annotation to indicate that the parameter is not empty

pros and cons

advantage

  • Effectively reduce the amount of code

  • dynamic. For example, when adding or subtracting fields, there is no need to worry about the modification of methods such as Getter/Setter 

  • Improves the readability of the code to a certain extent

shortcoming

  • Introduce external dependencies. Once lombok is used in the resource package, others will have to install plugins if they want to see your source code

  • Generated code is not intuitive and may not generate code as expected

  • Reduced source code integrity

common problem

@Builder

Property default value problem. If you use @Builder to generate code, the default value of the attribute will be invalid. You need to use @Builder.Default to mark the attribute with the default value, such as:

    @Builder
    public class UserQueryParam {
        private Long id;
        private String username;
        @Builder.Default
        private int page = 1;
        @Builder.Default
        private int size = 15;
    }

@Getter

Special Handling of Boolean Values

private boolean test;

1. The default generation rule for the method of reading attributes is is+attribute name instead of get+attribute name, that is, isTest instead of getTest

2. If the attribute name itself starts with is, such as isTest, the method to obtain the attribute is still isTest, not the awkward isIsTest

3. In the above two rules, there will be an extreme situation caused by unreasonableness, that is, there are two attributes, one is named isTest, and the other is test. This is essentially a design problem. For this situation, the plug-in The processing method is to generate only one isTest. As for which attribute is read, it depends on the order of the attributes, and the former takes precedence.

@Value and @Data

@Value is often used for immutable classes. An immutable class means that after an instance of the class is created, the instance variables of the instance cannot be changed.

Similar to @Data, there are two main differences:

  • A constructor with full parameters is generated;

  • only getter methods, no setter methods;

@Value

@Data

@Getter

@Setter

@ToString

@EqualsAndHashCode

@RequiredArgsConstrutor

@AllArgsConstructor 

@FieldDefaults(makeFinal=true, level=AccessLevel.PRIVATE)

Annotations identified by @Value cannot be used together with @RequestBody and jackson.

The reason why jackson deserialization fails after using @Value and fastjson can succeed:

 1. Jackson reads the set method, and @Value does not generate the set method, so it fails; 

2. Fastjson reads the constructor, and @Value generates the constructor, so it succeeds; 

Extension: When using fastjson, use @Builder to check whether @NoArgsConstructor or @AllArgsConstructor is added

Lombok principle

Modify the Abstract Syntax Tree (AST) at compile time

@Getter(AccessLevel.PUBLIC)
public class User {

    private Long id;

    private String username;

    @Getter(AccessLevel.PRIVATE)
    private String password;
}
public class User {

    private Long id;

    private String username;

    private String password;

    public Long getId() {return this.id;}

    public String getUsername() {return this.username;

    // Field 优先级更高
    private String getPassword() {return this.password;}
}

AST syntax tree

introduce

Lombok is processed in the AST syntax tree link. AST is a tree representation used to describe the grammatical structure of the program code. Each node of the grammatical tree represents a grammatical structure in the program code, such as package, type, modifier , operators, interfaces, return values, and even code comments can all be a grammatical structure.

sample

On Idea, you can also view the AST syntax tree of the code by installing the plug-in, as shown in the figure below

Each attribute and each method on the left can find the corresponding node on the right, so by manipulating the nodes of the AST tree, the code can be added dynamically during compilation.

JSR 269 

Since Java 6, javac has started to support the JSR 269 Pluggable Annotation Processing API specification. As long as the program implements the API, the defined annotations can be called when the java source code is compiled. The essence of Lombok is to rely on JSR 269 to realize the use of "Annotation Processor" (annotation processing tool) in the Javac compilation stage to preprocess the custom annotations and generate a "Class file" that is actually executed on the JVM.

From the above schematic diagram, it can be seen that Annotation Processing is a step between the compiler parsing the Java source code and generating the Class file.

lombok

Lombok is essentially a program that implements the " JSR 269 API ". In the process of using javac, the specific process of its function is as follows:

  1. javac analyzes the source code and generates an Abstract Syntax Tree (AST)

  2. Call the Lombok program that implements "JSR 269 API" during operation

  3. At this time, Lombok processes the AST obtained in the first step, finds the syntax tree (AST) corresponding to the class where the @Data annotation is located, and then modifies the syntax tree (AST) to add the corresponding tree nodes defined by the getter and setter methods

  4. javac uses the modified abstract syntax tree (AST) to generate bytecode files, that is, adding new nodes (code blocks) to the class

As can be seen from the flow chart of Lombok execution above, after Javac is parsed into an AST abstract syntax tree, Lombok dynamically modifies the AST according to the annotation processor written by itself, adding new nodes (that is, the Lombok custom annotation needs to generate code), and finally generate a JVM executable bytecode Class file through analysis.

core code

Multiple custom annotations in Lombok have corresponding handler processing classes. It is these handler classes that actually replace, modify and process their custom annotations in Lombok. For details of their implementation, please refer to code.

lombok plugin

When the method omitted using the Lombok annotation is called, an error that the definition cannot be found will be reported. In this case, some special processing is required. For example, in Intellij Idea, the Lombok plugin needs to be downloaded and installed.

Guess you like

Origin blog.csdn.net/xue_xiaofei/article/details/126145975