1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > lombok -- 爱的人爱的疯狂 恨的人恨的切齿

lombok -- 爱的人爱的疯狂 恨的人恨的切齿

时间:2023-06-18 13:47:17

相关推荐

lombok -- 爱的人爱的疯狂 恨的人恨的切齿

lombok简介

lombok是一个java库,致力于通过一组注解消除代码中的一些必要但是臃肿的样板代码,精简代码,提高效率,还有耍酷。

如何使用

使用lombok需要在IDE中引入对应的插件,并在项目中引入对应的pom依赖

安装插件

在IDEA的插件中搜索lombok然后安装

引入依赖

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

注解

lombok提供了一系列的注解来帮助我们简化代码,下面我们分别对其中一些高频的注解怎么使用进行介绍

@Getter / @Setter

@Getter和@Setter注解可以作用在类上,也可以作用在字段上。

使用前我们的写法如下:

//code1public class Person {private Long personId;private String misNum;private String fullName;private String name;public Long getPersonId() {return personId;}public void setPersonId(Long personId) {this.personId = personId;}public String getMisNum() {return misNum;}public void setMisNum(String misNum) {this.misNum = misNum;}public String getFullName() {return fullName;}public void setFullName(String fullName) {this.fullName = fullName;}public String getName() {return name;}public void setName(String name) {this.name = name;}}

使用后我们的写法如下:

//code2import lombok.Getter;import lombok.Setter;@Getter@Setterpublic class Person {private Long personId;private String misNum;private String fullName;private String name;}

查看编译后的class文件,在编译后会自动生成对应的get和set方法

//code2.classpublic class Person {private Long personId;private String misNum;private String fullName;private String name;public Person() {}public Long getPersonId() {return this.personId;}public String getMisNum() {return this.misNum;}public String getFullName() {return this.fullName;}public String getName() {return this.name;}public void setPersonId(Long personId) {this.personId = personId;}public void setMisNum(String misNum) {this.misNum = misNum;}public void setFullName(String fullName) {this.fullName = fullName;}public void setName(String name) {this.name = name;}}

@ToString

使用前我们的写法如下:

//code3public class Person {private Long personId;private String misNum;private String fullName;private String name;@Overridepublic String toString() {final StringBuilder sb = new StringBuilder("{");sb.append("\"personId\":").append(personId);sb.append(",\"misNum\":\"").append(misNum).append('\"');sb.append(",\"fullName\":\"").append(fullName).append('\"');sb.append(",\"name\":\"").append(name).append('\"');sb.append('}');return sb.toString();}}

使用后我们的写法如下:

//code4import lombok.ToString;@ToStringpublic class Person {private Long personId;private String misNum;private String fullName;private String name;}

编译后的class文件如下,会自动生成toString()方法,但是比较遗憾的是不够灵活,没有办法直接生成Json格式的toString() 方法

//code4.classpublic class Person {private Long personId;private String misNum;private String fullName;private String name;public Person() {}public String toString() {return "Person(personId=" + this.personId + ", misNum=" + this.misNum + ", fullName=" + this.fullName + ", name=" + this.name + ")";}}

@ToString还提供了一些参数

callSuper = true 可以打印父类,指定exclude可以排除字段,不过都比较鸡肋。

@Data

使用@Data写法如下:

//code5@Datapublic class Person {private Long personId;private String misNum;private String fullName;private String name;}

编译后的class文件如下:

//code5.classpublic class Person {private Long personId;private String misNum;private String fullName;private String name;public Person() {}public Long getPersonId() {return this.personId;}public String getMisNum() {return this.misNum;}public String getFullName() {return this.fullName;}public String getName() {return this.name;}public void setPersonId(Long personId) {this.personId = personId;}public void setMisNum(String misNum) {this.misNum = misNum;}public void setFullName(String fullName) {this.fullName = fullName;}public void setName(String name) {this.name = name;}public boolean equals(Object o) {if (o == this) {return true;} else if (!(o instanceof Person)) {return false;} else {Person other = (Person)o;if (!other.canEqual(this)) {return false;} else {label59: {Object this$personId = this.getPersonId();Object other$personId = other.getPersonId();if (this$personId == null) {if (other$personId == null) {break label59;}} else if (this$personId.equals(other$personId)) {break label59;}return false;}Object this$misNum = this.getMisNum();Object other$misNum = other.getMisNum();if (this$misNum == null) {if (other$misNum != null) {return false;}} else if (!this$misNum.equals(other$misNum)) {return false;}Object this$fullName = this.getFullName();Object other$fullName = other.getFullName();if (this$fullName == null) {if (other$fullName != null) {return false;}} else if (!this$fullName.equals(other$fullName)) {return false;}Object this$name = this.getName();Object other$name = other.getName();if (this$name == null) {if (other$name != null) {return false;}} else if (!this$name.equals(other$name)) {return false;}return true;}}}protected boolean canEqual(Object other) {return other instanceof Person;}public int hashCode() {int PRIME = true;int result = 1;Object $personId = this.getPersonId();int result = result * 59 + ($personId == null ? 43 : $personId.hashCode());Object $misNum = this.getMisNum();result = result * 59 + ($misNum == null ? 43 : $misNum.hashCode());Object $fullName = this.getFullName();result = result * 59 + ($fullName == null ? 43 : $fullName.hashCode());Object $name = this.getName();result = result * 59 + ($name == null ? 43 : $name.hashCode());return result;}public String toString() {return "Person(personId=" + this.getPersonId() + ", misNum=" + this.getMisNum() + ", fullName=" + this.getFullName() + ", name=" + this.getName() + ")";}}

