Lombok开发提效神器之使用整合及原理解析

739

Lombok

以下所有的注解操作,都可以通过该命令:mvn compile打包编译 查看编译后字节码样子,你就会发现Lombok通过注解是怎么修改的

Lombok插件IDEA安装

什么是lombok

官网:projectlombok.org/

一个优秀的java代码库,简化了java的编码,为java代码的精简提供了一种方式

lombok消除了java的冗长代码,尤其是对于简单的java对象,加上注解即可

使用方式

Step1:pom依赖添加

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
<scope>provided</scope>
</dependency><!--https://mvnrepository.com/artifact/org.projectlombok/lombok/1.18.16-->
<!--scope=provided,说明它只在编译阶段生效,不需要打入包中, Lombok在编译期将带Lombok注解的Java文件正确编译为完整的Class文件-->

Step2:添加IDE工具对Lombok的支持

点击File-Settings设置界面,安装Lombok插件,重启IDE

WechatIMG280.png

Step3:需要在设置中启用annotation processors,重启IDEA

WechatIMG281.png

Lombok插件注解Set/Get方法

常见注解

@Getter/@Setter/@Data

作用类上,生成所有成员变量的getter/setter方法

作用于成员变量上,生成该成员变量对应的getter/setter方法

可通过编译查看字节码 mvn compile

进一步控制

方法控制访问级别、不生成set或get方法

@Setter
@Getter//对全部属性增加set/get方法
public class User {

    /**
     * 不想生成 get方法
     */
    @Getter(AccessLevel.NONE)
    private int id;

    /**
     * 控制访问权限
     */
    @Getter(AccessLevel.PROTECTED)
    private String name;

    private final String DEMONAME = "金华";

    static Date createTime = new Date();
    private static final String ADDRESS = "浙江省金华市";
}

NonNull+构造函数注解ArgsConstructor实战

老式参数校验

    public void login(String pwd){
        //参数校验
        if(pwd != null){
            //TODO
        }else {
            throw new NullPointerException();
        }
    }

NonNull参数校验

会在编译时字节码中增加逻辑校验

    public void login(String pwd){
        //参数校验
        if(pwd != null){
            //TODO
        }else {
            throw new NullPointerException();
        }
    }

生成空构造函数

生成全部参数构造函数

@NoArgsConstructor//生成空构造函数
@AllArgsConstructor//全部参数生成构造函数(不会对final以及静态生成构造函数)

指定参数的构造函数

@RequiredArgsConstructor
1)final类型未被初始化的属性,且标记了@NonNull的属性
2@NoArgsConstrucotr不能作用在类上

Lombok插件对比反射技术

插件注解化

JSR 269插件注解处理

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

科普

JSR是Java Specification Requests的缩写,意思是Java规范提案
是指想JCP(java Community Process)提出新增一个标准化技术规范的正式请求
任何人都可以提交JSR,以向java平台增添新的API和服务。JSR已成为java界的一个重要标准

Lombok解析流程

WechatIMG282.png

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

可以看编译后的在target目录下的class文件

反射技术对比

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

使用Annotation Processing自定义注解是在编译阶段进行修改(做好使用)
JDK的反射技术是在运行时动态修改(边做边用)

结论:反射更加灵活一些但是带来的性能损耗更加大

Lombok的toString()注解

javabean对象为什么要重写toString()注解?

  • List或者其他集合调试不方便
  • 控制台或日志输出对象, 默认打印的是内存地址

🌰

UserDO userDO = new UserDO();
  userDO.setAge(11);
  System.out.println(userDO);
  //输出信息    
  com.keep.shop.model.UserDO@1677d1

@ToString

1.作用于类上,覆盖默认的toString()方法

2.不包括某个字段

@ToString(exclude = {"age"})

3.只输出某个字段

@ToString(of = {"name"})

hashcode和equal方法

为什么对象要重写hashcode和equal方法?

HashCode方法

顶级类Object的方法,所有类都是继承Object的,返回值int类型

根据一定的hash规则(存储地址、字段或者长度等),映射成一个数值,即散列值

Equals方法

顶级类Object的方法,所有类都是继承Object的,返回值boolean类型

根据自定义的匹配规则,用于匹配两个对象是否一样,一般逻辑如下

//判断地址是否一样
//非空判断和class类型判断
//强转
//对象里面的字段是否一一匹配

解析

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

应用场景

🌰:当向集合中插入对象时,如何判断在集合中是否已经存在该对象,比如Set确保存储对象的唯一,并判断是不是同个对象呢?

