1.Lombok概述
2.Lombok介绍、源码使用方法和总结
3.Lombok工作原理
4.Java代码生成工具之Lombok
5.Lombok的分析介绍及实现原理解析(Java插入式注解处理器)
6.Lombok经常用,但是源码你知道它的原理是什么吗?
Lombok概述
Lombok是一个提升Java开发效率的库,通过注解简化代码编写。分析
它在编译期修改源码,源码性能损耗较小,分析源码编织器下载自动插入IDE,源码简化getter/setter、分析toString、源码equals、分析hashCode方法生成。源码
使用Lombok能节省大量时间,分析但需引入依赖并下载IDE插件。源码不建议在getter/setter中加业务代码,分析会导致Model复杂。源码
Lombok注解包括@ToString、@EqualsAndHashCode、@AllArgsConstructor等,分别用于生成toString、equals和hashCode方法,全参、特定参数、无参构造器。
@Data整合了多个注解,提供getter、setter、toString、equals、hashCode、无参构造器。@NonNull对参数做非空检查,@Cleanup自动关闭资源,@SneakyThrows捕获异常,@Synchronized自动加锁。
使用Lombok时,需权衡是否使用插件,以及避免在getter/setter中添加业务代码,以保持Model的纯粹性。
Lombok介绍、使用方法和总结
Lombok是一种旨在简化Java编程的工具,通过添加“处理程序”来生成无需手动编写、减少重复和提高开发效率的代码。其核心是使用注解,如@Data、@Getter/@Setter、@NonNull等,来在编译时自动生成构造器、getter/setter、源码怎么找到中文equals、hashcode和toString等方法,无需开发者亲自编写,从而让代码显得更简洁。
使用Lombok,开发者可以在类上添加@Data,它会自动创建所有属性的getter和setter,以及equals、hashCode和toString方法。如果需要更精细的控制,可以使用@Getter/@Setter注解针对单个属性生成方法。@NonNull确保属性非空,@Cleanup简化资源管理,@ToString和@EqualsAndHashCode则用于生成对象的字符串表示和相等性比较,而无需逐一编写。
然而,Lombok也存在一些局限,比如不支持多参数构造器的重载,这可能导致源代码可读性降低。尽管如此,Lombok的优点在于提高开发效率,减少重复工作,特别是对于属性众多的javabean类。但需注意,过度依赖Lombok可能会降低代码的可维护性和阅读体验,因此应根据项目需求和团队规范合理使用。
有人认为,虽然Lombok提供了便利,但它本质上改变了编程模式,可能成为语言演进的障碍。使用Lombok时,要权衡其优点和可能带来的代码复杂性,确保它真正服务于业务逻辑,而非成为代码质量的负担。
总结来说,Lombok是一把双刃剑,适合用在适当的地方,理解其工作原理和优缺点是关键。在实际开发中,需结合具体项目情况,灵活运用,以实现最佳效果。
Lombok工作原理
Lombok注解生成代码的机制基于Java注解解析,分为运行时解析和编译时解析两部分。运行时解析依赖注解的@Retention设置为RUNTIME,通过反射获取注解信息。编译时解析则分为APT(Annotation Processing Tool)和Pluggable Annotation Processing API两种方式。如何更换源码名字APT在JDK6起被引入,但在JDK8中被删除,因其API位于非标准包内且未集成到javac中,需额外运行。Pluggable Annotation Processing API作为APT的替代方案,通过在javac执行过程中调用实现该API的程序,对编译器进行增强。Lombok源码中,各种注解的实现集中于HandleXXX类中,如@Getter注解的实现位于HandleGetter.handle()方法。许多其他类库亦采用类似方式实现,如Google Auto和Dagger。
Lombok在简化Java代码编写方面具有显著优势,其注解使得类定义更加简洁,无需手动编写getter、setter、构造器等常用方法,极大提高了开发效率。同时,Lombok能够减少冗余代码,降低维护成本,提升代码可读性。然而,Lombok的使用也存在一些潜在的缺点。首先,过度依赖注解可能导致代码难以理解,对于初次接触Lombok的开发者来说,理解代码结构和功能可能较为困难。其次,Lombok的某些功能在特定情况下可能引入不必要的复杂性,如依赖注入的自动化处理,可能在某些项目中并非必要,且可能导致代码难以调试。此外,由于Lombok是第三方库,其更新和维护依赖于社区支持,可能导致与特定版本的Java或IDE不兼容的问题,影响开发效率。综合而言,Lombok的使用应根据项目需求和团队习惯权衡利弊,合理选择是否采用。
Java代码生成工具之Lombok
Lombok是Java开发中常用的代码生成工具,它通过注解在编译期间自动生成相应的代码,简化了开发过程。首先,你需要在POM文件中添加Lombok的依赖,并在IDEA的同花顺zlcb指标源码Plugins Marketplace中安装Lombok插件,启用注解处理器功能。
在类上使用常见的注解如:@Data,它会为类的所有属性添加get、set方法,并自动生成equals、canEquals、hashCode和toString方法。例如:
@Data class Example { ... }
编译后的class文件将包含这些方法,源代码保持简洁。
还有@Getter和@Setter,分别用于添加属性的Get和Set方法,以及@Accessors,用于调整生成的get、set方法。@EqualsAndHashCode用于添加equals、canEqual和hashCode方法,@ToString则用于添加toString方法。
对于子类,@EqualsAndHashCode和@ToString的callSuper属性可确保继承父类的属性。而@AllArgsConstructor和@NoArgsConstructor分别用于添加全参和无参构造器,@Builder和@SuperBuilder则支持基于建造者模式的对象创建,@NonNull用于非空检查和有参构造器,@RequiredArgsConstructor则简化了Spring的依赖注入。
最后,@Slf4j注解用于为类添加SLF4J日志对象。例如:
@Slf4j class LoggingExample { ... }
以上是Lombok的一些基本用法,通过这些注解,开发者可以编写出更加简洁和易于维护的代码。
Lombok的介绍及实现原理解析(Java插入式注解处理器)
在日常的Java项目开发中,手动编写Bean的getter、setter、构造方法、equals、hashcode和toString等方法往往耗时且易出错。这时,Lombok提供了强大的解决方案,它通过注解方式,自动为Bean生成这些方法,极大减轻了开发者的负担。
例如,使用@NonNull注解可避免空指针异常,@Accessors注解可灵活生成getter、setter、构造方法、equals和hashCode。此外,@Builder注解则提供了一种简洁的甄嬛传源码构建Bean实例方式,极大提升了开发效率。
让我们以一个简单的Bean为例,添加Lombok注解后,编译器会在生成字节码时自动为其添加所有所需方法,省去了手动编写大量代码的麻烦。
Lombok实现的原理基于Java的JSR规范——插入式注解处理器(PEP)。PEP允许开发者在编译阶段嵌入自定义的代码逻辑,Lombok正是通过PEP插件与Java编译器协作,自动分析源代码、生成或修改编译过程中产生的抽象语法树(AST),从而实现自动化代码生成。
具体而言,Lombok插件会读取源代码中的注解信息,对AST进行分析,并依据注解内容修改AST结构。这些修改包括但不限于新增方法、修改方法签名、创建新类等。编译器在处理这些修改后,会再次分析修改后的AST,直到所有注解处理完成。
借助PEP的API,开发者可以编写自定义的注解处理器,以实现特定的功能。例如,编写一个注解处理器来生成getter方法或实现类规范检查,只需要理解PEP的API和AST的结构,即可轻松完成。
总结而言,Lombok通过JSR规范下的PEP插件,实现了在编译阶段自动分析和修改源代码的功能,极大地简化了开发者的工作,提高了代码的可读性和维护性。通过学习和实践,开发者可以更好地利用Lombok的自动代码生成能力,提升开发效率。
Lombok经常用,但是你知道它的原理是什么吗?
Lombok是一个简化Java代码的工具,通过注解自动生成getter、setter、构造函数等,减少冗余代码。其原理在于编译时注解处理,利用编译时注解处理器APT在编译阶段分析和处理代码。
编译时注解是Java中一种特殊的注解形式,它们在代码编译阶段被处理,而不是运行时。APT(Annotation Processing Tool)是Java编译器提供的一种工具,用于处理编译时注解。APT通过在编译阶段扫描和处理注解信息,生成额外的代码,如类、接口、字段等,从而实现自动化代码生成,提高开发效率。
Lombok通过APT在编译阶段修改抽象语法树(AST),即修改原有的类结构,添加getter、setter等方法,实现对代码的自动化处理。在项目中,只需引入Lombok依赖,使用其注解,即可实现代码简化。
使用Lombok时,只需在类中添加如@Data、@Builder、@NoArgsConstructor等注解,即可自动生成对应的方法和构造函数,使得代码更加简洁、易于维护。同时,Lombok支持自定义注解和注解处理器,以满足特定需求,提高代码可读性和开发效率。
总结而言,Lombok借助编译时注解处理和APT技术,实现在编译阶段对源代码进行自动化处理,生成所需的辅助方法,从而简化开发过程,提高开发效率和代码质量。
Lombok注解引发的空指针问题分析
在上线后,日志中出现空指针报错,然而代码修改记录未涉及相关工具类。深入分析后发现,Lombok注解的使用,尤其是链式编程功能,引入了问题。Lombok的@Accessors(chain = true)注解使得类的set方法返回对象自身,而非void。JDK Introspector在处理写入方法时,仅识别返回void且以set命名的方法。由于返回值非void,Introspector未能正确识别写入方法,导致BeanCopier等工具在对象复制过程中出现问题,进而触发空指针异常。
分析路径如下:
(1) 报错信息指向了从服务获取的mtProcessDtoList可能为null,但问题实质出在工具类内部,具体为CglibBeanCopier的copyProperties方法。
(2) 细查copyProperties代码实现,发现BeanCopier的底层能力在处理目标类属性时,未触发预期的空指针异常,问题根源在于BeanCopier源码分析。
(3) 由于代码为反编译所得,实际行数不准确,直接展示报空指针异常的源代码截图。getMethodInfo函数的入参member为null,引发空指针问题。需跟踪运行时变量值,了解setters数组元素的生成过程。
(4) target作为目标对象类,setters数组通过反射获取所有具备读写方法的描述对象。方法名描述上存在歧义,实际上返回的是包含读写方法的属性描述,两个布尔值分别对应读取与写入校验。
总结,由于无法获取目标类的writeMethod,导致无法找到写入方法,进而无法对目标对象赋值。问题源头指向Lombok注解的使用,通过去除@Accessors(chain = true)注解,将工程中链式set赋值改回常规方式,或替换对象拷贝工具类,推荐使用MapStruct配合Lombok在编译时自动生成get/set方法,实现更加安全高效的对象拷贝。
需注意,依赖于JDK Introspector获取类set方法描述的工具类、组件均受其定义限制。在工程实践中,对象拷贝与属性赋值过程中可能出现类似问题,因此在日常开发时需关注组件特性使用。此外,对象拷贝的最佳实践已有相关文章,推荐大家阅读。感谢阅读!
我怀疑这是IDEA的BUG,但是我翻遍全网没找到证据!
分享一个关于IDEA的有趣问题。最近,有朋友在使用Lombok的@Data注解时遇到了奇怪的现象,代码中一个布尔类型赋值给整型,居然没有报错。他将问题发给了我,我一开始也觉得不可思议。经过研究,我发现原因可能出在IDEA上,而并非Lombok插件本身。
为了验证我的猜想,我在本地环境中复现了问题。在源文件中,我只添加了@Data注解。经过编译,我发现Lombok自动为我们生成了无参构造函数、getter和setter方法、equals和hashCode方法等。这让我意识到@Data注解实际上是一个复合注解,包含了多个功能。
在深入研究中,我发现真正生成hashCode方法的注解应该是@EqualsAndHashCode。为了排除干扰,我将@Data注解替换为@EqualsAndHashCode。结果,生成的方法确实少了,而且我不关心这些方法。观察到hashCode方法的第一行代码是int PRIME = true;,我意识到这里可能存在问题。
通过使用反编译工具jd-gui和查看字节码,我发现hashCode方法的实现与预期不符。在jd-gui中,我看到的hashCode方法的第一个命令使用的是整型入栈指令,值为,而不是true。这个PRIME变量似乎没有被实际使用,这个问题暂且搁置。
在查看字节码时,我注意到hashCode方法的实现是通过整型入栈指令bipush生成的,值为。经过验证,我有理由怀疑IDEA在显示int PRIME = true时存在BUG。
尽管我在网络上进行了深入搜索,但并未找到与此问题相关的详细资料。我尝试了多种搜索策略,包括使用jd-gui工具进行反编译和直接查看字节码。虽然我未能找到权威证据证明这是IDEA的BUG,但基于上述发现,我确信这是IDEA的一个问题。
这个发现为我提供了丰富的素材,我感到非常兴奋。尽管没有找到直接的权威证据,但我的分析和验证过程让我确信这是一个值得记录的问题。关于这个现象背后的原因,我在网上也找到了一些线索,包括关于常量折叠的解释和Lombok源代码中的相关提交记录。
在深入探讨IDEA的BUG时,我还提到了另一个案例,即IDEA在Debug模式下对ConcurrentLinkedQueue的处理方式可能导致空指针异常。这个问题最终被确认为IDEA的特性,并提供了关闭相关配置的解决方案。
总的来说,这篇文章分享了我对这个问题的探索过程、发现的线索以及最后的分析结果。尽管没有找到绝对的证据,但基于我的研究和分析,我确信IDEA在这特定情况下存在BUG。
用好这个IDEA插件,写代码效率至少提升5倍!
还在编写无聊枯燥又难以维护的POJO吗?洁癖者的春天在哪里?请看Lombok!在过往的Java项目中,充斥着太多不友好的代码:POJO的getter/setter/toString;异常处理;I/O流的关闭操作等等,这些样板代码既没有技术含量,又影响着代码的美观,Lombok应运而生。
首先说明一下:任何技术的出现都是为了解决某一类问题的,如果在此基础上再建立奇技*巧,不如回归Java本身。应该保持合理使用而不滥用。
Lombok的使用非常简单,下面我们一起来看下:
1)引入相应的maven包:
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1..</version><scope>provided</scope></dependency>Lombok的scope=provided,说明它只在编译阶段生效,不需要打入包中。事实正是如此,Lombok在编译期将带Lombok注解的Java文件正确编译为完整的Class文件。
2)添加IDE工具对Lombok的支持:
IDEA中引入Lombok支持如下:
点击File--Settings设置界面,安装Lombok插件:
点击File--Settings设置界面,开启AnnocationProcessors:
开启该项是为了让Lombok注解在编译阶段起到作用。
Eclipse的Lombok插件安装可以自行百度,也比较简单,值得一提的是,由于Eclipse内置的编译器不是Oraclejavac,而是eclipse自己实现的EclipseCompilerforJava(ECJ).要让ECJ支持Lombok,需要在eclipse.ini配置文件中添加如下两项内容:
-Xbootclasspath/a:[lombok.jar所在路径]
-javaagent:[lombok.jar所在路径]
3)Lombok实现原理:
自从Java6起,javac就支持“JSRPluggableAnnotationProcessingAPI”规范,只要程序实现了该API,就能在javac运行的时候得到调用。
Lombok就是一个实现了"JSRAPI"的程序。在使用javac的过程中,它产生作用的具体流程如下:
javac对源代码进行分析,生成一棵抽象语法树(AST)
javac编译过程中调用实现了JSR的Lombok程序
此时Lombok就对第一步骤得到的AST进行处理,找到Lombok注解所在类对应的语法树(AST),然后修改该语法树(AST),增加Lombok注解定义的相应树节点
javac使用修改后的抽象语法树(AST)生成字节码文件
4)Lombok注解的使用:
POJO类常用注解:
@Getter/@Setter:作用类上,生成所有成员变量的getter/setter方法;作用于成员变量上,生成该成员变量的getter/setter方法。可以设定访问权限及是否懒加载等。
packagecom.trace;importlombok.AccessLevel;importlombok.Getter;importlombok.Setter;/***CreatedbyTraceon/5/.<br/>*DESC:测试类*/@SuppressWarnings("unused")publicclassTestClass{ publicstaticvoidmain(String[]args){ }@Getter(value=AccessLevel.PUBLIC)@Setter(value=AccessLevel.PUBLIC)publicstaticclassPerson{ privateStringname;privateintage;privatebooleanfriendly;}publicstaticclassAnimal{ privateStringname;privateintage;@Getter@Setterprivatebooleanfunny;}}在Structure视图中,可以看到已经生成了getter/setter等方法:
编译后的代码如下:[这也是传统Java编程需要编写的样板代码]
////Sourcecoderecreatedfroma.classfilebyIntelliJIDEA//(poweredbyFernflowerdecompiler)//packagecom.trace;publicclassTestClass{ publicTestClass(){ }publicstaticvoidmain(String[]args){ }publicstaticclassAnimal{ privateStringname;privateintage;privatebooleanfunny;publicAnimal(){ }publicbooleanisFunny(){ returnthis.funny;}publicvoidsetFunny(booleanfunny){ this.funny=funny;}}publicstaticclassPerson{ privateStringname;privateintage;privatebooleanfriendly;publicPerson(){ }publicStringgetName(){ returnthis.name;}publicintgetAge(){ returnthis.age;}publicbooleanisFriendly(){ returnthis.friendly;}publicvoidsetName(Stringname){ this.name=name;}publicvoidsetAge(intage){ this.age=age;}publicvoidsetFriendly(booleanfriendly){ this.friendly=friendly;}}}@ToString:作用于类,覆盖默认的toString()方法,可以通过of属性限定显示某些字段,通过exclude属性排除某些字段。
@EqualsAndHashCode:作用于类,覆盖默认的equals和hashCode
@NonNull:主要作用于成员变量和参数中,标识不能为空,否则抛出空指针异常。
@NoArgsConstructor,@RequiredArgsConstructor,@AllArgsConstructor:作用于类上,用于生成构造函数。有staticName、access等属性。
staticName属性一旦设定,将采用静态方法的方式生成实例,access属性可以限定访问权限。
@NoArgsConstructor:生成无参构造器;
@RequiredArgsConstructor:生成包含final和@NonNull注解的成员变量的构造器;
@AllArgsConstructor:生成全参构造器。
编译后结果:
publicstaticclassPerson{ @NonNullprivateStringname;privateintage;privatebooleanfriendly;publicStringtoString(){ return"TestClass.Person(name="+this.getName()+",age="+this.getAge()+")";}@NonNullpublicStringgetName(){ returnthis.name;}publicintgetAge(){ returnthis.age;}publicbooleanisFriendly(){ returnthis.friendly;}publicvoidsetName(@NonNullStringname){ if(name==null){ thrownewNullPointerException("name");}else{ this.name=name;}}publicvoidsetAge(intage){ this.age=age;}publicvoidsetFriendly(booleanfriendly){ this.friendly=friendly;}privatePerson(){ }privatestaticTestClass.Personof(){ returnnewTestClass.Person();}@ConstructorProperties({ "name"})Person(@NonNullStringname){ if(name==null){ thrownewNullPointerException("name");}else{ this.name=name;}}@ConstructorProperties({ "name","age","friendly"})publicPerson(@NonNullStringname,intage,booleanfriendly){ if(name==null){ thrownewNullPointerException("name");}else{ this.name=name;this.age=age;this.friendly=friendly;}}}@Data:作用于类上,是以下注解的集合:@ToString@EqualsAndHashCode@Getter@Setter@RequiredArgsConstructor
@Builder:作用于类上,将类转变为建造者模式
@Log:作用于类上,生成日志变量。针对不同的日志实现产品,有不同的注解:
其他重要注解:
@Cleanup:自动关闭资源,针对实现了java.io.Closeable接口的对象有效,如:典型的IO流对象
编译后结果如下:
是不是简洁了太多。
@SneakyThrows:可以对受检异常进行捕捉并抛出,可以改写上述的main方法如下:
@Synchronized:作用于方法级别,可以替换synchronize关键字或lock锁,用处不大。
作者:LiWenD正在掘金