@Data注解自动生成了getter/setter方法、toString()方法、覆写了hashCode()和equals()方法.

@Slf4j

使用@Slf4j注解可以省去实例化log对象的代码

使用方式如下:

//code6@Slf4jpublic class Person {}

编译后的class文件如下:

//code6.classimport org.slf4j.Logger;import org.slf4j.LoggerFactory;public class Person {private static final Logger log = LoggerFactory.getLogger(Person.class);public Person() {}}

@Builder

@Builder作用在类上可以将类转换为建造者模式

//code7import lombok.Builder;@Builderpublic class Person {private Long personId;private String misNum;private String fullName;private String name;}

编译后的class文件如下

public class Person {private Long personId;private String misNum;private String fullName;private String name;Person(Long personId, String misNum, String fullName, String name) {this.personId = personId;this.misNum = misNum;this.fullName = fullName;this.name = name;}public static PersonBuilder builder() {return new PersonBuilder();}}

实例化对象时可以按照下面的方式写:

public class PersonTest {public static void main(String[] args){Person person = Person.builder().personId(1L).misNum("zhangsan").build();}}

也可以指定默认值:

import lombok.Builder;import lombok.Singular;import lombok.ToString;@Builder@ToStringpublic class Person {private Long personId;private String misNum;private String fullName;private String name;@Builder.Defaultprivate int tenant = 1;}

其他的一些方法比较鸡肋 就不一一列出了

原理

lombok的基本流程是:

定义编译期的注解

利用JSR269 api(Pluggable Annotation Processing API )创建编译期的注解处理器

利用tools.jar的javac api处理AST(抽象语法树)

将功能注册进jar包

因为是在编译期生效的 所以其实直接从代码上来看,代码可能都是错误的,所以需要安装对应的IDE 插件对这些错误进行排除

想要了解lombok的原理,肯定是手撸代码来的快

前边提到lombok提供的toString方法不是json格式的,不如我们先来写一个json格式的toString方法 姑且叫做ToJsonString

@ToJsonString

先定义注解类:

package lombok;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;//作用到类@Target({ElementType.TYPE})//只在编译期起作用@Retention(RetentionPolicy.SOURCE)public @interface ToJsonString {}

然后定义对应的处理器:

