Lombok 让代码“亚健康”?你真的用对了吗?

2022-04-07 14:28:07 浏览数 (1)

大家好,我是一航!

周末的时候,和朋友闲聊,说最近在加班,赶进度,搞项目重构;然后跟我在吐槽,团队不让用Lombok,还列了一堆的缺点说让代码变的"亚健康",本来进度就挺赶的,因为一些基础的代码,消磨着精力和时间,心挺累的...

我个人呢,是挺喜欢lombok的,也一直推荐身边的朋友去使用;虽然很多人反对lombok的朋友细数了他的各种罪状,但是仍然没有办法说服我放弃使用lombok;至少,目前在我的团队,是适用的;

在讨论Lombok的各种问题之前,还是先还是来说说什么是Lombok以及日常用法,防止有朋友没有使用过;或者并没有使用到全部的功能,导致不知道在讲什么。

那么一起先来体验一下他的优势吧。

Lombok

什么是Lombok?

Lombok 是一种 Java 实用工具,可用来帮助开发人员消除 Java 的冗长,尤其是对于简单的 Java 对象(POJO)。它通过注释实现这一目的。通过在开发环境中实现 Lombok,开发人员可以节省构建诸如getter() 、setter() 、 hashCode() 和 equals() 这样的方法以及以往用来分类各种 accessor 和 mutator 的大量时间。

基础使用

安装插件(必装

导入依赖

代码语言:javascript复制
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.22</version>
    <optional>true</optional>
</dependency>

使用lombok代码之前对象

代码语言:javascript复制
public class User {
    private Long id;

    private String name;

    private Integer age;

    private String addr;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getAddr() {
        return addr;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return id.equals(user.id) &&
                name.equals(user.name) &&
                age.equals(user.age) &&
                addr.equals(user.addr);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name, age, addr);
    }

    @Override
    public String toString() {
        return "User{"  
                "id="   id  
                ", name='"   name   '''  
                ", age="   age  
                ", addr='"   addr   '''  
                '}';
    }
}

使用Lombok

通过4个注解就能完成上面那些冗长的方法;

代码语言:javascript复制
@Getter
@Setter
@ToString
@EqualsAndHashCode
public class User {
    private Long id;

    private String name;

    private Integer age;

    private String addr;
}

同样,上面冗长的注解,也可以通过一个@Data注解来代替:一下子是不是变的简洁多了...

代码语言:javascript复制
@Data
public class User {
    private Long id;

    private String name;

    private Integer age;

    private String addr;
}

注解说明

@Setter

自动生成属性的set方法

  • AccessLevel.NONE 表示不生成set方法,如果在类上,就是所有变量都没有set方法,如果在变量上,就指明单个变量没有set方法 @Setter(AccessLevel.NONE) @Getter(AccessLevel.NONE) private Integer age;

@Getter

自动生成属性的get方法

@EqualsAndHashCode

自动生成equals()、canEquals()和hashCode()方法

@ToString

自动生成toString方法

@NonNull

指明对象不允许为空,会自动在构造方法,成员方法上面加上非空的校验

@Builder

自动生成一个对象的构造器

代码语言:javascript复制
public static void main(String[] args) {
    UserBuilder userBuilder = new UserBuilder();
    User user = userBuilder.id(1L)
            .name("张三")
            .age(10)
            .addr("北京")
            .build();
}

注:加上@Builder注解之后,虽然能沟通构造器去构建对象,但是大部分框架都是采用的get/set进行取值/赋值;所以在使用建议同时加上@Getter/@Setter注解

@NoArgsConstructor

自动生成无参构造方法

  • @NoArgsConstructor(staticName = "getUser") 自动生成一个返回对象的静态方法,方法名称为staticName变量指定的名称 public static User getUser() { return new User(); }

@AllArgsConstructor

自动生成包含所有属性的构造方法

@RequiredArgsConstructor

自动生成包含必要参数的构造方法,也就是被final@NonNull修饰的变量

@Data

这个是使用频率非常高的一个注解,日常开发中的DTO对象,Entity对象普遍都会加上这个注解

这个是使用频率非常高的一个注解,@Data = @Setter @Getter @ToString @EqualsAndHashCode @RequiredArgsConstructor

@Value

@Value = @Getter @ToString @EqualsAndHashCode @RequiredArgsConstructor

需要注意的是,这个注解生成的代码是不包含set方法,是因为他会所有变量都使用final修饰,然后通过构造方法对所有变量进行赋值。

@Log

自动生成一个log静态常量,常用的注解之一@Slf4j

