Loading... 懒人必备插件!本质是个Java库,主要能够简化pojo层的一些无脑操作,如:对象的构造函数、 equals()方法,属性的 get()/set()方法等等。 <!--more--> ### 项目引入 现在SpringBoot创建时可以自动勾选Lombok了,手动的话需要两点: 1.在pom.xml中引入Lombok依赖 ```XML <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> ``` 2.在Settings——Plugins下载 `Lombok Plugin` --- ### @Data `@Data`整合了以下五个: * **`@Getter/@Setter`**:可以在需要的属性上打上注解,通过 `AccessLevel.MODULE/PROTECTED/PRIVATE/PACKAGE/PUBLIC(默认)`设置方法权限;也可以直接打在类上,通过 `AccessLevel.NONE`手动禁止生成get或set方法 ```Java public class Student { @Getter @Setter private int stuno; @Getter @Setter private String stuname; @Getter(AccessLevel.PRIVATE) private int cardid; } --- @Getter @Setter public class Student { private int stuno; private String stuname; @Setter(AccessLevel.NONE) private int cardid; } ``` * **`@ToString`**:默认生成所有字段。可以用 `exclude`属性禁止在 ToString方法中使用某字段,而用 `of`属性指定需要使用的字段。 ```Java @ToString(of = {"stuno","stuname"},exclude = {"cardid"}) //只打印stuno和stuname两个属性,Student(stuno=1, stuname=zs) public class Student { private int stuno; private String stuname; private int cardid; } ``` * **`@EqualsAndHashCode`**:自动生成 `equals(Object other)`和 `hashcode()`方法,包括所有非静态属性和非 transient 的属性,同样该注解也可以通过 exclude 属性排除某些字段,of 属性指定某些字段,也可以通过 callSuper 属性在重写的方法中使用父类的字段。一般直接使用就行了。 编译后的代码如下: ```Java 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 { label39: { Object this$stuno = this.getStuno(); Object other$stuno = other.getStuno(); if (this$stuno == null) { if (other$stuno == null) { break label39; } } else if (this$stuno.equals(other$stuno)) { break label39; } return false; } Object this$stuname = this.getStuname(); Object other$stuname = other.getStuname(); if (this$stuname == null) { if (other$stuname != null) { return false; } } else if (!this$stuname.equals(other$stuname)) { return false; } if (this.getCardid() != other.getCardid()) { return false; } else { return true; } } } } protected boolean canEqual(final Object other) { return other instanceof Student; } public int hashCode() { int PRIME = true; int result = 1; Object $stuno = this.getStuno(); int result = result * 59 + ($stuno == null ? 43 : $stuno.hashCode()); Object $stuname = this.getStuname(); result = result * 59 + ($stuname == null ? 43 : $stuname.hashCode()); result = result * 59 + this.getCardid(); return result; } ``` > **Q : 为什么只有一个整体的 @EqualsAndHashCode 注解,而不是分开的两个 @Equals 和 @HashCode?** > A : 在 Java 中有规定,当两个对象 equals 时,他们的 hashcode 一定要相同,反之,当 hashcode 相同时,对象不一定 equals。所以 equals 和 hashcode 要一起实现,免得发生违反 Java 规定的情形发生 * **`@RequireArgsConstructor`**:改注解生成有参数构造器时只会包含有 final 和 @NonNull 标识的 field。 **注意:**当有final字段存在时,会与下文无参构造 `@NoArgsConstructor`产生冲突。 ```Java @RequiredArgsConstructor public class Student { @NonNull private int stuno; private final String stuname; private int cardid; } ---相当于--- public class Student { @NonNull private Integer stuno; private final String stuname; private int cardid; public Student(@NonNull final Integer stuno, final String stuname) { if (stuno == null) { throw new NullPointerException("stuno is marked non-null but is null"); } else { this.stuno = stuno; this.stuname = stuname; } } } ``` **注意:** 如果使用 `@Data` 注解的类,继承成了其它父类的属性,最好额外添加 `@ToString(callSuper = true)` 和 `@EqualsAndHashCode(callSuper = true)` 注解。 因为默认情况下,`@Data` 注解不会处理父类的属性。所以需要我们通过 `callSuper = true` 属性,声明需要调用父类对应的方法。 这个情况非常常见,例如说在实体类中,我们可能会声明一个抽象父类 `AbstractEntity`,而该类里有一个 `id` 编号属性。 --- ### @NonNull 打在方法或构造器的参数上或属性上,如上文所展示的,主要是用来判空。 ```Java public class Student { @NonNull private Integer stuno; private final String stuname; private int cardid; } --- @RequestMapping("/deleteByStuno") public void deleteByStuno(@NonNull String stuno){ studentService.deleteByStuno(stuno); } ``` --- ### @NoArgsConstructor、@RequiredArgsConstructor、@AllArgsConstructor **`@NoArgsConstructor`**:无参构造 **`@RequiredArgsConstructor`**:指定参数构造,具体详见上文 `@Data` **`@AllArgsConstructor`**:全参构造 **从编译文件可看出:**指定参数构造和全参构造的方法会自动为每个属性添加 `final`属性。 --- ### @Builder 优雅的new一个对象,本质是用**设计模式**中的**建造者模式**创建对象。 链式调用确实优雅了很多起码指针不需要移动。 **注意!!!** **`@Builder`和 `@Data`连用时有可能会导致无参构造失效,最好和 `@NoArgsConstructor`、`@AllArgsConstructor`连用!** 报错如下: ```java 需要: 没有参数 找到: java.lang.String,java.lang.String,java.lang.String,java.lang.Integer 原因: 实际参数列表和形式参数列表长度不同 ``` **`@Builder`默认是全参构造,而一般主键都是设置自增不用写,如果要这样使用又得使用自定义参数构造专门构造一个方法,太麻烦了!因此建议还是只用@Data、构造函数还是用IDEA的快捷构造 `Alt+Insert+Enter`就好了** ```Java @ToString @Builder public class Student { private Integer stuno; private String stuname; private int cardid; } @RequestMapping("/stuToString") @ResponseBody public void stuToString(){ Student student = Student.builder().stuno(1).stuname("zs").cardid(233).build(); System.out.println(student.toString()); } Student(stuno=1, stuname=zs, cardid=233) ``` --- ### @Log 省略了定义静态常量Logger的写法,直接在类上加 `@Log`即可实现: `private static final Logger log = Logger.getLogger(XXX.class.getName())`; 当然也有很多中日志选择: `@CommonLog`、`@Log`、`@Log4j`、`@Log4j2`、`Slf4j`等等。 ```Java @Log @Controller public class StudentController { @Autowired private StudentService studentService; @RequestMapping("/stuToString") @ResponseBody public void stuToString(){ log.info("this is log..."); Student student = Student.builder().stuno(1).stuname("zs").cardid(233).build(); System.out.println(student.toString()); } } 2020-06-04 20:58:42.150 INFO 10884 --- [nio-8080-exec-1] c.c.b.sys.controller.StudentController : this is log... Student(stuno=1, stuname=zs, cardid=233) ``` --- ### @Cleanup 自动调用流处理的 `close()`方法。并且自动放到try/finally中。 ```Java public void testCleanup(){ @Cleanup BufferedReader br = null; try{ FileReader fileReader = new FileReader("唐宋.txt");//定义文件 br = new BufferedReader(fileReader);//读取文本 System.out.println(br.readLine());//读取一行内容 } catch (Exception e) { e.printStackTrace(); } } ---相当于--- public void testCleanup(){ BufferedReader br = null; try{ FileReader fileReader = new FileReader("唐宋.txt");//定义文件 br = new BufferedReader(fileReader);//读取文本 System.out.println(br.readLine());//读取一行内容 } catch (Exception e) { e.printStackTrace(); }finally { try{ br.close();// 无论如何文件句柄在使用结束后得手动关闭! }catch (IOException e){ e.printStackTrace(); } } } ``` --- ### @SneakyThrows 简化异常,自动补全try/catch。 **注意:**别人调用你编写的函数,并不能显式的获知需要处理哪些异常,这样容易留坑! ```Java @SneakyThrows public void testCleanup(){ @Cleanup BufferedReader br = null; FileReader fileReader = new FileReader("唐宋.txt");//定义文件 br = new BufferedReader(fileReader);//读取文本 System.out.println(br.readLine());//读取一行内容 } ``` --- ### @Synchronized 类似于 `synchronized` 关键字,只能注释在静态方法和实例方法上 如果部署单机服务需要用到互斥锁,那么这个注解也可以起到作用。如果部署集群或分布式应用,就不会起到互斥作用,需要引入分布式锁的概念。 另外,`@Synchronized` 锁定的作用域与 `synchronized` 加在**方法**上的作用域一致,**但是不推荐直接加在方法上,应在使用到互斥的方法体中进行锁定**。 --- ### @Getter(lazy=true) 生成第一次调用此getter时计算的值,然后将其缓存。如果计算该值占用大量 CPU 或改值占用大量内存,那么推荐使用此注解。 如果使用此注解,那么请创建一个 `private final` 变量,标记 `@Getter(lazy=true)` 注解,在这里不必考虑线程安全问题,lombok 负责锁定该资源. ```Java 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 class GetterLazyExample { private final java.util.concurrent.atomic.AtomicReference<java.lang.Object> cached = new java.util.concurrent.atomic.AtomicReference<java.lang.Object>(); 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); } return result; } } ``` Last modification:August 12, 2022 © Allow specification reprint Like 0 喵ฅฅ