最近 IDEA 2020 最后一个版本发布了,听说
已经内置了 Lombok 插件,SpringBoot 2.1.x 之后的版本也在 Starter 中内置了 Lombok 依赖
Lombok简介 Lombok 是一款 Java
代码功能增强库,在 Github 上已有 9.9 k Star。它会自动集成到你的编辑器和构建工具中,从而使你的 Java 代码更加生动有趣。通过 Lombok 的注解,你可以不用再写 getter、setter、equals 等方法,Lombok 将在编译时为你自动生成。
Lombok安装 首先我们需要在 IDEA 中安装好 Lombok 插件,如果你使用的是最新版 IDEA 2020.3
,则 Lombok 插件已经内置,无需安装。
之后在项目的 pom.xml
文件中添加 Lombok 依赖就好啦
SpringBoot 2.1.x 版本后无需指定 Lombok 版本,因为 SpringBoot 在 spring-boot-dependencies
中已经内置了 Lombok
Lombok使用 Lombok 中有很多注解,这些注解使得我们可以更加方便的编写 Java 代码,下面介绍下这些注解的使用。
val 使用 val
注解可以取代任意类型作为局部变量,这样我们就不用写复杂的 ArrayList
和 Map.Entry
类型了,具体例子如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public class LombokDemo { public static void main (String[] args) { result(); result2(); } private static void result () { val list = new ArrayList <String>(); list.add("GARVEY ZHONG" ); val res = list.get(0 ); System.out.println(res.toLowerCase()); } private static void result2 () { val map = new HashMap <Integer, String>(); map.put(0 , "blog.imzjw.cn" ); System.out.println(map.get(0 )); } } =========================================================== "C:\Program Files\Java\jdk1.8.0_162\bin\java.exe" ... garvey zhong blog.imzjw.cn Process finished with exit code 0 ===========================================================
当我们使用了 val
注解后,Lombok 会从局部变量的初始化表达式推断出具体类型,编译后会生成如下代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class LombokDemo { public LombokDemo () { } public static void result () { ArrayList<String> list = new ArrayList <>(); list.add("GARVEY ZHONG" ); String res = list.get(0 ); System.out.println(res.toLowerCase()); } public static void result2 () { HashMap<Integer, String> map = new HashMap <>(); map.put(0 , "blog.imzjw.cn" ); System.out.println(map.get(0 )); } }
@NonNull 在方法上使用 @NonNull
注解可以做非空判断,如果传入空值的话会直接抛出 NullPointerException
(俗称空指针)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class LombokDemo { private String name; public static void main (String[] args) { new LombokDemo ("garvey" ); new LombokDemo (null ); } public LombokDemo (@NonNull String name) { this .name = name; } }
编译后会在构造器中添加非空判断,具体代码如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class LombokDemo { private String name; public static void main (String[] args) { new LombokDemo ("garvey" ); new LombokDemo ((String)null ); } public LombokDemo (@NonNull String name) { if (name == null ) { throw new NullPointerException ("name is marked non-null but is null" ); } else { this .name = name; } } }
@Cleanup 当我们在 Java 中使用资源时,不可避免地需要在使用后关闭资源。使用 @Cleanup
注解可自动关闭资源。
1 2 3 4 5 6 7 8 9 10 11 12 13 public static void main (String[] args) throws IOException { String str = "garvey" ; @Cleanup ByteArrayInputStream in = new ByteArrayInputStream (str.getBytes("UTF-8" )); @Cleanup ByteArrayOutputStream out = new ByteArrayOutputStream (); byte [] b = new byte [1024 ]; while (true ) { int r = in.read(b); if (r == -1 ) break ; out.write(b, 0 , r); } System.out.println(out.toString("UTF-8" )); }
编译后 Lombok 会生成如下代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public static void main (String[] args) throws IOException { String str = "garvey" ; ByteArrayInputStream in = new ByteArrayInputStream (inStr.getBytes("UTF-8" )); try { ByteArrayOutputStream out = new ByteArrayOutputStream (); try { byte [] b = new byte [1024 ]; while (true ) { int r = in.read(b); if (r == -1 ) { System.out.println(out.toString("UTF-8" );); return ; } out.write(b, 0 , r); } } finally { if (Collections.singletonList(out).get(0 ) != null ) { out.close(); } } } finally { if (Collections.singletonList(in).get(0 ) != null ) { in.close(); } } }
@Getter/@Setter 有了 @Getter 和 @Setter 注解,我们再也不用编写 getter and setter 方法了。
其实写 getter 和 setter 方法也不是麻烦事,因为 IDEA 自动生成 getter and setter 方法
麻烦的是如果类属性的类型和名称改了的话,又要重新生成 getter and setter 方法,这就很头疼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class Student { @Getter @Setter private String name; @Getter @Setter(AccessLevel.PROTECTED) private Integer age; public static void main (String[] args) { Student student = new Student (); student.setName("garvey" ); student.setAge(20 ); System.out.println("name=" + student.getName() + "\nage=" + student.getAge()); } }
编译后 Lombok 会生成如下代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class Student { private String name; private Integer age; public Student () { } public String getName () { return this .name; } public void setName (final String name) { this .name = name; } public Integer getAge () { return this .age; } protected void setAge (final Integer age) { this .age = age; } }
@ToString 使用 @ToString
注解可以自动生成 toString 方法,默认会包含所有类属性,使用 @ToString.Exclude
注解可以排除属性的生成。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @ToString public class Student { @ToString .Exclude private Long id; private String name; private Integer age; public Student (Long id, String name, Integer age) { this .id = id; this .name = name; this .age = age; } public static void main (String[] args) { Student student = new Student (1L , "garvey" , 20 ); System.out.println(student); } }
编译后 Lombok 会生成如下代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class Student { private Long id; private String name; private Integer age; public Student (Long id, String name, Integer age) { this .id = id; this .name = name; this .age = age; } public String toString () { return "Student(name=" + this .name + ", age=" + this .age + ")" ; } }
@EqualsAndHashCode 使用 @EqualsAndHashCode
注解可以自动生成 hashCode
和 equals
方法,默认包含所有类属性,使用 @EqualsAndHashCode.Exclude
可以排除属性的生成。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Getter @Setter @EqualsAndHashCode public class Student { private Long id; @EqualsAndHashCode .Exclude private String name; @EqualsAndHashCode .Exclude private Integer age; public static void main (String[] args) { Student student = new Student (); student.setId(1L ); student.setName("garvey" ); student.setAge(20 ); Student student2 = new Student (); student2.setId(1L ); System.out.println(student.equals(student2)); } }
编译后 Lombok 会生成如下代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 public class Student { private Long id; private String name; private Integer age; public Student () { } public boolean equals (final Object o) { if (o == this ) { return true ; } else if (!(o instanceof Student)) { return false ; } else { Student other = (Student)o; if (!other.canEqual(this )) { return false ; } else { Object this$id = this .getId(); Object other$id = other.getId(); if (this $id == null ) { if (other$id != null ) { return false ; } } else if (!this $id.equals(other$id)) { return false ; } return true ; } } } protected boolean canEqual (final Object other) { return other instanceof Student; } public int hashCode () { int PRIME = true ; int result = 1 ; Object $id = this .getId(); int result = result * 59 + ($id == null ? 43 : $id.hashCode()); return result; } }
@XxConstructor 使用 @XxConstructor
注解可以自动生成构造方法,有 @NoArgsConstructor
、 @RequiredArgsConstructor
和 @AllArgsConstructor
三个注解可以使用。
@NoArgsConstructor:生成无参构造函数。 @RequiredArgsConstructor:生成包含必须参数的构造函数,使用 @NonNull 注解的类属性为必须参数。 @AllArgsConstructor:生成包含所有参数的构造函数。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @NoArgsConstructor @RequiredArgsConstructor(staticName = "of") @AllArgsConstructor public class Student { @NonNull private Long id; private String name; private Integer age; public static void main (String[] args) { Student student1 = new Student (); Student student2 = new Student (1L , "garvey" , 20 ); Student student3 = Student.of(1L ); } }
编译后 Lombok 会生成如下代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class Student { @NonNull private Long id; private String name; private Integer age; public Student () { } private Student (@NonNull final Long id) { if (id == null ) { throw new NullPointerException ("id is marked non-null but is null" ); } else { this .id = id; } } public static Student of (@NonNull final Long id) { return new Student (id); } public Student (@NonNull final Long id, final String name, final Integer age) { if (id == null ) { throw new NullPointerException ("id is marked non-null but is null" ); } else { this .id = id; this .name = name; this .age = age; } } }
@Data @Data
是一个方便使用的组合注解,是 @ToString
、@EqualsAndHashCode
、@Getter
、@Setter
和 @RequiredArgsConstructor
的组合体。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Data public class Student { @NonNull private Long id; @EqualsAndHashCode .Exclude private String name; @EqualsAndHashCode .Exclude private Integer age; public static void main (String[] args) { Student student1 = new Student (1L ); student1.setName("garvey" ); student1.setAge(20 ); System.out.println(student1); Student student2 = new Student (1L ); System.out.println(student1.equals(student2)); } }
编译后 Lombok 会生成如下代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 public class Student { @NonNull private Long id; private String name; private Integer age; public Student (@NonNull final Long id) { if (id == null ) { throw new NullPointerException ("id is marked non-null but is null" ); } else { this .id = id; } } @NonNull public Long getId () { return this .id; } public String getName () { return this .name; } public Integer getAge () { return this .age; } public void setId (@NonNull final Long id) { if (id == null ) { throw new NullPointerException ("id is marked non-null but is null" ); } else { this .id = id; } } public void setName (final String name) { this .name = name; } public void setAge (final Integer age) { this .age = age; } public boolean equals (final Object o) { if (o == this ) { return true ; } else if (!(o instanceof Student)) { return false ; } else { Student other = (Student)o; if (!other.canEqual(this )) { return false ; } else { Object this$id = this .getId(); Object other$id = other.getId(); if (this $id == null ) { if (other$id != null ) { return false ; } } else if (!this $id.equals(other$id)) { return false ; } return true ; } } } protected boolean canEqual (final Object other) { return other instanceof Student; } public int hashCode () { int PRIME = true ; int result = 1 ; Object $id = this .getId(); int result = result * 59 + ($id == null ? 43 : $id.hashCode()); return result; } public String toString () { return "Student(id=" + this .getId() + ", name=" + this .getName() + ", age=" + this .getAge() + ")" ; } }
@Value 使用 @Value 注解可以把类声明为不可变的,声明后此类相当于 final
类,无法被继承,其属性也会变成 final
属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 @Value public class Student { private Long id; private String name; private Integer age; public static void main (String[] args) { Student student = new Student (1L ,"garvey" ,20 ); } }
编译后 Lombok 会生成如下代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public final class Student { private final Long id; private final String name; private final Integer age; public static void main (String[] args) { new Student (1L , "garvey" , 20 ); } public Student (final Long id, final String name, final Integer age) { this .id = id; this .name = name; this .age = age; } public Long getId () { return this .id; } public String getName () { return this .name; } public Integer getAge () { return this .age; } }
@Builder 使用 @Builder
注解可以通过建造者模式来创建对象,建造者模式加链式调用,创建对象太方便了!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Builder @ToString public class Student { private Long id; private String name; private Integer age; public static void main (String[] args) { Student student = Student.builder() .id(1L ) .name("garvey" ) .age(20 ) .build(); System.out.println(student); } }
编译后 Lombok 会生成如下代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 public class Student { private Long id; private String name; private Integer age; Student(final Long id, final String name, final Integer age) { this .id = id; this .name = name; this .age = age; } public static Student.BuilderExampleBuilder builder () { return new Student .BuilderExampleBuilder(); } public String toString () { return "Student(id=" + this .id + ", name=" + this .name + ", age=" + this .age + ")" ; } public static class BuilderExampleBuilder { private Long id; private String name; private Integer age; BuilderExampleBuilder() { } public Student.BuilderExampleBuilder id (final Long id) { this .id = id; return this ; } public Student.BuilderExampleBuilder name (final String name) { this .name = name; return this ; } public Student.BuilderExampleBuilder age (final Integer age) { this .age = age; return this ; } public Student build () { return new Student (this .id, this .name, this .age); } public String toString () { return "Student.BuilderExampleBuilder(id=" + this .id + ", name=" + this .name + ", age=" + this .age + ")" ; } } }
@SneakyThrows 还在手动捕获并抛出异常?使用 @SneakyThrows
注解自动实现试试!
1 2 3 4 5 6 7 8 9 10 11 12 13 public class Student { @SneakyThrows(UnsupportedEncodingException.class) public static byte [] str2byte(String str) { return str.getBytes("UTF-8" ); } public static void main (String[] args) { String str = "garvey" ; System.out.println(str2byte(str).length); } }
编译后 Lombok 会生成如下代码。
1 2 3 4 5 6 7 8 9 10 11 12 public class Student { public Student () { } public static byte [] str2byte(String str) { try { return str.getBytes("UTF-8" ); } catch (UnsupportedEncodingException var2) { throw var2; } } }
@Synchronized 当我们在多个线程中访问同一资源时,往往会出现线程安全问题,以前我们往往使用 synchronized 关键字修饰方法来实现同步访问。使用 @Synchronized 注解同样可以实现同步访问。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 @Data public class SynchronizedExample { @NonNull private Integer count; @Synchronized @SneakyThrows public void reduceCount (Integer id) { if (count > 0 ) { Thread.sleep(500 ); count--; System.out.println(String.format("thread-%d count:%d" , id, count)); } } public static void main (String[] args) { SynchronizedExample example = new SynchronizedExample (20 ); new ReduceThread (1 , example).start(); new ReduceThread (2 , example).start(); new ReduceThread (3 , example).start(); } @RequiredArgsConstructor static class ReduceThread extends Thread { @NonNull private Integer id; @NonNull private SynchronizedExample example; @Override public void run () { while (example.getCount() > 0 ) { example.reduceCount(id); } } } }
编译后 Lombok 会生成如下代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class SynchronizedExample { private final Object $lock = new Object [0 ]; @NonNull private Integer count; public void reduceCount (Integer id) { try { synchronized (this .$lock) { if (this .count > 0 ) { Thread.sleep(500L ); Integer var3 = this .count; Integer var4 = this .count = this .count - 1 ; System.out.println(String.format("thread-%d count:%d" , id, this .count)); } } } catch (Throwable var7) { throw var7; } } }
@With 使用 @With 注解可以实现对原对象进行克隆,并改变其一个属性,使用时需要指定全参构造方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @With @AllArgsConstructor public class WithExample { private Long id; private String name; private Integer age; public static void main (String[] args) { WithExample example1 = new WithExample (1L , "garvey" , 20 ); WithExample example2 = example1.withAge(22 ); System.out.println(example1.equals(example2)); } }
编译后 Lombok 会生成如下代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class WithExample { private Long id; private String name; private Integer age; public WithExample withId (final Long id) { return this .id == id ? this : new WithExample (id, this .name, this .age); } public WithExample withName (final String name) { return this .name == name ? this : new WithExample (this .id, name, this .age); } public WithExample withAge (final Integer age) { return this .age == age ? this : new WithExample (this .id, this .name, age); } public WithExample (final Long id, final String name, final Integer age) { this .id = id; this .name = name; this .age = age; } }
@Getter(lazy=true) 当我们获取某一个属性比较消耗资源时,可以给 @Getter 添加 lazy=true
属性实现懒加载,会生成 Double Check Lock
样板代码对属性进行懒加载。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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); } return result; } public static void main (String[] args) { GetterLazyExample example = new GetterLazyExample (); System.out.println(example.getCached().length); } }
编译后 Lombok 会生成如下代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class GetterLazyExample { private final AtomicReference<Object> cached = new AtomicReference (); public GetterLazyExample () { } private double [] expensive() { double [] result = new double [1000000 ]; for (int i = 0 ; i < result.length; ++i) { result[i] = Math.asin((double )i); } return result; } public double [] getCached() { Object value = this .cached.get(); if (value == null ) { synchronized (this .cached) { value = this .cached.get(); if (value == null ) { double [] actualValue = this .expensive(); value = actualValue == null ? this .cached : actualValue; this .cached.set(value); } } } return (double [])((double [])(value == this .cached ? null : value)); } }
@Log 使用 @Log 注解,可以直接生成日志对象 log,通过 log 对象可以直接打印日志。
1 2 3 4 5 6 7 8 @Log public class LogExample { public static void main (String[] args) { log.info("level info" ); log.warning("level warning" ); log.severe("level severe" ); } }
编译后 Lombok 会生成如下代码。
1 2 3 4 5 6 7 8 9 10 11 12 public class LogExample { private static final Logger log = Logger.getLogger(LogExample.class.getName()); public LogExample () { } public static void main (String[] args) { log.info("level info" ); log.warning("level warning" ); log.severe("level severe" ); } }
@Slf4j 使用 Lombok 生成日志对象时,根据使用日志实现的不同,有多种注解可以使用。比如 @Log、@Log4j、@Log4j2、@Slf4j 等。
1 2 3 4 5 6 7 8 @Slf4j public class LogSlf4jExample { public static void main (String[] args) { log.info("level:{}" ,"info" ); log.warn("level:{}" ,"warn" ); log.error("level:{}" , "error" ); } }
编译后 Lombok 会生成如下代码。
1 2 3 4 5 6 7 8 9 10 11 12 public class LogSlf4jExample { private static final Logger log = LoggerFactory.getLogger(LogSlf4jExample.class); public LogSlf4jExample () { } public static void main (String[] args) { log.info("level:{}" , "info" ); log.warn("level:{}" , "warn" ); log.error("level:{}" , "error" ); } }
参考资料