代码语言:javascript复制
private static final Logger log = LoggerFactory.getLogger(User.class);
  • 更多日志注解 不同的日志框架对应的不同的注解 @CommonsLog private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(User.class); @JBossLog private static final org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(User.class); @Log private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(User.class.getName()); @Log4j private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(User.class); @Log4j2 private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(User.class); @Slf4j private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(User.class); @XSlf4j private static final org.slf4j.ext.XLogger log = org.slf4j.ext.XLoggerFactory.getXLogger(User.class);

@Cleanup

自动生成释放资源的代码,默认是调用资源的close()方法,也可以自己指定释放的方法,如:@Cleanup("shutDown")

@Accessors

Accessor的中文含义是存取器,@Accessors用于配置getter和setter方法的生成结果

  • @Accessors(fluent = true) fluent表示流畅的;默认为false,设置为true时,getter和setter方法全部和属性名同名,并且setter方法返回当前对象
  • @Accessors(prefix = {"pre","my"}) 去掉指定前缀;遵循驼峰命名的变量,通过prefix指定前缀的集合之后,setter方法将会自动去掉对应的前缀,如下图,preName变量的set方法为setName();myAddr的set方法为setAddr()

@Synchronized

synchronized关键词的用法差不多,加上@Synchronized注解之后,会自动对方法类的内容使用synchronized关键词包裹

注:个人不太建议采用这个注解进行加锁,最主要的原因是粒度太粗了;所以需要酌情考虑是否真的合适!

所有的注解加起来,是不是能为我们日常开发省去很多的体力活?答案是肯定的,这也是他吸引了这么多人使用的最根本原因;

主要的问题

上面的示例以及涵盖了Lombok的绝大部分的使用场景;确实为日常开发带来了很多便捷,但这些优点的背后同样也衍生出了一些不得不说的问题:

强耦合、被迫使用

文章一开始就有说到,使用lombok必须安装一个插件,才能正常编译通过,如果是团队协作开发,某一天你加入了Lombok,其他人并没有使用,那么他在同步你的代码之后,需要被迫使用Lombok插件,否则编译无法通过;无形中增加了代码、工具的耦合度

如果你要做开发项目,我的建议是,老老实实把这个插件给卸载了吧,因为这可能直接影响到你开源项目的使用率;特别是插件类的开源项目。

阅读性差

源码的主要目的是为了:阅读;使用Lombok之后,相关的方法都是在编译器自动生成的,在源码上并没有体现出来,从而导致无法直观的看到具体的实现逻辑,增加了理解成本;

当对代码进行debug的时候,自动生成的代码无法进行逐行调试,增加了对问题的排查成本;

误用

举几个简单的例子

  • 几个构造方法的注解,不同的注解有着不同的效果,不能一味的使用@AllArgsConstructor生成一个大而全的构造方法讲所有属性都对外暴露了,需要在不同的场景,使用不同的注解;
  • @Synchronized注解,如果方法中只有一小段代码需要加上锁,使用@Synchronized直接是将整个方法都加上了锁,从而导致粒度变粗,性能变差;
  • 比如对象只需要get方法,但是在使用过程中把@Data注解给安排上了,虽然get方法都有了,但同时也增加了所有的set方法也都给安排上了,无效中增加了代码的安全性问题。

这些使用,虽然在最终的结果上面,并没有带来什么大的影响,但是无形中产生了一些隐患,一旦隐患堆积多了之后,就会产生一种插件不好用的错觉。

技术债

如果是个人使用,Lombok的学习成本并不高,就那么十来个注解,可能花一会儿功夫就了解清楚了;但当团队决定使用Lombok的时候,就需要要求每一个成员都能够熟练的掌握,那最终带来的成本就是几何倍了;

总结

本文不仅列举出了Lombok详细的用法,还列举了其优缺点,同样这也正是赞成派和反对派各自所持不同的立场;凡事都具备两面性,不能完全从一个角度去钻牛角尖,那样本身就失去了技术创新的意义了;

优点就不过分的吹捧,缺点不过分的贬低;最终的使用还得看是否真的合适;

至于不足的那些方方面面,也并不是完全无计可施;使用框架的最终目的是提高生产力被迫使用技术债这些问题,团队需要在一开始的时候就讨论清楚,并结合团队间的影响、学习成本等方面来权衡利弊,如果利大于弊,那就可以安排上,通过系统的学习、培训之后,自然也就不会出现误用这些问题了;

至于阅读性差;Lombok所生成的方法,几乎都是通用的,标准的那一批;作用、含义基本都很容易达成共识的,所以只要详细的理解了各注解的作用,在代码的理解上,也就不会成为问题;

那么最后,一起来投个票吧!

0 人点赞