1 introduction

Lombok is a handy tool that, like Google Guava, is highly recommended and should be used by every Java engineer. Lombok is a Java™ utility that helps developers eliminate Java’s verbose code, especially for simple Java objects (POJOs). It does this through annotations. By implementing Lombok in a development environment, developers can save much of the time spent building methods such as hashCode() and equals() and sorting various accessors and mutators.

2 IntelliJ Installs Lombok

  1. Install through IntelliJ’s plug-in center

  2. Install Plugin

  3. Finally, when using Lombok annotations, remember to import the Lombok. Jar package into your Project, and add dependencies in pom.xml if you are using a Maven Project.

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

3 Lombok usage

3.1 Lombok annotations

  • Val: is used before a local variable, making the variable final

  • NonNull: Adding this annotation to a method parameter automatically checks whether the parameter is null within the method. If it is null, it throws a NPE (NullPointerException).

  • Cleanup: Automatic management of resources, used in front of local variables, automatic Cleanup of resources before exiting at the end of the current variable scope, automatic generation of code like try-finally to close the stream

  • @getter / @setter: for properties, you no longer have to write your own setters and getters, and you can specify the scope of access

  • @ ToString: ToString(exclude= “id”); toString (exclude= “id”); Or @toString (callSuper=true, includeFieldNames=true) calls the ToString method of the superclass, containing all the attributes

  • EqualsAndHashCode: Automatically generates equals and hashCode methods on a class

  • @noArgsConstructor, @requiredargsConstructor and @AllargsConstructor: If you specify a staticName = “of”, you will also generate a static factory method that returns the class object. This is much more convenient than using a constructor

  • @DATA: The annotation is the class equivalent of using @toString, @EqualSandHashCode, @Getter, @setter, and @RequiredargsConstrutor, which are especially useful for POJO classes

  • @value: used on a class, is an immutable form of @data, equivalent to adding a final declaration to a property, providing only getters, but not setters

  • @ Builder: For classes, constructors, methods, giving you the complex Builder APIs, Person.builder().name(“Adam Savage”).city(“San Francisco”).job(“Mythbusters”).job(“Unchained “) Reaction”).build(); Refer to Builder for more information

  • SneakyThrows: Automatically throws checked exceptions without explicitly using the throws statement on the method

  • @ Synchronized: The lock object is a private property $lock or $lock. The synchronized keyword in Java is this. A lock on this or on an object of its own class has side effects. You can’t prevent uncontrolled code from locking this or class objects, which could lead to race conditions or other thread errors

  • Getter(lazy=true) : Can replace the classic Double Check Lock boilerplate code

  • Log: Generates different types of Log objects based on different annotations, but the instance name is Log. There are six optional implementation classes

  • @CommonsLog Creates log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);

    • @Log Creates log = java.util.logging.Logger.getLogger(LogExample.class.getName());
    • @Log4j Creates log = org.apache.log4j.Logger.getLogger(LogExample.class);
    • @Log4j2 Creates log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
    • @Slf4j Creates log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
    • @XSlf4j Creates log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);

3.2 Lombok Code Examples

Val sample

public static void main(String[] args) {
    val sets = new HashSet<String>();
    val lists = new ArrayList<String>();
    val maps = new HashMap<String, String>();
    //=> Equivalent to the following
    final Set<String> sets2 = new HashSet<>();
    final List<String> lists2 = new ArrayList<>();
    final Map<String, String> maps2 = new HashMap<>();
}
Copy the code

@ NonNull sample

public void notNullExample(@NonNull String string) {
    string.length();
}
/ / = > equivalent
public void notNullExample(String string) {
    if(string ! =null) {
        string.length();
    } else {
        throw new NullPointerException("null"); }}Copy the code

@ the Cleanup examples

