zoukankan      html  css  js  c++  java
  • lombok基本使用

    lombok基本使用


    Lombok实现原理:

    自从Java 6起,javac就支持“JSR 269 Pluggable Annotation Processing API”规范,只要程序实现了该API,就能在javac运行的时候得到调用。

    Lombok就是一个实现了"JSR 269 API"的程序。在使用javac的过程中,它产生作用的具体流程如下:


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

    2. javac编译过程中调用实现了JSR 269的Lombok程序

    3. 此时Lombok就对第一步骤得到的AST进行处理,找到Lombok注解所在类对应的语法树 (AST),然后修改该语法树(AST),增加Lombok注解定义的相应树节点

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

      @Getter and @Setter
      @FieldNameConstants
      @ToString
      @EqualsAndHashCode
      @AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor
      @Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger
      @Data
      @Builder
      @Singular
      @Delegate
      @Value
      @Accessors
      @Wither
      @SneakyThrows
      

    Lombok注解的使用

    在 IDE 安装了 Lombok 插件后,我们就可以在 pom.xml 文件中添加 Lombok 的依赖进行使用了。

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.10</version>
    </dependency>
    

    注意:pom 依赖设置 scope 为 provided,是为了让 Lombok 库不被打包进程序。

    @Getter/@Setter

    通常我们编写实体类无论多少个字段,都要为其提供 gettersetter 方法,

    我们常会遇到这种情况:某个实体类新增和修改某个字段,我们都需要单独处理调整,十分麻烦并且重复。这时候如果我们使用 Lombok 提供 @Getter/@Setter 注解就能帮我们省去 getter 和 setter 方法的维护,由 Lombok 对 User 类自动生成 gettersetter 方法,两者最终的字节码时一样的,而我们现在在 User.java 上编写的代码仅仅 7 行即可:

    @Getter
    @Setter
    public class User {
        private Integer id;
        private String username;
        private String password;
    }
    

    然后用测试类 UserTests.java 测试结果如下:

    public class UserTests {
        @Test
        public void test() {
            User user = new User();
            user.setUsername("one");
            user.setPassword("zxc123");
            Assert.assertEquals(user.getUsername(), "one");
            Assert.assertEquals(user.getPassword(), "zxc123");
        }
    }
    

    @Getter/@Setter 注解不仅可以使用在类上,还可以使用在字段上,这样就是表示针对该字段自动生成 getter /setter 方法。

    @Getter
    @Setter
    private String password;
    

    这里该注解使用在类上,还是在字段上的区别就是,如果注解使用在类上,只针对这个类的非静态字段有效。

    需要注意的一点是:如果 @Getter 注解修饰了 boolean 类型的变量,其生成的 getter 方法签名是 isXXX 形式,而不是 getXXX形式。

    除此之外,@Getter/@Setter 还提供访问权限控制的属性 lombok.AccessLevel value(), 默认为 PUBLIC,而其他选值都是枚举类型:MODULE, PROTECTED, PACKAGE, PRIVATE

    2.3 @NonNull

    顾名思义,@NonNull 用于标记类中不能允许为 null 的字段或者参数上,任何使用该字段的地方都生成空指针判断代码,若@NonNull 标记的变量为 null,抛出 NullPointException (NPE) 异常。比如下面示例代码:

    public class User {
        private Integer id;
        private String username;
        private String password;
    
        public User(Integer id, @NonNull String username, @NonNull String password) {
            this.id = id;
            this.username = username;
            this.password = password;
        }
    }
    

    使用了 @NonNull 注解之后我们可以获取到反编译之后的字节码信息如下,这就是 Lombok 给我们生成的最终的代码:

    public class User {
        private Integer id;
        private String username;
        private String password;
    
        public User(Integer id, @NonNull String username, @NonNull String password) {
            if (username == null) {
                throw new NullPointerException("username is marked non-null but is null");
            } else if (password == null) {
                throw new NullPointerException("password is marked non-null but is null");
            } else {
                this.id = id;
                this.username = username;
                this.password = password;
            }
        }
    }
    

    2.4 构造器注解

    再来看下平时经常会遇见的场景,为实体类编写构造器方法,Lombok 提供了三个不同构造器注解 @NoArgsConstructor / @AllArgsConstructor / @RequiredArgsConstructor 分别对用不同构造器方法处理方式,接下来就一一描述。

    • @NoArgsConstructor 为实体类生成无参的构造器方法

    • @AllArgsConstructor 为实体类生成除了static修饰的字段之外带有各参数的构造器方法。

    • @RequiredArgsConstructor 为实体类生成指定字段的构造器方法,而这些字段需要被 final,或者 @NonNull 修饰。

      @RequiredArgsConstructor
        public class User3 {
            private Integer id;
            private final String username;
            @NonNull
            private String password;
        }
      

      编译成功后使用构造器方法时就是这样的效果:User3 user3 = new User3("user3", "zxc123");

      2.5 @ToString

      @ToString 会给类自动生成易阅读的 toString 方法,带上有所非静态字段的属性名称和值,这样就十分便于我们日常开发时进行的打印操作。

      @Getter
      @Setter
      @AllArgsConstructor
      @ToString
      public class User2 {
          private Integer id;
          private String username;
          private String password;
      }
      

      最终编译成字节码,反编译结果如下:

    public class User2 {
        private Integer id;
        private String username;
        private String password;
        // 省去 setter/getter
        public String toString() {
            return "User2(id=" + this.getId() + ", username=" + this.getUsername() + ", password=" + this.getPassword() + ")";
        }
    }
    

    另外,注解 @ToString 还支持设置指定哪些字段的日志化输出,哪些不需要出现在 toString 方法中。使用属性 @ToString.Exclude排除不需要在 toString 中出现的字段,使用 @ToString.Include标记需要出现在 toString 中的字段,具体用法可参见示例:

    @Getter
    @Setter
    @AllArgsConstructor
    @ToString
    public class User2 {
        @ToString.Exclude
        private Integer id;
        @ToString.Include
        private String username;
        @ToString.Include
        private String password;
    }
    

    打印 User2 对象的日志效果就是:User2(username=user2, password=zcx123)

    2.6 @EqualsAndHashCode

    @EqualsAndHashCode 注解就是用于根据类所拥有的非静态字段自动重写 equals 方法和 hashCode 方法,方便我们用于对象间的比较。类似 @ToString@EqualsAndHashCode 还可以使用需要作为比较的字段和排除不需要比较的字段,具体用法可以看如下示例:

    @Getter
    @Setter
    @AllArgsConstructor
    @ToString
    @EqualsAndHashCode
    public class User4 {
        @EqualsAndHashCode.Exclude
        private Integer id;
        @EqualsAndHashCode.Include
        private String username;
        @EqualsAndHashCode.Exclude
        private String password;
    }
    

    写完实体类代码,我们编写测试方法试下效果:

    @Test
    public void testEqual() {
        User4 user4 = new User4(1, "user4", "zxc");
        User4 user4_2 = new User4(1, "user4", "123");
        Assert.assertEquals(user4, user4_2); // ture
    }
    

    2.7 @Data/@Value

    @Data/@Value 注解,提供了更综合的生成代码功能,等价于下面几个注解

    @Getter
    @Setter
    @RequiredArgsConstructor
    @ToString
    @EqualsAndHashCode
    

    两个注解都只能使用在类上,与 @Data 不同, @Value 用来修饰不可变的类上。一般实体类没有特别的限制的话,通常可以直接使用 @Data 注解修饰。

    2.8 @Builder

    @Builder 是一个非常强大的注解,提供了一种基于建造者模式的构建对象的 API。使用 @Builder 注解为给我们的实体类自动生成 builder() 方法,并且直接根据字段名称方法进行字段赋值,最后使用 build()方法构建出一个实体对象。

    @Data
    @Builder
    public class User6 {
        private Integer id;
        private String username;
        private String password;
    }
    
    @Test
    public void testBuilder() {
        User6 user6 = User6.builder().id(1).username("user6").password("zxc123").build();
        log.warn("testLog: {}", user6); // User6(id=1, username=user6, password=zxc123)
    }
    

    需要注意的是 @Builder 不支持父类字段的生成,当一个实体类存在父类时,@Builder 只能生成当前类的字段构建方法。若需要用到父类的字段方法时, Lombok 提供了新的注解 @SuperBuilder 来应对这种情况,下面是 @SuperBuilder 注解的使用方式:

    @SuperBuilder
    @Getter
    @Setter
    public class Parent {
       private int id;
       private String name;
    }
    
    @SuperBuilder
    @Data
    public class Child extends Parent {
        private String childName;
    }
    

    调用示例:

    Child child = Child.builder().id(1).name("父类名称").childName("子类名称").build();
    System.out.println(child.getId());
    

    由于 Lombok Plugin 还未更新支持@SuperBuilder,所以以上写法在 IDEA 下还会提示编译错误,无法找到 builder()方法。

    2.9 日志注解

    正对程序类中常见不同框架 Logger 对象,Lombok 也提供了注解,来自动生成 Logger 对象,实现优雅地输出日志,只需要在类上使用日志注解如 @Log。当然 Lombok 支持了多个日志框架,并且提供对应的注解如下:

    @CommonsLog 等价效果: private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);
    
    @Flogger 等价效果: private static final com.google.common.flogger.FluentLogger log = com.google.common.flogger.FluentLogger.forEnclosingClass();
    
    @JBosLog 等价效果: private static final org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(LogExample.class);
    
    @Log 等价效果: private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());
    
    @Log4j 等价效果: private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class);
    
    @Log4j2 等价效果: private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
    
    @Slf4j 等价效果: private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
    
    @XSlf4j 等价效果: private static final org.slf4j.ext.XLogger log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);
    

    下面代码使用 @Slf4j 注解进行日志输出:

    @Slf4j
    public class UserTests {
        // ....
        @Test
        public void testLog() {
            User5 user5 = new User5();
            user5.setId(1);
            user5.setUsername("user5");
            user5.setPassword("zxc123");
            log.warn("testLog: {}", user5);
       // 21:57:15.488 [main] WARN com.one.learn.lombok.UserTests - testLog: User5(id=1, username=user5, password=zxc123)
        }
    }
    

    2.10 @Cleanup

    @Cleanup 用于标记需要释放清理操作的资源对象变量,如 FileInputStream, FileOutputStream 等,标记之后资源对象使用完毕后,就会被自动关闭和清理,实际上这里 Lombok 实现效果与 Java7 特性 try with resource 一样, 为我们屏蔽了关闭资源的模板代码,下面给出 @Cleanup 的使用示例:

    public class CleanupExample {
        public static void main(String[] args) throws IOException {
            @Cleanup InputStream in = new FileInputStream(args[0]);
            @Cleanup OutputStream out = new FileOutputStream(args[1]);
            byte[] b = new byte[10000];
            while (true) {
                int r = in.read(b);
                if (r == -1) {
                    break;
                }
                out.write(b, 0, r);
            }
        }
    }
    

    装载于:https://www.cnblogs.com/blogtech/p/11994217.html

  • 相关阅读:
    ACM学习历程——POJ 1700 Crossing River(贪心)
    ACM学习历程——POJ 2376 Cleaning Shifts(贪心)
    ACM学习历程——HDU1331 Function Run Fun(锻炼多维dp的打表)
    hadoop-mapreduce-架构概念
    Java-笔记12
    Java-笔记11-复习
    机器学习概述
    hadoop-hdfs-API
    hadoop-
    Java-Project-02
  • 原文地址:https://www.cnblogs.com/jinronga/p/12753842.html
Copyright © 2011-2022 走看看