• Java 开发工具–Lombok介绍


    Java 开发工具–Lombok介绍

    概述

    在写POJO代码时,经常需要生成getter和setter方法,以及hashcode、tostring和equals等代码。感觉甚是麻烦,虽然有Eclipse或者intellij IDEA快捷键能够自动生成些代码;代码里面充斥着这种没有意义的片段,看着都觉得烦;另一方面,单元测试时,若是考察行覆盖率,这种POJO代码势必会增加分母的大小,降低代码覆盖率。
    偶然中,得知Lombok Project,通过使用注解,可以消除冗余的java代码。
    官网介绍:

    Project Lombok makes java a spicier language by adding ‘handlers’ that know how to build and compile simple, boilerplate-free, not-quite-java code.

    如何使用

    引入依赖即可:

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <scope>provided</scope>
    </dependency>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    Gradle项目的引入方式类似:

    provided group: 'org.projectlombok', name: 'lombok', version: '1.16.18'
    
    
    • 1

    在spring boot项目中,引入Lombok时没有必要指定<version>和<scope>,因为 spring boot已经自动配置好。

    如何安装

    先说IDEA,在IDEA中安装Lombok需要先安装插件,快捷键 Ctrl + Alt + S,然后选择 plugins——> browse repositories,搜索 Lombok,install即可,安装需要重启。
    其次,Lombok是在编译时期,自动生成所需方法的,所以需要开启annotation processing;依旧是刚才的快捷键,可以搜索"annotation processing",然后选择"Enable annotation processing".
    如果是Eclipse的话,官网下载最新的Lombok.jar,双击或者
    java -jar lombok.jar在弹出的窗口,确认刚才 Lombok 扫描整个硬盘得到的Eclipse安装程序结果即可(Eclipse,MyEclipse,STS(Spring tool suite)),点击 Install/Update。

    注解

    不完全统计:

    @NonNull:标识对象是否为空,为空则抛出异常
    @Getter/@Setter:自动生成Getter/Setter方法
    @ToString:覆盖tostring方法,可以添加限制条件
    @EqualsAndHashCode:覆盖equal和hashCode方法,生成方法时只会使用类中的非静态和非transient成员变量
    @Data:@Getter/@Setter, @ToString, @EqualAndHashCode,@NoArgsConstructor等组合
    @Cleanup:用在变量前面,可以保证此变量代表的资源会被自动关闭,默认是调用资源的close()方法,如果该资源有其它关闭方法,可使用@Cleanup(“methodName”)来指定要调用的方法
    @NoArgsConstructor/@RequiredArgsConstructor /@AllArgsConstructor:第二个注解使用类中所有带有@NonNull注解的或带有final修饰的非静态成员变量生成对应的构造方法,
    @Log:类注解,可以省去从日志工厂生成日志对象这一步,直接进行日志记录,具体注解根据日志工具的不同而不同,同时,可以在注解中使用topic来指定生成log对象时的类名
    @Builder:关于build模式,可以参考《Effective  java》这本书
    @Value
    @SneakyThrows
    @Synchronized
    @Accessors(chain = true),在生成 setter 方法时不返回 void,而是返回 this,当前类;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    实例代码

    @Getter & @Setter & @ToString

    public class Person {
        private boolean employed;
        private String name;
    
        Person() {
        }
    
        public String say() {
            return this.getName();
        }
    
        public String toString() {
            return "Person(employed=" + this.isEmployed() + ", name=" + this.getName() + ")";
        }
    
        public boolean isEmployed() {
            return this.employed;
        }
    
        public void setEmployed(boolean employed) {
            this.employed = employed;
        }
    
        public String getName() {
            return this.name;
        }
    
        protected void setName(String name) {
            this.name = name;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    如果使用Lombok提供的注解:

    @ToString
     class Person {
        @Getter
        @Setter
        private boolean employed;
        @Getter
        @Setter(AccessLevel.PROTECTED)
        private String name;
    
        public String say() {
            return getName();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    @Builder

    这次先看看使用注解之前的java code:

    @Builder
    public class BuilderExample {
        private String name;
        private int age;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    再看看其经过编译之后的java code:

    import java.beans.ConstructorProperties;
    
    public class BuilderExample {
        private String name;
        private int age;
    
        @ConstructorProperties({"name", "age"})
        BuilderExample(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public static BuilderExample.BuilderExampleBuilder builder() {
            return new BuilderExample.BuilderExampleBuilder();
        }
    
        public static class BuilderExampleBuilder {
            private String name;
            private int age;
    
            BuilderExampleBuilder() {
            }
    
            public BuilderExample.BuilderExampleBuilder name(String name) {
                this.name = name;
                return this;
            }
    
            public BuilderExample.BuilderExampleBuilder age(int age) {
                this.age = age;
                return this;
            }
    
            public BuilderExample build() {
                return new BuilderExample(this.name, this.age);
            }
    
            public String toString() {
                return "BuilderExample.BuilderExampleBuilder(name=" + this.name + ", age=" + this.age + ")";
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    @Data

    先看使用注解的类:

    @Data
    public class User {
        private Integer id;
        private String password;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    编译之后的类长这样:

    public class User {
        private Integer id;
        private String password;
    
        public User() {
        }
    
        public Integer getId() {
            return this.id;
        }
    
        public String getPassword() {
            return this.password;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        public boolean equals(Object o) {
            if (o == this) {
                return true;
            } else if (!(o instanceof User)) {
                return false;
            } else {
                User other = (User)o;
                if (!other.canEqual(this)) {
                    return false;
                } else {
                    Object this$id = this.getId();
                    Object other$id = other.getId();
                    if (this$id == null) {
                        if (other$id != null) {
                            return false;
                        }
                    } else if (!this$id.equals(other$id)) {
                        return false;
                    }
    
                    Object this$password = this.getPassword();
                    Object other$password = other.getPassword();
                    if (this$password == null) {
                        if (other$password != null) {
                            return false;
                        }
                    } else if (!this$password.equals(other$password)) {
                        return false;
                    }
    
                    return true;
                }
            }
        }
    
        protected boolean canEqual(Object other) {
            return other instanceof User;
        }
    
        public int hashCode() {
            int PRIME = true;
            int result = 1;
            Object $id = this.getId();
            int result = result * 59 + ($id == null ? 43 : $id.hashCode());
            Object $password = this.getPassword();
            result = result * 59 + ($password == null ? 43 : $password.hashCode());
            return result;
        }
    
        public String toString() {
            return "User(id=" + this.getId() + ", password=" + this.getPassword() + ")";
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76

    更多注解的用法,请参考官网

    lombok.config

    config官方文档
    查看当前版本的lombok所支持的配置项使用命令工具config

    java -jar lombok.jar config -g --verbose
    cofing工具查看某个配置文件所有的配置项
    java -jar lombok.jar config lombok.config

    示例:

    # Tell the configuration system it should stop looking for other configuration files (default: false).
    # 指明lombok的根目录为当前配置文件所在目录
    # 配置文件是分层的,原则是接近源文件的配置设置优先。在根目录的子目录中可以创建lombok.config配置文件,来覆盖根目录的配置文件(只覆盖配置项相同的配置,其他继承根目录的配置,若没有显式配置则使用默认值)
    config.stopBubbling = true
    # Generate setters that return 'this' instead of 'void' (default: false).
    lombok.accessors.chain = true
    # 使用不带前缀的简单方式生成setter和getter,即使用原始的字段名称省略get和set前缀
    lombok.accessors.fluent=true
    # 跳过添加一个@java.bean.ConstructorProperties生成的构造器
    lombok.anyConstructor.suppressConstructorProperties=true
    # Don't call the getters but use the fields directly in the generated toString method (default = false).
    lombok.toString.doNotUseGetters = true
    # When generating toString for classes that extend something (other than Object), either automatically take into account superclass implementation (call), or don't (skip), or warn and don't (warn). (default = warn).
    lombok.toString.callSuper = CALL
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    要求:lombok 1.14+

    原理

    采用JSR269所提出的插入式注解处理(Pluggable Annotation Processing),并结合动态代码生成技术所开发的。如下图所示:
    这里写图片描述
    图示为 javac 的编译过程,java文件首先通过进行解析构建出一个AST,然后执行注解处理,最后经过分析优化生成二进制的.class文件。
    Annotation Processing 是在解析和生成之间的一个步骤。
    这里写图片描述
    上图是 Lombok 处理流程,在Javac 解析成抽象语法树之后(AST),Lombok 根据自己的注解处理器,动态的修改 AST,增加新的节点(所谓代码),最终通过分析和生成字节码。

    其他注解

    Wither

    在这里插入图片描述

    val

    无忧的final本地变量

    NonNull

    Cleanup

    自动的资源管理:安全调用你的close() 方法,无需任何麻烦。

    Lombok缺点

    这么好的工具,那可以应用到生产环境吗?真不一定,Lombok 注解的引入一定程度上会造成源码的可读性(不是所有人都了解其使用)和完整性,而且生成环境使用,不一定还会遇到什么坑,看看其代码托管GitHub的issue就知道。
    不过反过来一想,issue 开得多,不正说明其应用还是很广泛的?

    参考

    基本实现原理
    jdk-compilation-overview
    lombok.config-lombok配置系统

  • 相关阅读:
    laravel 使用构造器进行增删改查
    explan各项说明
    data函数参数
    php引用
    PHP开发api接口安全验证方法一
    redis主从配置
    php 实现同一个账号同时只能一个人登录
    MySQL慢查询1- 开启慢查询
    前端基础 & 初识HTML
    HTTP协议概述
  • 原文地址:https://www.cnblogs.com/ios9/p/14040045.html
Copyright © 2020-2023  润新知