Java library Lombok commonly used annotations

Lombok is already one of the most commonly used libraries in many Java projects, and I have been using it, but it is limited to annotations such as @Data, @XxxConstructer, @Slf4j, and I have not seen other annotations.
It wasn’t until I saw someone else’s code some time ago, used an @SneakyThrowsannotation, searched, and found that it was also a Lombok annotation, and it solved one of my pain points, so I decided to spend some time studying other Lombok annotations.
For all the annotations of Lombok, you can go to the official website to see: https://projectlombok.org/features/

Principle brief

1. Lombok needs to install a plug-in in the IDE (for example, the latest version of IDEA has integrated the Lombok plug-in by default after installation)
insert image description here

2. Add lombok dependencies in the pom.xml of the project, and add lombok annotations to the corresponding code
3. Depend on JSR 269: Pluggable Annotation Processing API, Lombok has customized the annotation processor, and will add new ones to the annotated code when the project is compiled code, and finally generate bytecode, process reference: https://developer.aliyun.com/article/995108

Common Notes

Let me introduce the annotations that I often use. I won’t introduce some of the annotations that I think are not very useful. You can refer to the detailed list on the official website.

1. @val and @var

These two are type inference annotations for local variables, reducing the code for writing local variable types:

  • val is a variable that defines a final type:
    val a = 123;equivalent tofinal int a = 123;
  • var is to define ordinary variables:
    var a = 123;equivalent to int a = 123;
    Note: Since Java10, the var keyword and inference ability have been introduced, and there is no need to use lombok's var anymore

Another: Many people object to the type inference of var, thinking that the readability is poor. This is a matter of opinion. I prefer it, especially when the type name is very long, and the easy-to-use IDE (such as IDEA) will automatically display The corresponding variable type, such as:
insert image description here

2、@NonNull

For parameters of methods or constructors, reduce writing code to judge whether parameters are null, such as:

public static String nonNullDemo2(@NonNull Integer arg) {
    
    
    return arg.toString();
}

The above code, the final generated actual code is as follows, and the code for judging empty and throwing exceptions will be automatically added:

public static String nonNullDemo2(@NonNull Integer arg) {
    
    
    if (arg == null) {
    
    
        throw new NullPointerException("arg is marked non-null but is null");
    }
    return arg.toString();
}

Note: It can also be used on the member properties of the class, but it must be used in conjunction with AllArgsConstructor or RequiredArgsConstructor, which is not recommended

3、@Data

This is a combined annotation, which is usually placed on entity class objects such as Dto and Entity.
This annotation combines:

  • @ToString
  • @EqualsAndHashCode
  • @Getter
  • Add @Setter to fields that are not defined as final
  • @RequiredArgsConstructor

For the functions of the above annotations, please refer to the description of the corresponding annotations below

4、@Getter/@Setter

Automatically generate the get/set method of the specified field, which can be added to the class to affect all fields of the class, or can be added to the specified field, such as:

public class GetterSetterDemo {
    
    
    @Getter
    public static class Dto1 {
    
    
        private int id;

        private String name;
    }

    @Setter
    public static class Dto2 {
    
    
        private int id;

        private String name;
    }

    public static class Dto3 {
    
    
        @Getter
        private int id;
        @Setter
        private String name;
    }
}

The actual code generated by the above code is as follows:

public class GetterSetterDemo {
    
    
    public static class Dto1 {
    
    
        private int id;
        private String name;

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

    public static class Dto2 {
    
    
        private int id;
        private String name;

        public void setId(final int id) {
    
    
            this.id = id;
        }
        public void setName(final String name) {
    
    
            this.name = name;
        }
    }

    public static class Dto3 {
    
    
        private int id;
        private String name;

