Lombok正确使用姿势,你真的了解了吗?


介绍

Project Lombok是一个java库,它可以自动插入编辑器和构建工具,通过Lombok我们不需要再在类上编写setter/getter、equals、try/catch、无参构造器、全参构造器 等方法。

Lombok features(特性)

Lombok包下注解:

Lombok属性注解特性说明
val用在局部变量前面,相当于将变量声明为 final
@NonNull给方法参数增加这个注解会自动在方法内对该参数进行是否为空的校验,如果为空,则抛出NPE(NullPointerException)
@Cleanup自动管理资源,用在局部变量之前,在当前变量范围内即将执行完毕退出之前会自动清理资源,自动生成 try-finally 这样的代码来关闭流
@Getter/@Setter用在属性上,再也不用自己手写settergetter方法了,还可以指定访问范围
@ToString用在类上可以自动覆写toString方法,当然还可以加其他参数,例如 @ToString(exclude=“id”) 排除id属性,或者 @ToString(callSuper=true, includeFieldNames=true) 调用父类的toString方法,包含所有属性;
@EqualsAndHashCode用在类上自动生成equals方法和hashCode方法
@NoArgsConstructor, @RequiredArgsConstructor and @AllArgsConstructor用在类上,自动生成无参构造和使用所有参数的构造函数以及把所有 @NonNull 属性作为参数的构造函数,如果指定 staticName="of" 参数,同时还会生成一个返回类对象的静态工厂方法,比使用构造函数方便很多
@Data注解在类上,相当于同时使用了@ToString、@EqualsAndHashCode、@Getter、@Setter和 @RequiredArgsConstrutor这些注解,对于POJO类十分有用
@Value用在类上,是@Data的不可变形式,相当于为属性添加final声明,只提供getter方法,而不提供setter方法
@Builder用在类、构造器、方法上,为你提供复杂的 builder APIs,让你可以像如下方式一样调用Person.builder().name("xxx").city("xxx").build()
@SneakyThrows自动抛受检异常,而无需显式在方法上使用throws语句
@Synchronized用在方法上,将方法声明为同步的,并自动加锁,而锁对象是一个私有的属性 LOCK,而Java中的synchronized关键字锁对象是 this,锁在this或者自己的类对象上存在副作用,就是你不能阻止非受控代码去锁this或者类对象,这可能会导致竞争条件或者其它线程错误
@Getter(lazy=true)可以替代经典的Double Check Lock样板代码
@Log根据不同的注解生成不同类型的log对象,但是实例名称都是log,有六种可选实现类 @CommonsLog Creates log = org.apache.commons.logging.LogFactory.getLog(LogExample.class); @Log Creates log = java.util.logging.Logger.getLogger(LogExample.class.getName()); @Log4j Creates log = org.apache.log4j.Logger.getLogger(LogExample.class); @Log4j2 Creates log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class); @Slf4j Creates log = org.slf4j.LoggerFactory.getLogger(LogExample.class); @XSlf4j Creates log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class)

@SneakyThrows

一般都是标注在方法上面,通过@SneakyThrows我们不需要是手动写try{}...catch(Throwable e){}代码,Lombok会自动为我们生成,举个🌰:

@Override
@SneakyThrows
public String createOrder(CreateOrderDTO createOrderDTO) {
    boolean res = stockService.checkStock(createOrderDTO);
    if (res) {
        doCreateOrder(createOrderDTO);
    }
    return "create order success!";
}
复制代码

最后编译生成的代码是这样的:

public String createOrder(CreateOrderDTO createOrderDTO) {
    try {
        boolean res = stockService.checkStock(createOrderDTO);
        if (res) {
            this.doCreateOrder(createOrderDTO);
        }
        return "create order success!";
    } catch (Throwable var3) {
        throw var3;
    }
}
复制代码

@CleanUp

JAVA在实现IO流读写的场景,每次都要在finally里面关闭资源,这样会很让人头疼?那么有没有好的方法去生成这样的重复代码。方法有两种:一种是使用Lombok@Cleanup,另一种是使用jdk1.7+try-with-resources语法糖。我个人推荐使用try-with-resources语法糖,因为它是jdk提供的,所以受众更广,别人能更容易读懂你的代码,也不用绑定插件才能使用。在关闭流(资源)的时候,经常使用到以下代码:

public void cleanUpSample() {
    String filePath = "E://test.txt";
    FileReader reader = null;
    try {
        reader = new FileReader(filePath);
    } catch (FileNotFoundException e) {
        throw new RuntimeException(e);
    } finally {
        try {
            // 有时候可能会忘记对文件资源close处理
            reader.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
复制代码

当在处理文件对象,或者数据库资源时,我们总是会忘记close,可能引发内存溢出或者连接池用尽。如果手动去调用close方法,代码又会非常长,现在有了@Cleanup,我们不再需要担心这些问题。通过使用@Cleanup确保在代码执行路径退出当前作用域之前自动清除给定资源,举个例子:

public void cleanUpSample() {
    String filePath = "E://test.txt";
    try {
        @Cleanup FileReader reader = new FileReader(filePath);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
复制代码

最后编译生成的代码是这样的:

public void cleanUpSample() {
    String filePath = "E://test.txt";

    try {
        FileReader reader = new FileReader(filePath);
        // @CleanUp注解实现自动为代码释放资源
        if (Collections.singletonList(reader).get(0) != null) {
            reader.close();
        }
    } catch (FileNotFoundException var3) {
        var3.printStackTrace();
    } catch (IOException var4) {
        var4.printStackTrace();
    }
}
复制代码

Lombok.experimental包下注解:

Lombok注解特性说明
@Accessors一般的用法@Accessors(chain = true),当设置chaintrue的时候,set方法返回的是当前对象,则在进行属性赋值则可以连续进行,如:createOrderDTO.setProductId("1").setQuantity(1).setAmount(new BigDecimal("100"));
@UntilityClass使用@UtilityClass修饰类,类会被标记成final修饰类,属性会标记成静态属性,会生成一个私有的无参构造方法,并抛出一个UnsupportedOperationException异常,方法都会被标记成静态方法。
@Tolerate使用@Builder对一个DTO实现一个构造器,但是在做Json反序列化的时候发生错误,原因就是缺少无参公共的构造函数,而手动写一个无参构造函数的时候编译错误,就是和@Builder冲突,虽然标准的@Builder没法是需要私有化构造函数的,但是在某些场景下我们需要对这种标准变形,这个时候Lombok提供了@Tolerate实现对冲突的兼容。

@UntilityClass

@UntilityClass主要是针对工具类的一个注解,它的作用是,被@UntilityClass修饰的类它的所有成员变量都是静态的,可以直接通过ExampleClass.method()直接调用,省了创建实例对象的过程,举个例子:

@UtilityClass
public class GlobalWebUtils extends WebUtils {

    /**
     * 获取 HttpServletRequest
     *
     * @return {HttpServletRequest}
     */
    public Optional<HttpServletRequest> getRequest() {
        return Optional.ofNullable(((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest());
    }

    /**
     * 获取 HttpServletResponse
     *
     * @return {HttpServletResponse}
     */
    public HttpServletResponse getResponse() {
        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
    }

}
复制代码

使用工具类的时候,直接通过类调用:

@Override
@SneakyThrows
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response) {
    log.warn("表单登录失败:{}", exception.getMessage());
    String url = "http://xxx.com";
    // 使用工具类直接调用
    GlobalWebUtils.getResponse().sendRedirect(url);
}
复制代码

Lombok使用注意点

1. Lombok中@Builder和@Data注解使用注意

  • @Builder使用设计模式中的创建者模式又叫建造者模式。简单来说,就是一步步创建一个对象,它对用户屏蔽了里面构建的细节,但却可以精细地控制对象的构造过程;
  • @Data注解的主要作用是提高代码的简洁,使用这个注解可以省去代码中大量的get()set()、 toString()等方法;

在项目中查询数据库后反射到实体类报错,找不到无参构造器。查看对应实体类上只使用@Builder、 @Data注解。

💥当@Data和@Builder 共同使用导致无参构造丢失

  • 单独使用@Data注解,会生成无参数构造方法;
  • 单独使用@Builder注解,生成全属性的构造方法,无无参构造方法;
  • @Data@Builder一起用:没有了默认的构造方法。手动添加无参数构造方法或者用@NoArgsConstructor注解会报错。

如果仅仅是使用@Data@Builder两个注解修饰实体类,编译之后生成的class文件:

public class CreateOrderDTO {

    private String productId;
    private Integer quantity;
    private BigDecimal amount;

    // (default类型) 全参构造器
    CreateOrderDTO(final String productId, final Integer quantity, final BigDecimal amount) {
        this.productId = productId;
        this.quantity = quantity;
        this.amount = amount;
    }
    
    // Builder
    public static CreateOrderDTOBuilder builder() {
        return new CreateOrderDTOBuilder();
    }

    public String getProductId() {
        return this.productId;
    }

    // equals()、hashCode()、GET/SET()等代码已省略

    public static class CreateOrderDTOBuilder {
        private String productId;
        private Integer quantity;
        private BigDecimal amount;

        CreateOrderDTOBuilder() {
        }

        public String toString() {
            return "CreateOrderDTO.CreateOrderDTOBuilder(productId=" + this.productId + ", quantity=" + this.quantity + ", amount=" + this.amount + ")";
        }
    }
}
复制代码

可以看出如果是同时使用@Data@Builder的话,会生成属性的GET/SET方法,但是无参构方法没有了,对象缺少无参构造器是不可接受的,因为很多框架都会调用无参构造函数去创建对象。

如果这时候在实体类上添加无参构造函数,在项目运行会编译报错,此时可以手动添加一个有参函数,也可以在无参函数上添加@Tolerate注解实现无参构造器的生成。

无参构造函数加上@Tolerate注解才能编译成功!

如果是使用@Data@Builder@AllArgsConstructor三个注解修饰实体类,编译之后生成的class文件跟上面的一样,会生成无参构造器、全参构造器、Builder、getter&setter、equals、hashCode等方法,区别是全参构造器为public修饰符修饰

public CreateOrderDTO(final String productId, final Integer quantity, final BigDecimal amount) {
    this.productId = productId;
    this.quantity = quantity;
    this.amount = amount;
}
复制代码

正常我们可以这样来定义一个实体类:

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CreateOrderDTO {

    /**
     * 商品ID
     */
    private String productId;

    /**
     * 商品数量
     */
    private Integer quantity;

    /**
     * 订单金额
     */
    private BigDecimal amount;
}
复制代码

最后编译测class文件 (无参构造器、全参构造器、GET/SET、equals、hashCode、toString、Builder等方法):

public class CreateOrderDTO {
    private String productId;
    private Integer quantity;
    private BigDecimal amount;

    public static CreateOrderDTOBuilder builder() {
        return new CreateOrderDTOBuilder();
    }

    public String getProductId() {
        return this.productId;
    }

    public Integer getQuantity() {
        return this.quantity;
    }

    public BigDecimal getAmount() {
        return this.amount;
    }

    public void setProductId(final String productId) {
        this.productId = productId;
    }

    public void setQuantity(final Integer quantity) {
        this.quantity = quantity;
    }

    public void setAmount(final BigDecimal amount) {
        this.amount = amount;
    }

    public boolean equals(final Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof CreateOrderDTO)) {
            return false;
        } else {
            CreateOrderDTO other = (CreateOrderDTO)o;
            if (!other.canEqual(this)) {
                return false;
            } else {
                label47: {
                    Object this$quantity = this.getQuantity();
                    Object other$quantity = other.getQuantity();
                    if (this$quantity == null) {
                        if (other$quantity == null) {
                            break label47;
                        }
                    } else if (this$quantity.equals(other$quantity)) {
                        break label47;
                    }

                    return false;
                }

                Object this$productId = this.getProductId();
                Object other$productId = other.getProductId();
                if (this$productId == null) {
                    if (other$productId != null) {
                        return false;
                    }
                } else if (!this$productId.equals(other$productId)) {
                    return false;
                }

                Object this$amount = this.getAmount();
                Object other$amount = other.getAmount();
                if (this$amount == null) {
                    if (other$amount != null) {
                        return false;
                    }
                } else if (!this$amount.equals(other$amount)) {
                    return false;
                }

                return true;
            }
        }
    }

    protected boolean canEqual(final Object other) {
        return other instanceof CreateOrderDTO;
    }

    public int hashCode() {
        int PRIME = true;
        int result = 1;
        Object $quantity = this.getQuantity();
        result = result * 59 + ($quantity == null ? 43 : $quantity.hashCode());
        Object $productId = this.getProductId();
        result = result * 59 + ($productId == null ? 43 : $productId.hashCode());
        Object $amount = this.getAmount();
        result = result * 59 + ($amount == null ? 43 : $amount.hashCode());
        return result;
    }

    public String toString() {
        return "CreateOrderDTO(productId=" + this.getProductId() + ", quantity=" + this.getQuantity() + ", amount=" + this.getAmount() + ")";
    }

    public CreateOrderDTO() {
    }

    public CreateOrderDTO(final String productId, final Integer quantity, final BigDecimal amount) {
        this.productId = productId;
        this.quantity = quantity;
        this.amount = amount;
    }

    public static class CreateOrderDTOBuilder {
        private String productId;
        private Integer quantity;
        private BigDecimal amount;

        CreateOrderDTOBuilder() {
        }

        public CreateOrderDTOBuilder productId(final String productId) {
            this.productId = productId;
            return this;
        }

        public CreateOrderDTOBuilder quantity(final Integer quantity) {
            this.quantity = quantity;
            return this;
        }

        public CreateOrderDTOBuilder amount(final BigDecimal amount) {
            this.amount = amount;
            return this;
        }

        public CreateOrderDTO build() {
            return new CreateOrderDTO(this.productId, this.quantity, this.amount);
        }

        public String toString() {
            return "CreateOrderDTO.CreateOrderDTOBuilder(productId=" + this.productId + ", quantity=" + this.quantity + ", amount=" + this.amount + ")";
        }
    }
}
复制代码
<think>嗯,用户问的是Java Lombok怎么使用,而且要求用中文说人话。首先,我需要回忆一下Lombok的基本功能和常见用法。Lombok是一个通过注解减少Java样板代码的工具,比如自动生成getter、setter、构造函数等。 用户可能是一个刚开始学习Java的新手,或者是对减少重复代码感兴趣的开发者。需要解释清楚Lombok的作用,以及如何安装和使用。要避免使用太专业的术语,保持口语化。 首先应该介绍Lombok的好处,比如不用手动写getter和setter,然后是安装步骤,包括IDE插件和项目依赖。接着是常用注解的例子,比如@Data、@Getter、@Setter,还有构造函数的注解,比如@NoArgsConstructor和@AllArgsConstructor。可能需要用代码示例来说明,但要注意用用户能理解的方式。 另外,用户可能想知道Lombok的原理,比如注解处理器在编译时生成代码。不过这部分可能需要简单带过,避免过于深入。还要提醒一些常见的坑,比如需要安装IDE插件,否则代码可能报错,或者与其他框架的兼容性问题。 可能还需要提到Lombok的优缺点,比如减少代码量,但可能隐藏了一些细节,导致调试困难。但用户主要是问怎么使用,所以重点放在使用方法上,其他内容可以简要提及。 检查是否需要分步骤说明,比如安装、引入依赖、常用注解、示例代码等。结构要清晰,用简单的语言,避免冗长。确保每个注解都有对应的例子,比如用User类来演示@Data的效果。 最后,总结一下使用Lombok的好处,以及需要注意的地方,比如团队协作时的统一配置,或者某些情况下可能需要排除Lombok。这样用户能全面了解如何开始使用,同时避免常见的问题。</think>来,咱们用大白话聊聊Lombok怎么用(附代码示例): --- ### 一、Lombok是啥? 就是个帮你**自动写重复代码**的小助手,比如自动生成getter/setter、构造方法等,让你少敲键盘。 --- ### 二、安装步骤 1. **装插件**(必须!): - IDEA:`File > Settings > Plugins` 搜 `Lombok` 安装 - Eclipse:需要单独下载插件 2. **加依赖**(Maven项目): ```xml <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.24</version> <scope>provided</scope> </dependency> ``` --- ### 三、常用功能(附代码对比) #### 1. 自动生成Getter/Setter 👉 `@Data` **传统写法**: ```java public class User { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } // 还要再写5个getter/setter... } ``` **Lombok写法**: ```java @Data // ← 一个注解搞定 public class User { private String name; private int age; } ``` #### 2. 自动构造方法 👉 `@NoArgsConstructor` / `@AllArgsConstructor` ```java @NoArgsConstructor // 无参构造 @AllArgsConstructor // 全参构造 public class User { private String name; private int age; } // 使用示例 User user1 = new User(); User user2 = new User("张三", 18); ``` #### 3. 自动打印对象内容 👉 `@ToString` ```java @ToString(exclude = "password") // 排除password字段 public class User { private String name; private String password; } // 打印时会自动生成:User(name=张三) ``` --- ### 四、其他实用功能 1. **自动判空** 👉 `@NonNull` ```java public void login(@NonNull String username) { // Lombok会自动帮你做空指针检查 } ``` 2. **自动关流** 👉 `@Cleanup` ```java @Cleanup InputStream in = new FileInputStream("test.txt"); // 不用手动写close()了 ``` --- ### 五、注意事项 1. 必须装插件!否则IDE会报错说找不到方法 2. 团队项目要统一环境,避免有人没装插件 3. 过度使用可能让代码可读性变差(比如全用@Data) 4. 部分框架(如JPA)可能需要配合其他注解使用 --- ### 六、适合场景 1. POJO类(各种Bean) 2. 需要频繁修改字段的类 3. 单元测试类(用`@RequiredArgsConstructor`自动注入) 试过之后你会感觉:真香!但别滥用哦~ (๑•̀ㅂ•́)و✧
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值