天天看点

你有没有使用过这些编程骚操作(一)- Lombok(Part B)

六、其他重要的Lombok注解

@Cleanup注解 与 @SneakyThrows注解

@Cleanup注解

@Cleanup注解可以生成对资源进行关闭的代码,无须手动通过try-catch-finally代码块判断并关闭所有的资源

在test包下新建CleanupAnnotationTest,测试@Cleanup注解

public class CleanupAnnotationTest {

    public void copyFile(String in, String out) throws Exception{
        @Cleanup FileInputStream fileInputStream = new FileInputStream(in);
        @Cleanup FileOutputStream fileOutputStream = new FileOutputStream(out);
        int r;
        while ((r = fileInputStream.read()) != -1){
            fileOutputStream.write(r);
        }
    }

    public static void main(String[] args) {
        
    }
}           

复制

@Cleanup注解使用在需要关闭资源的变量名前。执行main方法,查看target目录下生成的class文件

你有没有使用过这些编程骚操作(一)- Lombok(Part B)

这里自动生成了try-catch-finally代码块对资源进行了关闭操作,可以防止资源未关闭导致的性能问题

@SneakyThrows

@SneakyThrows方法只能作用在方法上,@SneakyThrows注解代替程序中的手动抛出异常代码既对受检异常进行捕捉并抛出

新建SneakyThrowsAnnotationTest测试类,将CleanupAnnotationTest的copyFile代码拷贝至SneakyThrowsAnnotationTest类下

public class SneakyThrowsAnnotationTest {
    @Test
    @SneakyThrows
    public void copyFile(String in, String out){
        @Cleanup FileInputStream fileInputStream = new FileInputStream(in);
        @Cleanup FileOutputStream fileOutputStream = new FileOutputStream(out);

        int r;

        while ((r = fileInputStream.read()) != -1){
            fileOutputStream.write(r);
        }
    }
}           

复制

添加了@SneakyThrows注解后,无须在方法上抛出异常,程序也不会报错

你有没有使用过这些编程骚操作(一)- Lombok(Part B)
你有没有使用过这些编程骚操作(一)- Lombok(Part B)

根据编译后的class文件可以确定,SneakyThrowsAnnotationTest类中有两层try-catch,最外层就是方法上添加了@SneakyThrows注解生成的异常处理代码。内层try-catch-finally则是资源关闭的代码

@Accessors注解

@Accessor注解是用于配置getter/setter方法生成的结果;Accessor是存取器、访问器的意思。@Accessor注解包含了三个属性分别是fluent、chain和prefix

chain 属性

在entity包下新建Porsche实体类,chain 属性可以设置为true或者false

@Data
@Accessors(chain = true)
public class Porsche {

    private Integer id;
    private String name;
    private Double price;
    private Integer stock;
}           

复制

运行maven的complie命令,查看target目录下的编译后的class文件

你有没有使用过这些编程骚操作(一)- Lombok(Part B)

@Accessor(chain=true)注解会在@Data注解生成的setter方法基础上做修改,将setter方法的返回值由void修改为实体类类型,因此可以执行链式操作

在test包下新建PorscheTest

public class PorscheTest {

    @Test
    public void testAccessorAnnotationChain(){
        Porsche prosche = new Porsche();

        prosche.setId(1).setName("Taycan").setPrice(880000.00).setStock(110);

        System.out.println(prosche);
    }
}           

复制

设置对象属性时,仅仅一行代码就可以搞定;执行测试方法

你有没有使用过这些编程骚操作(一)- Lombok(Part B)

fluent 属性

在entity包下新建Tesla实体类,fluent属性可以设置true或者false

@Data
@Accessors(fluent = true)
public class Tesla {

    private Integer id;
    private String vehicleName;
    private String vehicleType;
    private String vehiclePrice;
    private String factory;
}           

复制

运行maven的compile编译命令,查看target目录下编译后的Tesla实体类的class文件

你有没有使用过这些编程骚操作(一)- Lombok(Part B)

@Accessor注解fluent=true会在chain=true的基础上将getter/setter的方法名改为属性名

在test包下新增TeslaTest测试类

public class TeslaTest {

    @Test
    public void testAccessorAnnotationFluent(){
        Tesla tesla = new Tesla();
        tesla.id(1).vehicleName("Model S").vehicleType("轿跑").vehiclePrice("880000").factory("上海超级工厂");
        System.out.println(tesla);
    }
}           

复制

相比chain=true属性的设置,fluent=true将getter/setter方法名进行了统一,调用属性名的方法时如果传参就相当于调用setter方法,如果不传参就相当于调用getter方法;

执行测试方法

你有没有使用过这些编程骚操作(一)- Lombok(Part B)

prefix属性

prefix属性可以将指定的前缀表示去除,在entity包新建Jaugar实体类

@Data
@Accessors(prefix = "j")
public class Jaugar {

    private Integer jId;
    private String jName;
    private Double jPrice;
}           

复制

点击maven的compile命令,查看target目录下的Jaguar class文件

你有没有使用过这些编程骚操作(一)- Lombok(Part B)

@Accessors(prefix = "j")注解在@Data生成的getter/setter方法的基础上,将指定的前缀去除

七、Lombok中 @Slf4j 日志注解使用

@Slf4j可以简化日志的引入,用于代替以下这段代码

private static final Logger logger = LoggerFactory.getLogger(Xxx.class);           

复制

你有没有使用过这些编程骚操作(一)- Lombok(Part B)

上图中slf4j和commons-loggin都是日志规范;logback jdk-logging log4j log4j2都是日志实现。想要做到日志统一就需要用到桥接包,也可以参考SLF4J官网给出的解决方案

@Slf4j注解

首先添加相关的Jar包支持

