快捷搜索:

lombok用法

一、lombok介绍

官方定义:

Project Lombok is a java library that automatically plugs into your editor and build 
tools, spicing up your java.Never write another getter or equals method again, 
with one annotation your class has a fully featured builder, Automate your logging 
variables, and much more.

Lombok是一个Java库,能自动插入编辑器并构建工具,简化Java开发。通过添加注解的方式,不需要为类编写getter或eques方法,同时可以自动化日志变量

lombok是用java语言开发的一个插件,通过自定义注解来达到简化java代码的一个类库

Lombok 能够在编译源代码期间自动帮我们生成这些方法(get/set/equals等),但并不会像反射那样降低程序的性能。

二、lombok的使用

1.在pom.xml中加入依赖

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.4</version>
    <scope>provided</scope>
</dependency>

2.安装lombok插件

2.1 IDEA安装方式

File=>settings

安装好后重启,然后点击项目maven update,不行的话多执行几次

2.2 eclipse安装方式

下载lombok.jar包放到eclipse安装目录,再eclipse.ini配置后面加入以下内容:

-javaagent:lombok.jar
-vmargs -javaagent:lombok.jar

安装好后重启,然后点击项目maven update,不行的话多执行几次

三、lombok常用的注解

1.1 @Getter and @Setter

@Getter 或 @Setter 注解作用在类或字段上,Lombok 会自动生成默认的 getter/setter 方法

    @Getter 注解
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface Getter {
  // 若getter方法非public的话,可以设置可访问级别
    lombok.AccessLevel value() default lombok.AccessLevel.PUBLIC;
    AnyAnnotation[] onMethod() default {};
  // 是否启用延迟初始化
    boolean lazy() default false;
}
    @Setter
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface Setter {
  // 若setter方法非public的话,可以设置可访问级别
    lombok.AccessLevel value() default lombok.AccessLevel.PUBLIC;
    AnyAnnotation[] onMethod() default {};
    AnyAnnotation[] onParam() default {};
}

使用示例

@Getter
@Setter
public class GetterAndSetterDemo {
    String firstName;
    String lastName;
    LocalDate dateOfBirth;
}

以上代码经过 Lombok 编译后,会生成如下代码:

public class GetterAndSetterDemo {
    String firstName;
    String lastName;
    LocalDate dateOfBirth;
    public GetterAndSetterDemo() {
    }
    // 省略其它setter和getter方法
    public String getFirstName() {
        return this.firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
}

1.2 @NoArgsConstructor

@NoArgsConstructor 注解可以为指定类,生成默认的构造函数

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface NoArgsConstructor {
  // 若设置该属性,将会生成一个私有的构造函数且生成一个staticName指定的静态方法
    String staticName() default "";
    AnyAnnotation[] onConstructor() default {};
  // 设置生成构造函数的访问级别,默认是public
    AccessLevel access() default lombok.AccessLevel.PUBLIC;
  // 若设置为true,则初始化所有final的字段为0/null/false
    boolean force() default false;
}

使用示例

@NoArgsConstructor(staticName = "getInstance")
public class NoArgsConstructorDemo {
    private long id;
    private String name;
    private int age;
}

以上代码经过 Lombok 编译后,会生成如下代码:

public class NoArgsConstructorDemo {
    private long id;
    private String name;
    private int age;
    private NoArgsConstructorDemo() {
    }
    public static NoArgsConstructorDemo getInstance() {
        return new NoArgsConstructorDemo();
    }
}

1.3 @AllArgsConstructor

使用 @AllArgsConstructor 注解可以为指定类,生成包含所有成员的构造函数,@AllArgsConstructor 注解的定义如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface AllArgsConstructor {
  // 若设置该属性,将会生成一个私有的构造函数且生成一个staticName指定的静态方法
    String staticName() default "";
    AnyAnnotation[] onConstructor() default {};
  // 设置生成构造函数的访问级别,默认是public
    AccessLevel access() default lombok.AccessLevel.PUBLIC;
}

使用示例

@AllArgsConstructor
public class AllArgsConstructorDemo {
    private long id;
    private String name;
    private int age;
}

以上代码经过 Lombok 编译后,会生成如下代码:

public class AllArgsConstructorDemo {
    private long id;
    private String name;
    private int age;
    public AllArgsConstructorDemo(long id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
}

1.4 @RequiredArgsConstructor

使用 @RequiredArgsConstructor 注解可以为指定类必需初始化的成员变量,如 final 成员变量,生成对应的构造函数,@RequiredArgsConstructor 注解的定义如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface RequiredArgsConstructor {
  // 若设置该属性,将会生成一个私有的构造函数且生成一个staticName指定的静态方法
    String staticName() default "";
    AnyAnnotation[] onConstructor() default {};
  // 设置生成构造函数的访问级别,默认是public
    AccessLevel access() default lombok.AccessLevel.PUBLIC;
}

使用示例

@RequiredArgsConstructor
public class RequiredArgsConstructorDemo {
    private final long id;
    private String name;
    private int age;
}

以上代码经过 Lombok 编译后,会生成如下代码:

public class RequiredArgsConstructorDemo {
    private final long id;
    private String name;
    private int age;
    public RequiredArgsConstructorDemo(long id) {
        this.id = id;
    }
}

1.5 @EqualsAndHashCode

使用 @EqualsAndHashCode 注解可以为指定类生成 equals 和 hashCode 方法, @EqualsAndHashCode 注解的定义如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface EqualsAndHashCode {
  // 指定在生成的equals和hashCode方法中需要排除的字段列表
    String[] exclude() default {};
 
  // 显式列出用于identity的字段,一般情况下non-static,non-transient字段会被用于identity
    String[] of() default {};
 
  // 标识在执行字段计算前,是否调用父类的equals和hashCode方法
    boolean callSuper() default false;
 
    boolean doNotUseGetters() default false;
 
    AnyAnnotation[] onParam() default {};
 
    @Deprecated
    @Retention(RetentionPolicy.SOURCE)
    @Target({})
    @interface AnyAnnotation {}
 
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.SOURCE)
    public @interface Exclude {}
 
