目录

Life in Flow

知不知,尚矣;不知知,病矣。
不知不知,殆矣。

X

Lombok

Lombok 的优点

1Lombok:可以让你的POJO代码特别简洁,不止简单在 BO/VO/DTO/DO等大量使用,还有设计模式,对象比对等
1domain/model/dto/do/vo/vo等等
2大家写最看最多的的就是set和get方法,定义一个user对象,生成set/get方法
3
4缺点:大量冗余简单的代码出现在java对象里面

Lombok 安装使用

  • 添加 IDE 工具对 Lombok 的支持
    1点击File-- Settings设置界面,安装Lombok插件,然后重启idea
    
  • 项目添加依赖进行版本管理
1<dependency>
2<groupId>org.projectlombok</groupId>
3<artifactId>lombok</artifactId>
4<version>1.18.16</version>
5<scope>provided</scope>
6</dependency>
7
8<!--https://mvnrepository.com/artifact/org.projectlombok/lombok/1.18.16-->
9<!--scope=provided,说明它只在编译阶段生效,不需要打入包中, Lombok在编译期将带Lombok注解的Java文件正确编译为完整的Class文件-->

@Setter/@Getter

  • 基于项目的实体类测试
    • User 实体类
  • 常见注解 @Getter/@Setter
    • 作用类上,生成所有成员变量的 getter/setter 方法
    • 作用于成员变量上,生成该成员变量的 getter/setter 方法
  • 编译查看字节码 mvn compile
  • 进一步控制
    • 方法控制访问级别 set 和 get 注解加上 @Getter(AccessLevel.PROTECTED)
    • 不行生成 set、get 方法
  • 代码
 1@Setter
 2@Getter
 3public class UserDO {
 4
 5    /**
 6     * 不想生成 get方法
 7     */
 8    @Getter(AccessLevel.NONE)
 9    private int age;
10  
11    /**
12     * 控制访问权限
13     */
14    @Getter(AccessLevel.PROTECTED)
15    private int salary;
16  
17    /**
18     * final 只会生成get
19     */
20    private final String name="二当家小D";
21  
22    /**
23     * 下面两个静态成员变量不会生成set/get方法
24     */
25    static Date createTime = new Date();
26    private static final String address = "广东省广州市";
27}
28

@NonNull

  • 作用于方法上或者属性,用于非空判断,如果为空则抛异常
1   public void test(@NonNull  String email){
2          this.email = email;
3   }
  • 字节码
1    public void test(@NonNull String email) {
2          if (email == null) {
3              throw new NullPointerException("email is marked non-null but is null");
4          } else {
5              this.email = email;
6          }
7      }

@NoArgsConstructor

  • 生成无参构造器

@AllArgsConstructor

  • 生成全参构造器(非 final 类型的成员变量)

@RequiredArgsConstructor

  • 指定参数的构造函数,有以下的特征的字段

    • final 类型未被初始化的属性, 标记了 @NonNull 的属性
    • 注意:@NoArgsConstructor 不能加!!!

@ToString

  • 不包括某个字段
1@ToString(exclude = {"age"})
  • 只输出某个字段
1@ToString(of = {"name"})

@EqualsAndHashCode

  • @EqualsAndHashCode
  • 作用于类,覆盖默认的 equals 和 hashCode, 作用于全部属性
  • 不包括某个属性
1@EqualsAndHashCode(exclude = {"age"})

只输出某个属性

1@EqualsAndHashCode(of = {"name"})

@Data

  • @Data, 定义一个干净的类,增加此注解,mvn compile 查看字节码
  • 作用于类上,是以下注解的集合
    • @ToString
    • @EqualsAndHashCode
    • @Getter
    • @Setter
    • @RequiredArgsConstructor

@Builder

  • 帮你生成了 set、get 方法,还在苦苦的一个个赋值???

  • 构造者模式:又称之为建造者模式

    • 场景:当一个 bean 类重载了多个构造方法时,并且参数随机使用时,考虑使用构造者模式
    • 谷歌的开源的 Protobuf 协议生产的 Java bean 赋值就是采用建造者模式
  • @Builder 注解

    • 作用在类上
1//添加
2@Builder
3@Data
4public class StudentDO {
5}
6
7//使用
8StudentDO studentDO = StudentDO.builder().age(1).email("794666918@qq").build();
9System.out.println(studentDO);

@Log/@Slf4j

  • @Log / @Slf4j
    • 作用于类上,生成日志变量, 用于记录日志
  • 如果不生效,记得检查下面的配置,另外重新更新下 lombok 插件,重启 idea
    • 开始按照创建的那边,记得开启 开启 annotion processing
  • 使用
 1@Slf4j
 2public class UserServiceImpl{
 3    public void login(String email){
 4	//方便记录日志
 5        log.info("用户登录:{}",email);
 6    }
 7}
 8
 9//字节码