<!-- 日志规范 -->
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>1.7.36</version>
</dependency>

<!-- 日志实现 -->
<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>1.2.11</version>
  <scope>test</scope>
</dependency>           

复制

在test包中新建一个LogAnnotationTest测试类,使用@Slf4j注解

@Slf4j
public class LogAnnotationTest {
    
    @Test
    public void testLogger(){
        log.error("这是输出的日志信息");
    }
}           

复制

执行testLogger方法

你有没有使用过这些编程骚操作(一)- Lombok(Part B)

查看编译后的文件

你有没有使用过这些编程骚操作(一)- Lombok(Part B)

自动帮我们生成了一个构造方法,并且定义了一个log属性,这个属性是slf4j的LoggerFactory通过调用getLogger方法返回的,并且通过接口来输出日志,这也是比较推荐的方法

八、对象创建 @Builder注解 与 @Singular注解

@Builder注解

@Builder注解的作用将对象的创建和使用完全分割开来,对象的创建只能用@Builder来创建,创建完成之后,对象不可变,可以使用这个对象,但是不能修改,这也符合高耦合低内聚的原则。

在test包下新建一个BuilderAnnotationTest测试类

@Builder
@Data
public class BuilderAnnotationTest {

    private static String staticField;

    private final String finalField;

    private final String initFinalField = "已经初始化的字段";

    private String field;

    public static void main(String[] args) {
        // 创建BuilderAnnotationTest对象
        // 首先调用builder方法创建一个可以链式赋值的对象
        BuilderAnnotationTest builderAnnotationTest = BuilderAnnotationTest.builder()
                                                      .finalField("手动赋值字段finalField")
                                                      // 其他属性不能赋值
                                                      .field("手动赋值field")
                                                      // 创建对象,此时创建的对象是不可变的
                                                      .build();

        System.out.println(builderAnnotationTest);
    }
}           

复制

执行main方法

你有没有使用过这些编程骚操作(一)- Lombok(Part B)

根据控制台的输出说明可以成功创建对象

查看target目录下编译后的代码

@Builder注解帮我们创建了一个构造方法

你有没有使用过这些编程骚操作(一)- Lombok(Part B)

定义了一个builder方法来创建对象BuilderAnnotationTestBuilder 内部类

你有没有使用过这些编程骚操作(一)- Lombok(Part B)

其中getter/setter/hashcode/equals方法都是@Data注解生成的

@Builder注解生成的内部类

你有没有使用过这些编程骚操作(一)- Lombok(Part B)

这个类包含两个属性和一个空参构造方法,以及几个以属性名为方法名的方法,用于给属性赋值,还包含了toString方法以及build方法,build方法就是用于创建BuilderAnnotationTest对象的方法,使用了内部的两个属性,调用BuilderAnnotationTest上面的包含两个参数的构造方法来创建对象

总结下来就是创建一个内部类,用来持久化需要赋值的属性的属性值,并且使用这些属性通过调用构造方法来创建一个不可变的对象,对象创建过程对外是不可见的,所以对象是不可修改的

给普通属性赋值,再次调用main方法,查看创建出来的对象的普通属性的值是否会变化

你有没有使用过这些编程骚操作(一)- Lombok(Part B)
你有没有使用过这些编程骚操作(一)- Lombok(Part B)

普通属性的初始值创建对象的时候不会带过来;默认的值如果不手动赋值,是不会带过来的

@Singular注解

@Singular注解配合@Builder注解使用,可以简化集合类型的操作 给BuilderAnnotationTest类增加一个List列表属性,测试@Singular如何简化操作

private List<String> listFields;           

复制

修改main方法,手动给list属性赋值

public static void main(String[] args) {
    // 创建BuilderAnnotationTest对象
    // 首先调用builder方法创建一个可以链式赋值的对象
    BuilderAnnotationTest builderAnnotationTest = BuilderAnnotationTest.builder()
                                                  .finalField("手动赋值字段finalField")
                                                  // 其他属性不能赋值
                                                  .field("手动赋值field")
                                                  .listFields(new ArrayList<>())
                                                  // 创建对象,此时创建的对象是不可变的
                                                  .build();           

复制

执行main方法

你有没有使用过这些编程骚操作(一)- Lombok(Part B)

列表属性上添加@Singular注解

@Singular
private List<String> listFields;           

复制

再次手动给列表属性赋值

你有没有使用过这些编程骚操作(一)- Lombok(Part B)

有两个方法可以进行赋值

你有没有使用过这些编程骚操作(一)- Lombok(Part B)
你有没有使用过这些编程骚操作(一)- Lombok(Part B)

@Singular注解可以对集合属性生成单独追加单个元素的方法,并且可以连续追加。执行测试

你有没有使用过这些编程骚操作(一)- Lombok(Part B)

listField方法赋值成功。也可以连续调用listField赋值多个元素到列表中

你有没有使用过这些编程骚操作(一)- Lombok(Part B)

执行测试

你有没有使用过这些编程骚操作(一)- Lombok(Part B)

说明连续调用赋值也是成功的

查看target目录下生成的class代码

你有没有使用过这些编程骚操作(一)- Lombok(Part B)
你有没有使用过这些编程骚操作(一)- Lombok(Part B)

listField方法可以接收一个单独的String类型参数,并将该参数加入到初始化号的listFields 中,相当于帮我们解决了集合为空的时候如何填入第一个元素

你有没有使用过这些编程骚操作(一)- Lombok(Part B)

还生成了一个clearListFields方法,当集合不为空的时候清除集合

调用build方法的时候会判断集合是否为空,集合为空的时候会创建一个空的list赋值给集合属性,如果只有一个元素的时候,会创建一个singletonList赋值给列表,最后创建一个不可变的集合赋值给列表属性