public static void main(String[] args) {
    try {
        @Cleanup InputStream inputStream = new FileInputStream(args[0]);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
    / / = > equivalent
    InputStream inputStream = null;
    try {
        inputStream = new FileInputStream(args[0]);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } finally {
        if(inputStream ! =null) {
            try {
                inputStream.close();
            } catch(IOException e) { e.printStackTrace(); }}}}Copy the code

@ Getter / @ Setter example

@Setter(AccessLevel.PUBLIC)
@Getter(AccessLevel.PROTECTED)
private int id;
private String shap;
Copy the code

@ ToString sample

@ToString(exclude = "id", callSuper = true, includeFieldNames = true)
public class LombokDemo {
    private int id;
    private String name;
    private int age;
    public static void main(String[] args) {
        // Output LombokDemo(super=LombokDemo@48524010, name=null, age=0)
        System.out.println(newLombokDemo()); }}Copy the code

@ EqualsAndHashCode sample

@EqualsAndHashCode(exclude = {"id"."shape"}, callSuper = false)
public class LombokDemo {
    private int id;
    private String shap;
}
Copy the code

Examples: @noArgsConstructor, @requiredargsConstructor, and @AllargsConstructor

@NoArgsConstructor
@RequiredArgsConstructor(staticName = "of")
@AllArgsConstructor
public class LombokDemo {
    @NonNull
    private int id;
    @NonNull
    private String shap;
    private int age;
    public static void main(String[] args) {
        new LombokDemo(1."circle");
        // Use the static factory method
        LombokDemo.of(2."circle");
        // Constructs with no parameters
        new LombokDemo();
        // Include all parameters
        new LombokDemo(1."circle".2); }}Copy the code

@ the Data sample

import lombok.Data;
@Data
public class Menu {
    private String shopId;
    private String skuMenuId;
    private String skuName;
    private String normalizeSkuName;
    private String dishMenuId;
    private String dishName;
    private String dishNum;
    // Default threshold
    private float thresHold = 0;
    / / the new threshold
    private float newThresHold = 0;
    / / total score
    private float totalScore = 0;
}
Copy the code

@ the Value sample

@Value
public class LombokDemo {
    @NonNull
    private int id;
    @NonNull
    private String shap;
    private int age;
    / / equivalent to
    private final int id;
    public int getId(a) {
        return this.id; }... }Copy the code

@ Builder sample

@Builder
public class BuilderExample {
    private String name;
    private int age;
    @Singular
    private Set<String> occupations;
    public static void main(String[] args) {
        BuilderExample test = BuilderExample.builder().age(11).name("test").build(); }}Copy the code

@ SneakyThrows sample

import lombok.SneakyThrows;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
public class Test {
    @SneakyThrows(a)public void read(a) {
        InputStream inputStream = new FileInputStream("");
    }
    @SneakyThrows
    public void write(a) {
        throw new UnsupportedEncodingException();
    }
    / / equivalent to
    public void read(a) throws FileNotFoundException {
        InputStream inputStream = new FileInputStream("");
    }
    public void write(a) throws UnsupportedEncodingException {
        throw newUnsupportedEncodingException(); }}Copy the code

@ Synchronized sample

public class SynchronizedDemo {
    @Synchronized
    public static void hello(a) {
        System.out.println("world");
    }
    / / equivalent to
    private static final Object $LOCK = new Object[0];
    public static void hello(a) {
        synchronized ($LOCK) {
            System.out.println("world"); }}}Copy the code

@Getter(lazy = true)

public class GetterLazyExample {
    @Getter(lazy = true)
    private final double[] cached = expensive();
    private double[] expensive() {
        double[] result = new double[1000000];
        for (int i = 0; i < result.length; i++) {
            result[i] = Math.asin(i);
        }
        returnresult; }}// Equivalent to the following:
import java.util.concurrent.atomic.AtomicReference;
public class GetterLazyExample {
    private final AtomicReference<java.lang.Object> cached = new AtomicReference<>();
    public double[] getCached() {
        java.lang.Object value = this.cached.get();
        if (value == null) {
            synchronized (this.cached) {
                value = this.cached.get();
                if (value == null) {
                    final double[] actualValue = expensive();
                    value = actualValue == null ? this.cached : actualValue;
                    this.cached.set(value); }}}return (double[]) (value == this.cached ? null : value);
    }
    private double[] expensive() {
        double[] result = new double[1000000];
        for (int i = 0; i < result.length; i++) {
            result[i] = Math.asin(i);
        }
        returnresult; }}Copy the code

Lombok’s annotation principles

Speaking of Lombok, we need to mention JSR 269: Pluggable Annotation Processing API (www.jcp.org/en/jsr/deta…) . We had annotations before JSR 269, but we had to use reflection to get things done, and the reflection approach was very limited. First, it must define @retention as retentionpolicy.runtime, so that the annotation value can only be obtained by reflection at RUNTIME, making the RUNTIME code less efficient. Second, if you want to use annotations at compile time to do some checking, to report the user’s code is not reasonable error, the use of reflection method is powerless. After JSR 269, we can use annotations to do this at Javac compile time. So we find that the core distinction is at run time versus compile time.

As you can see from the figure above, Annotation Processing is a step between parsing and generation. Detailed steps are as follows:

The Lombok process is shown in the figure above. After Javac parses the AST into an abstract syntax tree (AST), Lombok dynamically modiizes the AST to add new nodes (called code) based on its annotation processor, and finally analyzes and generates bytecode.

Since Java 6, Javac has supported the “JSR 269 Pluggable Annotation Processing API” specification. As long as a program implements the API, it can be called when JavAC is running.

  1. The common project management tool Maven uses the Java compiler tool from the third-party tool configured. If we configure the third-party tool as Oracle Javac, then Maven directly supports Lombok.
  2. If the Intellij Idea configuration is compiled using Oracle Javac, lombok is directly supported.

IDE tool problem solving:

There is now A class A that has some fields that don’t create their setters and getters, using Lombok’s @data annotation, and A class B that calls the setters and getters of the corresponding fields on the instance of class A

Compiling the project in which classes A and B are located does not cause an error, because the resulting class A bytecode file contains the setter and getter methods for the corresponding fields

However, if the IDE finds that the setter and getter methods for the class A instance used in the class B source code are not defined in the class A source code, the IDE will consider this an error

To fix this error, which is not a true error, download and install the “Lombok Plugin “from Intellij Idea.

5 Customizing JSR269 Annotations

In the general javAC compilation process, the Java file is first parses to build an AST, then performs annotation processing, and finally generates binary.class files through analysis and optimization. What we can do is do some processing in the annotation processing phase. First we create the following file under meta-inf. Services:

Documents specified in the annotation processor: com. Alipay. Kris. Other. Lombok. MyAnnotaionProcessor, then we meet you can write your own annotations processor, a simple example code is as follows:

@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes("com.alipay.kris.other.lombok.*")
public class MyAnnotaionProcessor extends AbstractProcessor {
    public MyAnnotaionProcessor(a) {
        super(a); }@Override
    public boolean process(Set<? extends TypeElement> annotations,RoundEnvironment roundEnv) {
        for (Element elem : roundEnv.getElementsAnnotatedWith(MyAnnotation.class)) {
            MyAnnotation annotation = elem.getAnnotation(MyAnnotation.class);
            String message = "annotation found in " + elem.getSimpleName()
                + " with " + annotation.value();
            addToString(elem);
            processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, message);
        }
        return true; // no further processing of this annotation type}}Copy the code