    @Target({ElementType.FIELD, ElementType.METHOD})
    @Retention(RetentionPolicy.SOURCE)
    public @interface Include {
        String replaces() default "";
    }
}

使用示例

@EqualsAndHashCode
public class EqualsAndHashCodeDemo {
    String firstName;
    String lastName;
    LocalDate dateOfBirth;
}

以上代码经过 Lombok 编译后,会生成如下代码:

public class EqualsAndHashCodeDemo {
    String firstName;
    String lastName;
    LocalDate dateOfBirth;
    public EqualsAndHashCodeDemo() {
    }
    public boolean equals(Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof EqualsAndHashCodeDemo)) {
            return false;
        } else {
            EqualsAndHashCodeDemo other = (EqualsAndHashCodeDemo)o;
            if (!other.canEqual(this)) {
                return false;
            } else {
              // 已省略大量代码
        }
    }
    public int hashCode() {
        int PRIME = true;
        int result = 1;
        Object $firstName = this.firstName;
        int result = result * 59 + ($firstName == null ? 43 : $firstName.hashCode());
        Object $lastName = this.lastName;
        result = result * 59 + ($lastName == null ? 43 : $lastName.hashCode());
        Object $dateOfBirth = this.dateOfBirth;
        result = result * 59 + ($dateOfBirth == null ? 43 : $dateOfBirth.hashCode());
        return result;
    }
}

1.6 @ToString

使用 @ToString 注解可以为指定类生成 toString 方法, @ToString 注解的定义如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface ToString {
  // 打印输出时是否包含字段的名称
    boolean includeFieldNames() default true;
 
  // 列出打印输出时,需要排除的字段列表
    String[] exclude() default {};
 
  // 显式的列出需要打印输出的字段列表
    String[] of() default {};
 
  // 打印输出的结果中是否包含父类的toString方法的返回结果
    boolean callSuper() default false;
 
    boolean doNotUseGetters() default false;
 
    boolean onlyExplicitlyIncluded() default false;
 
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.SOURCE)
    public @interface Exclude {}
 
    @Target({ElementType.FIELD, ElementType.METHOD})
    @Retention(RetentionPolicy.SOURCE)
    public @interface Include {
        int rank() default 0;
        String name() default "";
    }
}

使用示例

