一、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 注解与同时使用以下的注解的效果是一样的:
- @ToString
- @Getter
- @Setter
- @RequiredArgsConstructor
- @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
JSR 269自JDK6加入,作为apt的替代方案,它解决了apt的两个问题,javac在执行的时候会调用实现了该API的程序,这样我们就可以对编译器做一些增强,javac执行的过程如下:
lombok工作原理
Lombok本质上就是一个实现了“JSR 269 API”的程序。在使用javac的过程中,它产生作用的具体流程如下:
- javac对源代码进行分析,生成了一棵抽象语法树(AST)
- 运行过程中调用实现了“JSR 269 API”的Lombok程序
- 此时Lombok就对第一步骤得到的AST进行处理,找到@Data注解所在类对应的语法树(AST),然后修改该语法树(AST),增加getter和setter方法定义的相应树节点
- 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:\java\jdk1.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优缺点
优点:
- 能通过注解的形式自动生成构造器、getter/setter、equals、hashcode、toString等方法,提高了一定的开发效率
- 让代码变得简洁,不用过多的去关注相应的方法
- 属性做修改时,也简化了维护为这些属性所生成的getter/setter方法等
缺点:
- 不支持多种参数构造器的重载
- 虽然省去了手动创建getter/setter方法的麻烦,但大大降低了源代码的可读性和完整性,降低了阅读源代码的舒适度