10public class StudentDO {
11    private static final Logger log = Logger.getLogger(StudentDO.class.getName());
12}

Lombok 插件原理-对比反射技术

熟悉 Java 自定义注解的同学已经猜到是: JSR 269 插件化注解处理

1JSR 269:  Pluggable Annotation Processing API
2实现在Javac编译阶段利用“Annotation Processor”对自定义的注解进行预处理后生成真正在JVM上面执行的“Class文件
3地址:https://www.jcp.org/en/jsr/detail?id=269

Lombok 解析流程如下

image-20201227112619988

1Javac 解析成AST抽象语法树后, Lombok根据自己编写的注解处理器,动态地修改 AST增加新的节点(即Lombok自定义注解所需要生成的代码),最终生成JVM可执行的字节码Class文件
2
3可以看编译后的在target目录下的class文件

能实现上述效果的还有一个反射技术,那两个对比如何?

1使用Annotation Processing自定义注解是在编译阶段进行修改
2JDK的反射技术是在运行时动态修改
3
4结论:反射更加灵活一些但是带来的性能损耗更加大

hashcode 和 equal 方法

  • Equals ⽅法
    • 顶级类 Object ⾥⾯的⽅法,所有类都是继承 Object 的,返回值 boolean 类型
    • 根据⾃定义的匹配规则,⽤于匹配两个对象是否⼀样, ⼀般逻辑是如下
  • HashCode ⽅法
    • 顶级类 Object ⾥⾯的⽅法,所有类都是继承 Object 的,返回值 int 类型
    • 根据⼀定的 hash 规则(存储地址,字段,或者⻓度等),映射成⼀个数值,即散列值
1  //判断地址是否⼀样
2  //⾮空判断和class类型判断
3  //强转
4  //对象⾥⾯的字段⼀⼀匹配

解析

1如果两个对象相等,那么它们的hashCode()值一定相同(这里的相等是指,通过equals()比较两个对象时返回true)
2
3如果两个对象hashCode()相等,它们并不一定相等。在散列表中hashCode()相等,即两个键值对的哈希值相等。
4然而哈希值相等,并不一定能得出键值对相等,就出现所谓的哈希冲突场景,还需判断equals⽅法判断对象是否相等

应用场景:当向集合中插⼊对象时,如何判别在集合中是否已经存在该对象,⽐如 Set 确保存储对象的 唯⼀,并判断是不是同个对象呢?

1依据hashCode和equals进⾏判断
2所以Set存储的对象必须重写这两个⽅法 判断两个对象是否⼀样
3⾸先判断插⼊obj的hashcode值是否存在,hashcode值不存在则直 接插⼊集合
4值存在则还需判断equals⽅法判断对象是否相等
 1class User {
 2 private int age;
 3 private String name;
 4 private Date time;
 5 //省略setter和getter⽅法
 6 @Override
 7 public boolean equals(Object o) {
 8   if (this == o) return true;
 9   if (o == null || getClass() != o.getClass()) return false;
10   Student student = (Student) o;
11   return age == student.age &&
12   Objects.equals(name, student.name) &&
13   Objects.equals(time, student.time);
14 }
15 @Override
16 public int hashCode() {
17   return Objects.hash(age, name, time);
18 }
19}

优缺点适合场景

Lombok 插件是不是万能的???

image-20201230141126142

缺点:

  • Lombok 的使用要求一定要在 IDE 中安装对应的插件,如果项目组中有一个人使用了 Lombok 则都要用
  • 代码可读性,可调试性低,比如想知道某个类中的某个属性的 getter 方法都被哪些类引用
  • 影响升级,如果升级到某个新版本的 JDK 的时候,如果其中的特性在 Lombok 中不支持的话就会受到影响
  • 注意常见的细节点
1比如只使用了@Data,而不使用@EqualsAndHashCode(callSuper=true)的话,会默认是@EqualsAndHashCode(callSuper=false),这时候生成的equals()方法只会比较子类的属性,不会考虑从父类继承的属性,无论父类属性访问权限是否开放,只要知道是否需要使用父类的属性即可,也提供定制化配置,所以不用过多担心
    • 优点:
      • 使用注解即可帮忙自动生成代码
      • 大大减少了代码量,使代码非常简洁
      • 部分注解在业务项目中开发能大大提高效率
  • 项目中应该用还是不用呢
    • 不建议开发中间件的项目使用,中间件设计的要求是解耦少依赖
    • 业务项目实体类可以用,且用的时候知道对应的常见的注解原理
  • Lombok
    • @Setter/@Getter
    • @NonNull
    • @NoArgsConstructor
    • @AllArgsConstructor
    • @RequiredArgsConstructor
    • @ToString
    • @EqualsAndHashCode
    • @Data
    • @Builder
    • @Log
    • @Slf4j

作者:Soulboy