@ToString(exclude = {"dateOfBirth"})
public class ToStringDemo {
    String firstName;
    String lastName;
    LocalDate dateOfBirth;
}

以上代码经过 Lombok 编译后,会生成如下代码:

public class ToStringDemo {
    String firstName;
    String lastName;
    LocalDate dateOfBirth;
    public ToStringDemo() {
    }
    public String toString() {
        return "ToStringDemo(firstName=" + this.firstName + ", lastName=" +
          this.lastName + ")";
    }
}

1.7 @Data

@Data 注解与同时使用以下的注解的效果是一样的:

  1. @ToString
  2. @Getter
  3. @Setter
  4. @RequiredArgsConstructor
  5. @EqualsAndHashCode

@Data 注解的定义如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface Data {
    String staticConstructor() default "";
}

使用示例

@Data
public class DataDemo {
    private Long id;
    private String summary;
    private String description;
}

以上代码经过 Lombok 编译后,会生成如下代码:

public class DataDemo {
    private Long id;
    private String summary;
    private String description;
    public DataDemo() {
    }
    // 省略summary和description成员属性的setter和getter方法
    public Long getId() {
        return this.id;
    }
 
    public void setId(Long id) {
        this.id = id;
    }
    public boolean equals(Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof DataDemo)) {
            return false;
        } else {
            DataDemo other = (DataDemo)o;
            if (!other.canEqual(this)) {
                return false;
            } else {
               // 已省略大量代码
            }
        }
    }
    protected boolean canEqual(Object other) {
        return other instanceof DataDemo;
    }
    public int hashCode() {
        int PRIME = true;
        int result = 1;
        Object $id = this.getId();
        int result = result * 59 + ($id == null ? 43 : $id.hashCode());
        Object $summary = this.getSummary();
        result = result * 59 + ($summary == null ? 43 : $summary.hashCode());
        Object $description = this.getDescription();
        result = result * 59 + ($description == null ? 43 : $description.hashCode());
        return result;
    }
    public String toString() {
        return "DataDemo(id=" + this.getId() + ", summary=" + this.getSummary() + ", description=" + this.getDescription() + ")";
    }
}

1.8 @Log

若你将 @Log 的变体放在类上(适用于你所使用的日志记录系统的任何一种);之后,你将拥有一个静态的 final log 字段,然后你就可以使用该字段来输出日志。

1.9 @Synchronized

@Synchronized 是同步方法修饰符的更安全的变体。与 synchronized 一样,该注解只能应用在静态和实例方法上。它的操作类似于 synchronized 关键字,但是它锁定在不同的对象上。synchronized 关键字应用在实例方法时,锁定的是 this 对象,而应用在静态方法上锁定的是类对象。@Synchronized 注解的定义如下:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Synchronized {
  // 指定锁定的字段名称
    String value() default "";
}

使用示例:

public class SynchronizedDemo {
    private final Object readLock = new Object();
    @Synchronized
    public static void hello() {
        System.out.println("world");
    }
    @Synchronized
    public int answerToLife() {
        return 42;
    }
    @Synchronized("readLock")
    public void foo() {
        System.out.println("bar");
    }
}

以上代码经过 Lombok 编译后,会生成如下代码:

public class SynchronizedDemo {
    private static final Object $LOCK = new Object[0];
    private final Object $lock = new Object[0];
    private final Object readLock = new Object();
    public SynchronizedDemo() {
    }
    public static void hello() {
        synchronized($LOCK) {
            System.out.println("world");
        }
    }
    public int answerToLife() {
        synchronized(this.$lock) {
            return 42;
        }
    }
    public void foo() {
        synchronized(this.readLock) {
            System.out.println("bar");
        }
    }
}

1.10 @Builder

使用 @Builder 注解可以为指定类实现建造者模式,该注解可以放在类、构造函数或方法上。@Builder 注解的定义如下:

@Target({TYPE, METHOD, CONSTRUCTOR})
@Retention(SOURCE)
public @interface Builder {
    @Target(FIELD)
    @Retention(SOURCE)
    public @interface Default {}
  // 创建新的builder实例的方法名称
    String builderMethodName() default "builder";
    // 创建Builder注解类对应实例的方法名称
    String buildMethodName() default "build";
    // builder类的名称
    String builderClassName() default "";
 