根据hashCode和equals进行判断
所以Set存储的对象必须重写两个方法,判断两个对象是否一样
首先判断插入的obj的hashcode值是否存在,hashcode值不存在则直接插入集合
值存在还需要判断equals方法判断对象是否相等
class User {
 private int age;
 private String name;
 private Date time;
 //省略setter和getter⽅法
 @Override
 public boolean equals(Object o) {
   if (this == o) return true;
   if (o == null || getClass() != o.getClass()) return false;
   Student student = (Student) o;
   return age == student.age &&
   Objects.equals(name, student.name) &&
   Objects.equals(time, student.time);
 }
 @Override
 public int hashCode() {
   return Objects.hash(age, name, time);
 }
}

对象匹配EquealsAndHashCode注解

@EqualsAndHashCode

作用于类,覆盖默认的equals和hashCode,作用于全部属性

  • 不包括某个属性
@EqualsAndHashCode(exclude = {"age"})
  • 只输出某个属性
@EqualsAndHashCode(of = {"name"})

Lombok插件多注解集合配置Data

上面lombok讲解了那么多注解,一个个加太麻烦了吧🐴

@Data

定义一个干净的类,定义在类上,增加此注解(上面的注解都可去掉不用)mvn compile查看字节码后的效果

(但是该注解不会生成有参构造函数)

@Data
public class User {
    private int id;
}

建造者模式和Lombok注解@Builder

lombok已经帮你get、set了,你在使用对象时还在一个个的赋值么?

例如下面的操作

	public static void main(String[] args) {
		User user = new User();
		user.setId(1);
		user.setName("法外狂徒:张三");

	}

建造者模式

或叫构建者模式

场景:当一个bean类重载了多个构造方法时,并且参数随机使用时,考虑使用建造者模式

谷歌的开源protobuf协议生产的javabean赋值就是采用建造者模式

@Builder注解

//添加
@Builder
@Data
public class User {
}

//使用
		User user = User.builder()
				.id(1)
				.name("法外狂徒张三").build();
		System.out.println(user);

日志打印更方便-@Log-@Slf4j

@Log/@Slf4j

作用于类上,生成日志变量,用于记录日志

如果不生效,记得检查lombok插件

原有日志打印我们需要写上一行Logger

    private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);

使用

@Slf4j
public class UserServiceImpl {
    
    public void getUser(String name){
        try {
            //TODO
            log.info("用户登录名称:{}",name);
        }catch (Exception e){
            log.error("UserServiceImpl.getUser error! name: {}"+name,e);
        }
    }
}

注意:在打印错误日志时一定要打全堆栈信息,具体要看调用的是哪个重载的方法

下面的error参数方法才能打全 也就是我上面的写法,如果你将+改成逗号就不行了

    public void error(String msg, Throwable t);

Lombok常用注解

注解作用列描述
@Getter/@Setter类/变量生成变量的getter/setter方法
@NonNull入参参数非空校验
@NoArgsConstructor生成空构造函数
@AllArgsConstructor生成全部参数构造函数
@RequiredArgsConstructor指定参数生成构造函数
@ToString重写toString()方法
@EqualsAndHashCode重写hashCode和equals方法
@Data多个注解的合集
@BuilderBean赋值
@Log/@Slf4j日志打印

Lombok使用场景优缺点

缺点

  • 使用要求一定要在IDE中安装对应插件,如果项目组中有一个人使用了,则所有人都要使用
  • 代码可读性,可调试性低,比如你想知道某个类中的某个属性的getter方法都被那些类引用,但你没办法看到
  • 影响升级,如果升级到某个新版本的JDK的时候,如果其中的特性在lombok中不支持的话就会受到影响
  • 注意常见细节点
比如只使用了@Data,而不使用@EqualsAndHashCode(callSuper=true)的话,会默认是@EqualsAndHashCode(callSuper=false),这时候生成的equals()方法只会比较子类的属性,不会考虑从父类继承的属性,无论父类属性访问权限是否开放,只要知道是否需要使用父类的属性即可,也提供定制化配置,所以不用过多担心

优点

  • 使用注解即可帮忙自动生成代码
  • 大大减少了代码量,使代码非常简介
  • 部分注解在业务项目中开发能提高效率

项目中到底该不该使用?

不建议开发中间件的项目中使用,中间件设计的要求就是要求解耦减少依赖**(高内聚、低耦合)**

业务项目实体类可以用,且用的时候知道对应的常见的注解的原理

感觉不错的大佬点个赞呗,手敲截图演示不易

WechatIMG244.jpeg