        public int getId() {
    
    
            return this.id;
        }
        public void setName(final String name) {
    
    
            this.name = name;
        }
    }
}

5、@Accessors(chain=true)

This annotation should be used in conjunction with @Setter. Chain=true is added to the annotation, indicating that the return value of each set method is the this object itself, instead of the default void, which is convenient for chain assignment, such as:

@Setter
@Accessors(chain = true)
public class AccessorsDemo {
    
    
    private int id;
    private String name;
}

With such a class defined, we can chain assignment:

public AccessorsDemo TestAccessors() {
    
    
    return new AccessorsDemo()
            .setId(123)
            .setName("abc");
}

The code of the above class class, the final generated code is as follows:

public class AccessorsDemo {
    
    
    private int id;
    private String name;

    public AccessorsDemo setId(final int id) {
    
    
        this.id = id;
        return this;
    }
    public AccessorsDemo setName(final String name) {
    
    
        this.name = name;
        return this;
    }
}

Note 1: I didn’t know there was this annotation before, and I always liked to use @Builder. I will no longer use @Builder in the future. I will use this annotation instead
. Note 2: The @Accessors annotation also has 2 attributes. Introduced, see the official website

6、@SneakyThrows

The purpose of this annotation is to suppress compile-time exceptions thrown in the method.
When we write code, if the statement throws an exception, we must either add try ourselves, or add an exception throwing statement after the method signature, which I find disgusting (C# does not need it).
In the past, I had to set one by hand try{}catch(Exception ex){throw new RuntimeException(ex);}. With this annotation, I no longer need to do so.
like:

@SneakyThrows
public int test1() {
    
    
    throw new Exception("abc");
}

The final generated code is as follows:

@SneakyThrows
public int test1() {
    
    
    try{
    
    
        throw new Exception("abc");
    } catch (UnsupportedEncodingException e) {
    
    
      throw Lombok.sneakyThrow(e);
    }
}

7、@Synchronized

Lock the entire method and execute the method serially.
The annotation defines a private class variable by default, and uses this variable to lock the code in the method.

  • Static method, defining static class variables as locking objects
  • Class member methods, define class member variables as locking objects
  • By default, all static methods share the same lock object, and all member methods share the same lock object
  • The value parameter of the annotation can define the variable name of the lock object to use different lock objects
  • Note: Class member methods use class member variables as lock objects, so the two initialized classes cannot use the same lock

Reference Code:

public class SynchronizedDemo {
    
    
    private static int num;

    @Synchronized
    @SneakyThrows
    public void syncMethod() {
    
    
        num++;
        System.out.println("before-num:" + num);
        Thread.sleep(3000);
        num++;
        System.out.println("after-num:" + num);
    }

    @Synchronized
    @SneakyThrows
    public static void syncStaticMethod() {
    
    
        num++;
        System.out.println("before-num:" + num);
        Thread.sleep(3000);
        num++;
        System.out.println("after-num:" + num);
    }

    @Synchronized
    @SneakyThrows
    public static void syncStaticMethod2() {
    
    
        num++;
        System.out.println("2-before-num:" + num);
        Thread.sleep(3000);
        num++;
        System.out.println("2-after-num:" + num);
    }
}

The final generated code is as follows:

public class SynchronizedDemo {
    
    
    private final Object $lock = new Object[0];
    private static int num;
    private static final Object $LOCK = new Object[0];

    public void syncMethod() {
    
    
        synchronized (this.$lock) {
    
    
            num++;
            System.out.println("before-num:" + Integer.valueOf(num));
            Thread.sleep(3000L);
            num++;
            System.out.println("after-num:" + Integer.valueOf(num));
        }
    }

    public static void syncStaticMethod() {
    
    
        synchronized ($LOCK) {
    
    
            num++;
            System.out.println("before-num:" + Integer.valueOf(num));
            Thread.sleep(3000L);
            num++;
            System.out.println("after-num:" + Integer.valueOf(num));
        }
    }

    public static void syncStaticMethod2() {
    
    
        synchronized ($LOCK) {
    
    
            num++;
            System.out.println("2-before-num:" + Integer.valueOf(num));
            Thread.sleep(3000L);
            num++;
            System.out.println("2-after-num:" + Integer.valueOf(num));
        }
    }
}

According to the generated code, this annotation is also valid for internal method intermodulation;
unlike Bean's annotations, internal intermodulation is invalid and must be called through a proxy.

8、@Cleanup

It is used for local variables that need to recycle resources, reducing the writing of finally code and code that recycles resources by itself.
This annotation requires variables to have a method named close. If not, you can customize it, such as the following 2 sample codes:

public class CleanupDemo {
    
    
    @SneakyThrows
    public static void test(HttpServletResponse response) {
    
    
        @Cleanup InputStream inputStream = new ByteArrayInputStream("I'm a string".getBytes());
        @Cleanup OutputStream outputStream = response.getOutputStream();
        inputStream.transferTo(outputStream);
    }
    @SneakyThrows
    public static void test2(HttpServletResponse response) {
    
    
        @Cleanup OutputStream outputStream = response.getOutputStream();
        @Cleanup("doClear") var tmp = new CleanClass();// 默认要求注解的变量有close方法,可以修改
        outputStream.write(tmp.now.getBytes());
    }

    // 定义一个带资源清理的类
    public static class CleanClass {
    
    
        public String now = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"));
        public void doClear() {
    
    
            now = null;
        }
    }
}

The above code, the final generated actual code is as follows, and the finally code will be automatically added to recycle resources:

public class CleanupDemo {
    
    
    public static void test(HttpServletResponse response) {
    
    
        InputStream inputStream = new ByteArrayInputStream("I'm a string".getBytes());
        ServletOutputStream outputStream = response.getOutputStream();
        try {
    
    
            inputStream.transferTo(outputStream);
            if (Collections.singletonList(outputStream).get(0) != null) {
    
    
                outputStream.close();
            }
            if (Collections.singletonList(inputStream).get(0) != null) {
    
    
                inputStream.close();
            }
        } catch (Throwable th) {
    
    
            if (Collections.singletonList(outputStream).get(0) != null) {
    
    
                outputStream.close();
            }
            throw th;
        }
    }
    public static void test2(HttpServletResponse response) {
    
    
        ServletOutputStream outputStream = response.getOutputStream();
        CleanClass tmp = new CleanClass();
        try {
    
    
            outputStream.write(tmp.now.getBytes());
            if (Collections.singletonList(tmp).get(0) != null) {
    
    
                tmp.doClear();
            }
            if (Collections.singletonList(outputStream).get(0) != null) {
    
    
                outputStream.close();
            }
        } catch (Throwable th) {
    
    
            if (Collections.singletonList(tmp).get(0) != null) {
    
    
                tmp.doClear();
            }
            throw th;
        }
    }
    public static class CleanClass {
    
    
        public String now = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"));
        public void doClear() {
    
    
            this.now = null;
        }
    }
}

9、@ToString

Automatically override the Object.toString() method to generate a toString() method that concatenates all fields, such as:

@ToString
public class ToStringDemo {
    
    
    private int id;
    private final String name = "";
    private final Environment env = null;
}

The final generated code:

public class ToStringDemo {
    
    
    private int id;
    private final String name = "";
    private final Environment env = null;

    public String toString() {
    
    
        int i = this.id;
        Objects.requireNonNull(this);
        return "ToStringDemo(id=" + i + ", name=" + "" + ", env=" + this.env + ")";
    }
}

10、@EqualsAndHashCode

Automatically rewrite Object.hashCode() and Object.equals() methods
such as:

@EqualsAndHashCode
public class EqualsAndHashCodeDemo {
    
    
    private int id;
    private String name;
}

The final generated code:

public class EqualsAndHashCodeDemo {
    
    
    private int id;
    private String name;

    public boolean equals(final Object o) {
    
    
        if (o == this) {
    
    
            return true;
        }
        if (o instanceof EqualsAndHashCodeDemo) {
    
    
            EqualsAndHashCodeDemo other = (EqualsAndHashCodeDemo) o;
            if (other.canEqual(this) && this.id == other.id) {
    
    
                Object this$name = this.name;
                Object other$name = other.name;
                return this$name == null ? other$name == null : this$name.equals(other$name);
            }
            return false;
        }
        return false;
    }

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

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

Note: There is a pitfall in this annotation. The two methods hashCode and equals only compare all the fields of the current class.
If this is a subclass, these two methods only compare the fields defined by themselves, and will not compare the fields of the parent class.
So if your class is a subclass, you need to change the annotation to: @EqualsAndHashCode(callSuper = true)Avoid comparison problems, or data loss when objects are added to Map as keys.

11、@NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor

  • @NoArgsConstructor: Automatically generate no-argument constructor
  • @AllArgsConstructor: Use all fields of the class as parameters to automatically generate constructors
  • @RequiredArgsConstructor: For all fields defined as final, automatically generate a constructor with parameters.
    This is the best use, especially for those @Servicespring @ComponentBean classes. With this annotation, there is no need to use @Autowiredannotations, such as:
@RequiredArgsConstructor
public class RequiredArgsConstructorDemo {
    
    
    private int id;
    private final String name;
    private final Environment env;
}

The generated code is as follows, there is no id parameter in the constructor:

public class RequiredArgsConstructorDemo {
    
    
    private int id;
    private final String name;
    private final Environment env;

    public RequiredArgsConstructorDemo(final String name, final Environment env) {
    
    
        this.name = name;
        this.env = env;
    }
}

12、@Slf4j

Automatically generate a log member variable for the class. In fact, there are many annotations related to logging, and the reference points to different logging frameworks.
The one I use the most is @Slf4j, such as:

@Slf4j
public class Slf4jDemo {
    
    
    public void doLog() {
    
    
        log.debug("DEBUG日志:当前时间:{}", LocalDateTime.now());
        log.info("INFO日志:当前时间:{}", LocalDateTime.now());
        log.warn("warn日志:当前时间:{}", LocalDateTime.now(), new Exception("我是异常"));
        log.error("error日志:当前时间:{}", LocalDateTime.now(), new Exception("我是异常"));
    }
}

The final generated code:

public class Slf4jDemo {
    
    
    private static final Logger log = LoggerFactory.getLogger(Slf4jDemo.class);

    public void doLog() {
    
    
        log.debug("DEBUG日志:当前时间:{}", LocalDateTime.now());
        log.info("INFO日志:当前时间:{}", LocalDateTime.now());
        log.warn("warn日志:当前时间:{}", LocalDateTime.now(), new Exception("我是异常"));
        log.error("error日志:当前时间:{}", LocalDateTime.now(), new Exception("我是异常"));
    }
}

13、@Builder

The builder() method is automatically generated, the chain assignment is performed, and the object is finally generated. Seeing that the final code also creates an internal class, it is more complicated.
It is recommended to use @Accessors(chain=true) above instead

actual code

All the codes have been uploaded to Github, you can refer to: https://github.com/youbl/study/tree/master/study-codes/lombok-demo

debate issue

Indeed, there are many online, such as:

  • Lombok is a black box, which leads to poor code readability. Instead, I feel that the code volume is less and the code readability is higher.
    It's not a problem if you understand how it works.
    To give a simple example, you call a method encapsulated by jdk. If you don’t study the source code of the method, and don’t have a deep understanding of what this method does and what pitfalls it may have, then it is similar to Lombok, and you will experience various errors during use. kind of problem.
  • Lombok is too polluting, and if one person in the team uses it, it will cause others to have to use it.
    This is a problem, but now the use of Lombok has become more and more widespread. The companies and teams I have contacted have not found any unused ones. Even IDEA has been integrated by default. I have not seen two libraries similar to Lombok before. There are teams using it.
    Of course, it may be that I have seen less. If you really hate Lombok, you can use the Delombok plugin to solve this problem.

Guess you like

Origin blog.csdn.net/youbl/article/details/131122408