    boolean toBuilder() default false;
 
    AccessLevel access() default lombok.AccessLevel.PUBLIC;
 
    @Target({FIELD, PARAMETER})
    @Retention(SOURCE)
    public @interface ObtainVia {
        String field() default "";
        String method() default "";
        boolean isStatic() default false;
    }
}

使用示例

@Builder
public class BuilderDemo {
    private final String firstname;
    private final String lastname;
    private final String email;
}

以上代码经过 Lombok 编译后,会生成如下代码:

public class BuilderDemo {
    private final String firstname;
    private final String lastname;
    private final String email;
    BuilderDemo(String firstname, String lastname, String email) {
        this.firstname = firstname;
        this.lastname = lastname;
        this.email = email;
    }
    public static BuilderDemo.BuilderDemoBuilder builder() {
        return new BuilderDemo.BuilderDemoBuilder();
    }
    public static class BuilderDemoBuilder {
        private String firstname;
        private String lastname;
        private String email;
        BuilderDemoBuilder() {
        }
        public BuilderDemo.BuilderDemoBuilder firstname(String firstname) {
            this.firstname = firstname;
            return this;
        }
        public BuilderDemo.BuilderDemoBuilder lastname(String lastname) {
            this.lastname = lastname;
            return this;
        }
        public BuilderDemo.BuilderDemoBuilder email(String email) {
            this.email = email;
            return this;
        }
        public BuilderDemo build() {
            return new BuilderDemo(this.firstname, this.lastname, this.email);
        }
        public String toString() {
            return "BuilderDemo.BuilderDemoBuilder(firstname=" + this.firstname + ", lastname=" + this.lastname + ", email=" + this.email + ")";
        }
    }
}

1.11 @SneakyThrows

@SneakyThrows 注解用于自动抛出已检查的异常,而无需在方法中使用 throw 语句显式抛出。@SneakyThrows 注解的定义如下:

@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.SOURCE)
public @interface SneakyThrows {
    // 设置你希望向上抛的异常类
    Class<? extends Throwable>[] value() default java.lang.Throwable.class;
}

使用示例

public class SneakyThrowsDemo {
    @SneakyThrows
    @Override
    protected Object clone() {
        return super.clone();
    }
}

以上代码经过 Lombok 编译后,会生成如下代码:

public class SneakyThrowsDemo {
    public SneakyThrowsDemo() {
    }
    protected Object clone() {
        try {
            return super.clone();
        } catch (Throwable var2) {
            throw var2;
        }
    }
}

1.12 @NonNull

你可以在方法或构造函数的参数上使用 @NonNull 注解,它将会为你自动生成非空校验语句。@NonNull 注解的定义如下:

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE, ElementType.TYPE_USE})
@Retention(RetentionPolicy.CLASS)
@Documented
public @interface NonNull {
}

使用示例

public class NonNullDemo {
    @Getter
    @Setter
    @NonNull
    private String name;
}

以上代码经过 Lombok 编译后,会生成如下代码:

public class NonNullDemo {
    @NonNull
    private String name;
    public NonNullDemo() {
    }
    @NonNull
    public String getName() {
        return this.name;
    }
    public void setName(@NonNull String name) {
        if (name == null) {
            throw new NullPointerException("name is marked non-null but is null");
        } else {
            this.name = name;
        }
    }
}

1.13 @Clean

@Clean 注解用于自动管理资源,用在局部变量之前,在当前变量范围内即将执行完毕退出之前会自动清理资源,自动生成 try-finally 这样的代码来关闭流。

@Target(ElementType.LOCAL_VARIABLE)
@Retention(RetentionPolicy.SOURCE)
public @interface Cleanup {
  // 设置用于执行资源清理/回收的方法名称,对应方法不能包含任何参数,默认名称为close。
    String value() default "close";
}

使用示例:

public class CleanupDemo {
    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);
        }
    }
}

以上代码经过 Lombok 编译后,会生成如下代码:

