JAVA代码简化之Lombok

911 阅读4分钟

参考:lombok官网文档

lombok是一个实现了”JSR 269 API”的程序,编译的流程如下:

1)javac对源代码进行分析,生成一棵**抽象语法树(AST) **

2)运行过程中调用实现了”JSR 269 API”的程序(lombok)

3)此时lombok程序就可以完成它自己的逻辑,包括修改第一步骤得到的抽象语法树(AST)

4)javac使用修改后的抽象语法树(AST)生成字节码文件

优点:消除样板代码,提升代码简洁度,减轻维护工作量

缺点:必须安装插件,不清楚底层实现的风险,IDE和JDK升级存在破裂的风险

一、配置

1)添加依赖

可以在Maven仓库或者Lombok官网中找到依赖:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.12</version>
    <scope>provided</scope>
</dependency>

2)添加插件

在Idea的File-Settings-Plugins-Marketplace中搜索Lombok进行Install:

如果没有安装此插件,不能对Bean进行set/get操作

二、使用

1.@Data

@Data注解用于Bean上,是一个方便快捷的注解,包含了@ToString、@EqualsAndHashCode、@Getter、@Setter(非Final字段)、@RequiredArgsConstructor,不需要写set、get、equals、hashCode、toString以及构造方法。

  • @Setter :注解在类或字段,注解在类时为所有字段生成setter方法,注解在字段上时只为该字段生成setter方法。
  • @Getter :使用方法同上,区别在于生成的是getter方法。
  • @ToString :注解在类,添加toString方法。
  • @EqualsAndHashCode: 注解在类,生成hashCode和equals方法。
  • @RequiredArgsConstructor: 注解在类,为类中需要特殊处理的字段生成构造方法,比如final和被@NonNull注解的字段。
@Data 
public class DataExample {
  private String name;
  private int age;
}

如需要对部分字段进行个性化,可参考:@Data详解

2.@Builder

@Builder注解用于Bean对象创建过程中,节省重复set代码,让代码更优雅

@Builder
public class DataExample {
  private String name;
  private int age;
}
// 传统方式创建对象
Student student = new Student();
student.setAge(1);
student.setName("admin");
// 使用Builder创建对象
Student student2 = Student.builder()
        .age(1)
        .name("admin")
        .build();

如果想2种方法混搭着使用,注意添加无参构造@NoArgsConstructor:

@AllArgsConstructor
@NoArgsConstructor
@Builder
@Data
public class DataExample {
  private String name;
  private int age;
}
2.1.@Builder中@Singular的使用

@Singular用于集合类型字段赋值,并且可以结合value去定义集合内参数变量名,如下:

@AllArgsConstructor
@NoArgsConstructor
@Builder
@Data
public class Student {
    private String name;
    private int age;
    @Builder.Default
    private final String id = UUID.randomUUID().toString();
    @Singular
    private List<String> books;
    @Singular(value = "score")
    private List<String> scoreList;
}
public static void main(String[] args) {
        // 不使用Singular注解
        List<String> list = new ArrayList<>();
        list.add("数学");
        list.add("语文");
        Student student3 = Student.builder()
                .age(1)
                .name("admin")
                .books(list)
                .build();
        System.out.println(student3.toString());
        
        // 使用Singular注解
        Student student2 = Student.builder()
                .age(1)
                .name("admin")
                .book("数学").book("语文")
                .build();
        System.out.println(student2.toString());
    }

输出结果:

Student(name=admin, age=1, id=18e76cc6-0a78-47b0-8961-f9594c32b6ce, books=[数学, 语文])
Student(name=admin, age=1, id=1fbafd1e-27f9-4b3a-b78b-3e4e0b96fe22, books=[数学, 语文])
2.2.@Builder.Default的使用

@Builder.Default用于对字段进行初始化值,如设置UUID主键:

@AllArgsConstructor
@NoArgsConstructor
@Builder
@Data
public class Student {
    private String name;
    private int age;
    @Builder.Default
    private final String id = UUID.randomUUID().toString();
    @Builder.Default
    private long current = System.currentTimeMillis();
}

在创建对象时:

public static void main(String[] args) {
        // 不对id进行赋值
        Student student3 = Student.builder()
                .age(1)
                .name("admin")
                .build();
        System.out.println(student3.toString());
        // 对id进行赋值
        Student student2 = Student.builder()
                .id("123")
                .age(1)
                .name("admin")
                .build();
        System.out.println(student2.toString());
    }

输出结果:

Student(name=admin, age=1, id=4a2174f5-7b1b-44f1-8987-cfcbf1af7931)
Student(name=admin, age=1, id=123)

3.@Log

日志输出

@Log
public class LogExample {
  public static void main(String... args) {
    log.severe("Something's wrong here");
  }
}

@Slf4j
public class LogExampleOther {
  public static void main(String... args) {
    log.error("Something else is wrong here");
  }
}

@CommonsLog(topic="CounterLog")
public class LogExampleCategory {
  public static void main(String... args) {
    log.error("Calling the 'CounterLog' with a message");
  }
}

4.@NonNull

使用@NonNull让lombok为你自动生成null-check语句,并且null检查会被添加到方法的最顶层,如:

if (param == null) {
	throw new NullPointerException("param is marked @NonNull but is null");
}

示例

 public static void main(String[] args) {
        test("这是一个测试!");
        test("");
        test(null);
    }

public static void  test(@NonNull String string){
    System.out.println("=======>"+string);
}

输出结果:

=======>这是一个测试!
=======>
Exception in thread "main" java.lang.NullPointerException: string is marked non-null but is null
	at com.hello.study.controller.ManageController.test(ManageController.java:19)
	at com.hello.study.controller.ManageController.main(ManageController.java:16)

5.@NoArgsConstructor和@AllArgsConstructor

@NoArgsConstructor: 注解在类,生成无参的构造方法。

@AllArgsConstructor: 注解在类,生成包含类中所有字段的构造方法。

6.@RequiredArgsConstructor

@RequiredArgsConstructor:注解在类,为类中需要特殊处理的字段生成构造方法(构造注入),比如final和被@NonNull注解的字段。 如下:

@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class DemoServiceImpl implements DemoService {

    private final DataSource dataSource;
    private final DemoMapper demoMapper;
}

相当于:

public class DemoServiceImpl implements DemoService {

    private DataSource dataSource;
    private DemoMapper demoMapper;
    
    @Autowired
    public DemoServiceImpl(DataSource dataSource, DemoMapper demoMapper) {
        this.dataSource = dataSource;
        this.demoMapper = demoMapper;
    }
}
  • Filed注入:直接在字段上添加@Autowired或者@Resource
  • 构造器注入:适合强制性的注入,旨在不变性(spring官方推荐)
  • Setter注入:适合可变性的注入。

构造器注入能够保证注入的组件不可变,并且确保需要的依赖不为空,并且总是能保证完全初始化的状态。

但是使用构造注入时,2个类相互调用会出现循环依赖的问题,相互依赖的类,可以使用@Resource或者@Autowired单独处理