package lombok;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.TypeTag;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;@SupportedAnnotationTypes("lombok.ToJsonString")@SupportedSourceVersion(SourceVersion.RELEASE_8)public class ToJsonStringProcessor extends AbstractProcessor {//主要是用来在编译期打log用的private Messager messager;//提供了待处理的抽象语法树private JavacTrees trees;//封装了创建AST节点的一些方法private TreeMaker treeMaker;//提供了创建标识符的方法private Names names;/*** 从环境里获取一些关键信息* @param processingEnv*/@Overridepublic synchronized void init(ProcessingEnvironment processingEnv) {super.init(processingEnv);this.messager = processingEnv.getMessager();this.trees = JavacTrees.instance(processingEnv);Context context = ((JavacProcessingEnvironment) processingEnv).getContext();this.treeMaker = TreeMaker.instance(context);this.names = Names.instance(context);}@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {//获取被ToJsonString标记的类Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(ToJsonString.class);//遍历 生成语法树set.forEach(element -> {JCTree jcTree = trees.getTree(element);jcTree.accept(new TreeTranslator() {@Overridepublic void visitClassDef(JCTree.JCClassDecl jcClassDecl) {List<JCTree.JCVariableDecl> jcVariableDeclList = List.nil();for (JCTree tree : jcClassDecl.defs) {if (tree.getKind().equals(Tree.Kind.VARIABLE)) {JCTree.JCVariableDecl jcVariableDecl = (JCTree.JCVariableDecl) tree;jcVariableDeclList = jcVariableDeclList.append(jcVariableDecl);}}jcClassDecl.defs = jcClassDecl.defs.prepend(makeToJsonStringMethodDecl(jcVariableDeclList));super.visitClassDef(jcClassDecl);}});});return false;}private JCTree.JCMethodDecl makeToJsonStringMethodDecl(List<JCTree.JCVariableDecl> jcVariableDeclList) {//方法名Name toString = names.fromString("toString");//返回类型JCTree.JCExpression returnType = getType("java.lang.String");//泛型参数列表List<JCTree.JCTypeParameter> methodGenericParams = List.nil();//参数列表List<JCTree.JCVariableDecl> params = List.nil();//异常抛出列表List<JCTree.JCExpression> throwsClauses = List.nil();//拼接要输出的JSONJCTree.JCExpression jcExpression = treeMaker.Literal( "{");for (int i = 0; i < jcVariableDeclList.size(); i++) {JCTree.JCVariableDecl jcVariableDecl = jcVariableDeclList.get(i);if (i != 0){jcExpression = treeMaker.Binary(JCTree.Tag.PLUS, jcExpression, treeMaker.Literal(",\"" + jcVariableDecl.name.toString() + "\":"));}else{jcExpression = treeMaker.Binary(JCTree.Tag.PLUS, jcExpression, treeMaker.Literal("\""+jcVariableDecl.name.toString() + "\":"));}if (jcVariableDecl.vartype.toString().contains("String")){jcExpression = treeMaker.Binary(JCTree.Tag.PLUS, jcExpression, treeMaker.Literal("\""));}jcExpression = treeMaker.Binary(JCTree.Tag.PLUS, jcExpression, treeMaker.Ident(jcVariableDecl.name));if (jcVariableDecl.vartype.toString().contains("String")){jcExpression = treeMaker.Binary(JCTree.Tag.PLUS, jcExpression, treeMaker.Literal("\""));}}jcExpression = treeMaker.Binary(JCTree.Tag.PLUS, jcExpression, treeMaker.Literal("}"));JCTree.JCStatement jcStatement = treeMaker.Return(jcExpression);List<JCTree.JCStatement> jcStatementList = List.nil();jcStatementList = jcStatementList.append(jcStatement);//方法体JCTree.JCBlock body = treeMaker.Block(0, jcStatementList);return treeMaker.MethodDef(treeMaker.Modifiers(Flags.PUBLIC),toString,returnType,List.nil(), params, List.nil(), body, null);}private JCTree.JCExpression getType(String components) {String[] componentArray = components.split("\\.");JCTree.JCExpression expr = treeMaker.Ident(names.fromString(componentArray[0]));for (int i = 1; i < componentArray.length; i++) {expr = treeMaker.Select(expr, names.fromString(componentArray[i]));}return expr;} }

再定义一个实体类,并使用@ToJsonString注解

@ToJsonStringpublic class Person {private String misNum;private String fullName;private Long personId;}

需要注意下 因为现在不是jar依赖,并且本身是在编译期生效,所以我们需要先编译处理器 再编译Person类,切换到代码的文件目录下,手动编译

//首先创建一个文件夹 存放编译后的class文件mrdir lombokclass//对编译器进行编译javac -cp $JAVA_HOME/lib/tools.jar ToJsonString* -d lombokclass//编译Person类javac -cp lombokclass -processor lombok.ToJsonStringProcessor Person.java -d lombokclass

我是直接在IDEA中看的编译后的文件,如下图:

关于JSR269 可以参考网上的这篇文章:JSR269参考资料

争议

正所谓:爱的人爱的疯狂 恨的人恨的发飙

lombok的好处显而易见,代码简洁,看着舒服 扩展时不必重写toString等

但反对使用的人也有很多正当理由

1.入侵性强 耦合增加 胁迫使用

首先如果你在项目里使用了lombok,那同一个项目组的小伙伴都必须安装lombok插件;如果你在对外提供的api里使用了lombok,那引用你的依赖的项目也必须使用lombok,简直是在耍流氓

2.技术债务

lombok自身无法及时支持最高的Java版本 如果升级项目的java版本时,lombok对此并不支持,将需要做很多工作去生成对应的方法

3.代码调试困难

如果我们需要查看某个getter方法在哪些地方被引用 你会发现做不到

4.equals重写

@Data默认对equals进行了重写,很难说这是不是一件好事,如果只使用了@Data,而不使用@EqualsAndHashCode(callSuper=true)的话,会默认是@EqualsAndHashCode(callSuper=false),这时候生成的equals()方法只会比较子类的属性,不会考虑从父类继承的属性。

所以,就我个人来说,不首先使用lombok,如果项目里已经有了 那我就用

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。