public class CleanupDemo {
    public CleanupDemo() {
    }
    public static void main(String[] args) throws IOException {
        FileInputStream in = new FileInputStream(args[0]);
        try {
            FileOutputStream out = new FileOutputStream(args[1]);
            try {
                byte[] b = new byte[10000];
                while(true) {
                    int r = in.read(b);
                    if (r == -1) {
                        return;
                    }
                    out.write(b, 0, r);
                }
            } finally {
                if (Collections.singletonList(out).get(0) != null) {
                    out.close();
                }
            }
        } finally {
            if (Collections.singletonList(in).get(0) != null) {
                in.close();
            }
        }
    }
}

1.14 @With

在类的字段上应用 @With 注解之后,将会自动生成一个 withFieldName(newValue) 的方法,该方法会基于 newValue 调用相应构造函数,创建一个当前类对应的实例。@With 注解的定义如下:

@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface With {
    AccessLevel value() default AccessLevel.PUBLIC;
    With.AnyAnnotation[] onMethod() default {};
    With.AnyAnnotation[] onParam() default {};
    @Deprecated
    @Retention(RetentionPolicy.SOURCE)
    @Target({})
    public @interface AnyAnnotation {
    }
}

使用示例

public class WithDemo {
    @With(AccessLevel.PROTECTED)
    @NonNull
    private final String name;
    @With
    private final int age;
    public WithDemo(String name, int age) {
        if (name == null) throw new NullPointerException();
        this.name = name;
        this.age = age;
    }
}

以上代码经过 Lombok 编译后,会生成如下代码:

public class WithDemo {
    @NonNull
    private final String name;
    private final int age;
    public WithDemo(String name, int age) {
        if (name == null) {
            throw new NullPointerException();
        } else {
            this.name = name;
            this.age = age;
        }
    }
    protected WithDemo withName(@NonNull String name) {
        if (name == null) {
            throw new NullPointerException("name is marked non-null but is null");
        } else {
            return this.name == name ? this : new WithDemo(name, this.age);
        }
    }
    public WithDemo withAge(int age) {
        return this.age == age ? this : new WithDemo(this.name, age);
    }
}

四、lombok工作原理

核心之处就是对于注解的解析上。JDK5引入了注解的同时,也提供了两种解析方式。

    运行时解析

运行时能够解析的注解,必须将@Retention设置为RUNTIME,这样就可以通过反射拿到该注解。java.lang.reflect反射包中提供了一个接口AnnotatedElement,该接口定义了获取注解信息的几个方法,Class、Constructor、Field、Method、Package等都实现了该接口,对反射熟悉的朋友应该都会很熟悉这种解析方式。

    编译时解析

编译时解析有两种机制,分别简单描述下:

1)Annotation Processing Tool

apt自JDK5产生,JDK7已标记为过期,不推荐使用,JDK8中已彻底删除,自JDK6开始,可以使用Pluggable Annotation Processing API来替换它,apt被替换主要有2点原因:

    api都在com.sun.mirror非标准包下 没有集成到javac中,需要额外运行

2)Pluggable Annotation Processing API

自JDK6加入,作为apt的替代方案,它解决了apt的两个问题,javac在执行的时候会调用实现了该API的程序,这样我们就可以对编译器做一些增强,javac执行的过程如下:

lombok工作原理

Lombok本质上就是一个实现了“”的程序。在使用javac的过程中,它产生作用的具体流程如下:

  1. javac对源代码进行分析,生成了一棵抽象语法树(AST)
  2. 运行过程中调用实现了“JSR 269 API”的Lombok程序
  3. 此时Lombok就对第一步骤得到的AST进行处理,找到@Data注解所在类对应的语法树(AST),然后修改该语法树(AST),增加getter和setter方法定义的相应树节点
  4. javac使用修改后的抽象语法树(AST)生成字节码文件,即给class增加新的节点(代码块)

jsr269抽象语法树操作API编译期注解处理-简单demo

JDK1.6引入了JSR269规范,允许在编译期处理注解,读取、修改、添加抽象语法树中的内容。 lombok插件就是应用了这个

1.自定义注解

package com.xiao.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自定义Data注解
 *
 * @author xiaoss
 * @since 1.0, 2022年05月09日 09:43:44
 */
@Target({ElementType.TYPE})   //标识作用对象
@Retention(RetentionPolicy.SOURCE)
/*
注解保留策略类:
1)RetentionPolicy.SOURCE表示该注解只在源码中保留,编译之后class文件就不存在了
2)RetentionPolicy.CLASS表示该注解只保留到编译后,运行时候会被遗弃(如:在运行时通过反射去获取这个注解是找不到的)
3)RetentionPolicy.RUNTIME 表示该注解一直保留到运行
*/
public @interface CustomData {
}

2.创建处理器

package com.xiao.annotation;

import com.google.auto.service.AutoService;
import com.sun.source.tree.Tree;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.TreeTranslator;
import com.sun.tools.javac.util.*;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import java.util.Set;

/**
 * 自定义注解处理类,继承AbstractProcessor,重写init和process方法
 *
 * @author xiaoss
 * @since 1.0, 2022年05月09日 10:05:59
 */
@AutoService(Processor.class)
@SupportedAnnotationTypes("com.xiao.annotation.CustomData")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class CustomDataAnnotationProcessor extends AbstractProcessor {

    private JavacTrees javacTrees;

    private TreeMaker treeMaker;

    private Names names;

    private Messager messager;

    /**
     * 从Context中初始化JavacTrees,TreeMaker,Names
     * @param processingEnv
     */
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        messager.printMessage(Diagnostic.Kind.ERROR,"========================>init");
        super.init(processingEnv);
        Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
        javacTrees = JavacTrees.instance(processingEnv);
        treeMaker = TreeMaker.instance(context);
        names = Names.instance(context);
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        messager.printMessage(Diagnostic.Kind.ERROR,"========================>process");
        messager.printMessage(Diagnostic.Kind.NOTE, "source version -- " + getSupportedSourceVersion());
        // 获取注解类的集合,之后依次去处理
        Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(CustomData.class);
        for (Element element : set) {
            // 获取当前类的抽象语法树
            JCTree tree = javacTrees.getTree(element);
            // 获取抽象语法树的所有节点
            // Visitor 抽象内部类,内部定义了访问各种语法节点的方法
            tree.accept(new TreeTranslator() {
                @Override
                public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
                    jcClassDecl.defs.stream()
                            // 过滤,只处理变量类型
                            .filter(it -> it.getKind().equals(Tree.Kind.VARIABLE))
                            // 类型强转
                            .map(it -> (JCTree.JCVariableDecl) it)
                            .forEach(it -> {
                                System.out.println(it.toString());
                                // 添加get方法
                                jcClassDecl.defs = jcClassDecl.defs.prepend(genGetterMethod(it));
                                // 添加set方法
                                jcClassDecl.defs = jcClassDecl.defs.prepend(genSetterMethod(it));
                            });

                    super.visitClassDef(jcClassDecl);
                }
            });

        }
        return true;
    }

    /**
     * 生成get方法
     * @param jcVariableDecl
     * @return
     */
    private JCTree.JCMethodDecl genGetterMethod(JCTree.JCVariableDecl jcVariableDecl) {
        // 生成return语句,return this.xxx
        JCTree.JCReturn returnStatement = treeMaker.Return(
                treeMaker.Select(
                        treeMaker.Ident(names.fromString("this")),
                        jcVariableDecl.getName()
                )
        );

        ListBuffer<JCTree.JCStatement> statements = new ListBuffer<JCTree.JCStatement>().append(returnStatement);

        // public 方法访问级别修饰
        JCTree.JCModifiers modifiers = treeMaker.Modifiers(Flags.PUBLIC);
        // 方法名 getXXX ,根据字段名生成首字母大写的get方法
        Name getMethodName = createGetMethodName(jcVariableDecl.getName());
        // 返回值类型,get类型的返回值类型与字段类型一致
        JCTree.JCExpression returnMethodType = jcVariableDecl.vartype;
        // 生成方法体
        JCTree.JCBlock body = treeMaker.Block(0, statements.toList());
        // 泛型参数列表
        List<JCTree.JCTypeParameter> methodGenericParamList = List.nil();
        // 参数值列表
        List<JCTree.JCVariableDecl> parameterList = List.nil();
        // 异常抛出列表
        List<JCTree.JCExpression> throwCauseList = List.nil();

        // 生成方法定义树节点
        return treeMaker.MethodDef(
                // 方法访问级别修饰符
                modifiers,
                // get 方法名
                getMethodName,
                // 返回值类型
                returnMethodType,
                // 泛型参数列表
                methodGenericParamList,
                //参数值列表
                parameterList,
                // 异常抛出列表
                throwCauseList,
                // 方法默认体
                body,
                // 默认值
                null
        );

    }

    /**
     * 生成set方法
     * @param jcVariableDecl
     * @return
     */
    private JCTree.JCMethodDecl genSetterMethod(JCTree.JCVariableDecl jcVariableDecl) {
        // this.xxx=xxx
        JCTree.JCExpressionStatement statement = treeMaker.Exec(
                treeMaker.Assign(
                        treeMaker.Select(
                                treeMaker.Ident(names.fromString("this")),
                                jcVariableDecl.getName()
                        ),
                        treeMaker.Ident(jcVariableDecl.getName())
                )
        );

        ListBuffer<JCTree.JCStatement> statements = new ListBuffer<JCTree.JCStatement>().append(statement);

        // set方法参数
        JCTree.JCVariableDecl param = treeMaker.VarDef(
                // 访问修饰符
                treeMaker.Modifiers(Flags.PARAMETER, List.nil()),
                // 变量名
                jcVariableDecl.name,
                //变量类型
                jcVariableDecl.vartype,
                // 变量初始值
                null
        );

        // 方法访问修饰符 public
        JCTree.JCModifiers modifiers = treeMaker.Modifiers(Flags.PUBLIC);
        // 方法名(setXxx),根据字段名生成首选字母大写的set方法
        Name setMethodName = createSetMethodName(jcVariableDecl.getName());
        // 返回值类型void
        JCTree.JCExpression returnMethodType = treeMaker.Type(new Type.JCVoidType());
        // 生成方法体
        JCTree.JCBlock body = treeMaker.Block(0, statements.toList());
        // 泛型参数列表
        List<JCTree.JCTypeParameter> methodGenericParamList = List.nil();
        // 参数值列表
        List<JCTree.JCVariableDecl> parameterList = List.of(param);
        // 异常抛出列表
        List<JCTree.JCExpression> throwCauseList = List.nil();
        // 生成方法定义语法树节点
        return treeMaker.MethodDef(
                // 方法级别访问修饰符
                modifiers,
                // set 方法名
                setMethodName,
                // 返回值类型
                returnMethodType,
                // 泛型参数列表
                methodGenericParamList,
                // 参数值列表
                parameterList,
                // 异常抛出列表
                throwCauseList,
                // 方法体
                body,
                // 默认值
                null
        );

    }

    /**
     * 生成get方法名称
     * @param variableName
     * @return
     */
    private Name createGetMethodName(Name variableName) {
        String fieldName = variableName.toString();
        return names.fromString("get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1));

    }

    /**
     * 生成set方法名称
     * @param variableName
     * @return
     */
    private Name createSetMethodName(Name variableName) {
        String fieldName = variableName.toString();
        System.out.println(fieldName.substring(1));
        return names.fromString("set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1));

    }

}

3.pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.xiao</groupId>
    <artifactId>custom-annotation</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <java.home>D:javajdk1.8</java.home>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.google.auto.service</groupId>
            <artifactId>auto-service</artifactId>
            <version>1.0</version>
        </dependency>

        <dependency>
            <groupId>jdk.tools</groupId>
            <artifactId>jdk.tools</artifactId>
            <version>1.8</version>
            <scope>system</scope>
            <systemPath>${java.home}/lib/tools.jar</systemPath>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
                <executions>
                    <execution>
                        <id>default-compile</id>
                        <configuration>
                            <compilerArgument>-proc:none</compilerArgument>
                            <source>1.8</source>
                            <target>1.8</target>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>


</project>

五、lombok优缺点

优点:

  1. 能通过注解的形式自动生成构造器、getter/setter、equals、hashcode、toString等方法,提高了一定的开发效率
  2. 属性做修改时,也简化了维护为这些属性所生成的getter/setter方法等

缺点:

  1. 不支持多种参数构造器的重载
  2. 虽然省去了手动创建getter/setter方法的麻烦,但大大降低了源代码的可读性和完整性,降低了阅读源代码的舒适度
经验分享 程序员 职场和发展