Lombok一文通
1、Lombok简介
作为java的忠实粉丝,但也不得不承认,java是一门比较啰嗦的语言,很多代码的编写远不如其他静态语言方便,更别说跟脚本语言比较了。
因此,lombok应运而生。
Lombok是一种工具库,它提供了一组注解,用于简化Java代码的编写。它的主要目标是通过自动生成代码来减少样板代码的数量,从而提高开发人员的生产力。
使用Lombok,开发人员可以通过添加注解来自动生成getter和setter方法、构造函数、equals和hashCode方法,以及其他常见的样板代码。此外,Lombok还提供了一些其他的注解,可以自动生成日志记录、单例模式、Builder模式等。
Lombok的使用非常简单,只需在类或字段上添加注解即可。在编译时,Lombok会自动生成相应的代码,并将其写入源代码中。这意味着生成的代码会被编译器视为原始代码的一部分,从而允许开发人员在使用生成的代码时获得代码补全和其他IDE功能的支持。
总的来说,Lombok是一个能够减少Java代码冗余的有用工具,它可以帮助开发人员更高效地编写代码,提高开发速度和代码质量。
项目地址: lombok官网
2、Lombok原理
用了那么久lombok,就是觉得写代码方便很多了,但它底层的原理是什么呢?
Lombok 的核心原理是在编译时通过注解处理器(Annotation Processor)来操作抽象语法树(Abstract Syntax Tree, AST),从而扩展 Java 编译器的功能。
2.1.Lombok工作原理
Lombok 的工作原理主要包括以下几个步骤:
-
注解定义: Lombok 定义了一系列注解,这些注解可以被应用于 Java 类、方法、字段等元素上。
-
注解处理器: Lombok 包含了一个或多个注解处理器,这些处理器在编译时期被触发。当处理器检测到 Lombok 注解时,它会根据注解的配置生成相应的代码。
-
编译时代码生成: 使用 Lombok 注解的 Java 类在编译时,Lombok 的注解处理器会读取注解信息,并根据这些信息生成额外的 Java 代码。例如,当在字段上使用
@Getter和@Setter注解时,Lombok 会生成对应的 getter 和 setter 方法。 -
AST 操作: Lombok 通过操作 Java 源文件的 AST 来插入生成的代码。AST 是源代码的树状结构表示,Lombok 通过修改 AST 来实现代码的自动添加。
-
源代码注入: 生成的代码会被注入到源文件中,通常作为单独的文件或在原有文件中以特殊的方式添加,以便编译器能够识别并编译这些新增的代码。
-
IDE 集成: Lombok 还提供了对 IntelliJ IDEA 和 Eclipse 等 IDE 的支持。这意味着在使用这些 IDE 时,你可以获得完整的 Lombok 注解支持,包括代码补全、导航、重构等特性。
-
构建工具集成: Lombok 与 Maven 和 Gradle 等构建工具集成,确保在构建项目时能够正确处理 Lombok 注解。
-
无运行时依赖: 由于 Lombok 在编译时生成代码,因此它不会在运行时添加任何依赖或性能开销。
2.2.idea为什么需要引入lombok插件
Lombok 通过注解来自动生成代码,但这些注解发生在编译期。但我们使用ide进行开发的时候,在编写代码就已经需要知道注解的语义,因此通过插件,以便 IDE 能够理解 Lombok 注解并提供相应的代码补全、导航和错误检查功能。
-
注解处理器集成: Lombok 作为一个 Java 库,通过注解处理器在编译时生成额外的代码。IntelliJ IDEA 插件确保了这些注解处理器被正确集成到 IDE 中。
-
代码补全: 插件为 Lombok 提供的注解(如
@Getter,@Setter,@ToString等)提供代码补全功能,使得开发更加高效。 -
导航和查找: 使用 Lombok 插件,IDEA 可以正确导航到 Lombok 生成的方法和构造函数,就像它们是手写的一样。
-
错误检查和代码分析: 插件允许 IDEA 对 Lombok 注解进行语义分析,提供错误和警告信息,帮助开发者避免潜在的问题。
-
预览生成的代码: 在某些情况下,插件允许开发者预览 Lombok 注解生成的代码,这有助于理解背后的行为。
3、插入式注解处理器
在 Java 中,插入式注解处理器(Pluggable Annotation Processor)允许开发者在编译时期自动处理特定的注解。下面是一个简单的示例,演示如何创建和使用自定义注解以及相应的注解处理器。
1.创建自定义注解
首先,定义一个注解,用来标记需要生成额外代码的类。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE) // 标记在类上
public @interface GenerateClass {String value() default "DefaultClassName";
}
2.创建注解处理器
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.Processor;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.Element;
import javax.tools.Diagnostic;
import com.google.auto.service.AutoService;
import com.example.annotations.GenerateClass;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.annotation.processing.Filer;
import javax.tools.JavaFileObject;
import java.io.Writer;
import java.util.Set;@AutoService(Processor.class)
@SupportedAnnotationTypes("com.example.annotations.GenerateClass")
@SupportedSourceVersion(javax.lang.model.SourceVersion.RELEASE_8)
public class GenerateClassProcessor extends AbstractProcessor {@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {for (Element element : roundEnv.getElementsAnnotatedWith(GenerateClass.class)) {GenerateClass generateClass = element.getAnnotation(GenerateClass.class);String className = generateClass.value();try {Filer filer = processingEnv.getFiler();JavaFileObject builderFile = filer.createSourceFile(className);try (Writer writer = builderFile.openWriter()) {writer.write("public class " + className + " {\n");writer.write(" public " + className + "() {\n");writer.write(" System.out.println(\\\"New class generated!\\\");\n");writer.write(" }\n");writer.write("}\n");}} catch (Exception e) {processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, e.getMessage());}}return true;}
}
3.注册注解处理器
如果你使用了 Google 的 AutoService,则注解处理器会自动注册。否则,你需要在 META-INF/services 目录下创建一个名为 javax.annotation.processing.Processor 的文件,并填入你的处理器的全限定名。
import com.example.annotations.GenerateClass;@GenerateClass(value = "MyGeneratedClass")
public class MyClass {// 这个类会被注解处理器处理,并生成 MyGeneratedClass
}
4.编译项目
编译你的项目,注解处理器将自动运行,并根据 GenerateClass 注解生成新的类文件。
注意事项:
- 注解处理器是在编译时执行的,因此它们不会影响运行时性能。
- 确保你的注解处理器类被正确注册,以便编译器能够发现并使用它。
- 使用
@SupportedAnnotationTypes和@SupportedSourceVersion注解来声明你的处理器支持哪些注解和 Java 版本。 - 处理过程中发生的任何异常都需要被捕获并适当处理,通常通过
processingEnv.getMessager().printMessage()打印错误信息。
4、Lombok示例
3.1.javabean注解
| 注解名称 | 作用 |
| @Getter | 用于自动生成字段的get方法。作为类注解,则生成该类所有字段的getter方法(static字段不参与);作为字段注解,则生成该字段的getter方法。 |
| @Setter | 用于自动生成字段的set方法。作为类注解,则生成该类所有字段的setter方法(final/static字段不参与);作为字段注解,则生成该字段的setter方法。 |
| @ToString | 自动生成 |
| @EqualsAndHashCode | 自动生成 equals() 和 hashCode() 方法 |
| @NoArgsConstructor | 自动生成无参构造函数 |
| @AllArgsConstructor | 自动生成包含所有字段的构造函数 |
| @RequiredArgsConstructo | 自动生成包含所有非 final 和非静态字段的构造函数,并标记为 @NonNull 的字段 |
| @Data | 组合注解,相当于 @Getter, @Setter, @RequiredArgsConstructor, @ToString, 和 @EqualsAndHashCode |
| @Value | 类似 @Data,但生成不可变对象(所有字段都是 final) |
现在对@Data注解进行演示
假设一个普通的javabean定义如下:
import lombok.Data;@Data
public class Person {private int age;private String name;private static String key = "key";private final String finalField = "FINALFIELD";
}
经过lombok插件的翻译之后,变成:
import java.util.Objects;public class Person {private int age;private String name;private static String key = "key";private final String finalField = "FINALFIELD";public Person() {}public int getAge() {return this.age;}public String getName() {return this.name;}public String getFinalField() {Objects.requireNonNull(this);return "FINALFIELD";}public void setAge(final int age) {this.age = age;}public void setName(final String name) {this.name = name;}public boolean equals(final 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 if (this.getAge() != other.getAge()) {return false;} else {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;}Object this$finalField = this.getFinalField();Object other$finalField = other.getFinalField();if (this$finalField == null) {if (other$finalField != null) {return false;}} else if (!this$finalField.equals(other$finalField)) {return false;}return true;}}}protected boolean canEqual(final Object other) {return other instanceof Person;}public int hashCode() {int PRIME = true;int result = 1;result = result * 59 + this.getAge();Object $name = this.getName();result = result * 59 + ($name == null ? 43 : $name.hashCode());Object $finalField = this.getFinalField();result = result * 59 + ($finalField == null ? 43 : $finalField.hashCode());return result;}public String toString() {int var10000 = this.getAge();return "Person(age=" + var10000 + ", name=" + this.getName() + ", finalField=" + this.getFinalField() + ")";}
}
3.2.@Builder注解
@Builder也是一个非常常用的注解,为类提供构建器模式支持,允许链式调用。对于上面的Person定义,使用该注解之后,编译器会自动生成一个Builder类。代码大致如下:
public class Person$PersonBuilder {private int age;private String name;Person$PersonBuilder() {}public Person$PersonBuilder age(final int age) {this.age = age;return this;}public Person$PersonBuilder name(final String name) {this.name = name;return this;}public Person build() {return new Person(this.age, this.name);}public String toString() {return "Person.PersonBuilder(age=" + this.age + ", name=" + this.name + ")";}
}
如此,客户端便可已链式的方式进行创建实例,如下:
Person p = Person.builder().age(1).name("Tom").build();
3.3.日志类注解
lombok为各种常见的日志框架提供简易注解,有如下几个注解。
需要注意的是,
log注解只能用于类,接口,enum,或者record等。
注解只提供代码上的引用,项目仍然需要引入相关的依赖及配置才能使日志生效。
| 注解名称 | 作用 |
@Log | jdk官方log接口 |
@Log4j | log4j日志接口 |
| @Log4j2 | log4j2日志接口 |
| @Slf4j | Slf4j日志门面接口 |
下面以@slf4j注解进行演示说明
@Slf4j
public class TestLombok {public static void main(String[] args) {log.info("此处是日志");}
}
翻译的代码如下:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class TestLombok {private static final Logger log = LoggerFactory.getLogger(TestLombok.class);public TestLombok() {}public static void main(String[] args) {log.info("此处是日志");}
}
3.4.@Cleanup注解
在jdk1.7之前,对于io资源的使用,一般是使用类似的代码,需要在io流使用结束后关闭资源。
import java.io.FileInputStream;public class Test {public static void main(String[] args) {FileInputStream fis = null;try {fis = new FileInputStream("input.txt");} catch (Exception e) {} finally {if (fis != null) {try {fis.close();} catch (Exception ignore) {}}}}
}
jdk1.7引入了try-with-resources语句块,可以自动处理单个或多个资源的关闭。类似下面的语法。
public static void main(String[] args) {try ( FileInputStream fis = new FileInputStream("input.txt")){// do sth} catch (Exception ignored) {} }
@Cleanup做的也是类似的工作。个人觉得该注解比较鸡肋,因为jdk已经有官方的实现了。
源代码如下:
import lombok.Cleanup;import java.io.FileInputStream;public class Test {public static void main(String[] args) throws Exception {@Cleanup var file1 = new FileInputStream("input.txt");@Cleanup var file2 = new FileInputStream("input.txt");@Cleanup var file3 = new FileInputStream("input.txt");}
}
翻译的代码如下:
import java.io.FileInputStream;
import java.util.Collections;public class Test {public Test() {}public static void main(String[] args) throws Exception {FileInputStream file1 = new FileInputStream("input.txt");try {FileInputStream file2 = new FileInputStream("input.txt");try {FileInputStream file3 = new FileInputStream("input.txt");if (Collections.singletonList(file3).get(0) != null) {file3.close();}} finally {if (Collections.singletonList(file2).get(0) != null) {file2.close();}}} finally {if (Collections.singletonList(file1).get(0) != null) {file1.close();}}}
}
当然,lombok还有其他很多注解,但可能使用频率没那么广泛,感兴趣的读者可以自行查阅。
相关文章:
Lombok一文通
1、Lombok简介 作为java的忠实粉丝,但也不得不承认,java是一门比较啰嗦的语言,很多代码的编写远不如其他静态语言方便,更别说跟脚本语言比较了。 因此,lombok应运而生。 Lombok是一种工具库,它提供了一组…...
Seq2Seq模型:详述其发展历程、深远影响与结构深度剖析
Seq2Seq(Sequence-to-Sequence)模型是一种深度学习架构,专为处理从一个输入序列到一个输出序列的映射任务设计。这种模型最初应用于机器翻译任务,但因其灵活性和有效性,现已被广泛应用于自然语言处理(NLP&a…...
公网如何访问内网?
公网和内网已经成为我们生活中不可或缺的存在。由于内网的安全性考虑,公网无法直接访问内网资源。如何实现公网访问内网呢?本文将介绍一种名为【天联】的私有通道技术,通过安全加密,保障数据传输的安全性。 【天联】私有通道技术 …...
手机定制开发_基于天玑900的5G安卓手机定制方案
手机定制方案基于联发科天玑900强劲旗舰八核2.4GHz处理器。这款处理器采用了6nm先进制程工艺,为用户带来了痛快淋漓的性能体验。不论是进行游戏还是日常娱乐,用户都能轻松驾驭。手机搭载了最新的Android 13操作系统,提高了数据读取的准确性&a…...
免费,C++蓝桥杯等级考试真题--第2级
C蓝桥杯等级考试真题–第2级...
panic 、asset、crash 的含义和区别
在编程中,“panic” 和 “assert” 都是用于处理错误和异常情况的机制,但在不同的编程语言和框架中有一些区别。 panic: 含义:通常表示程序发生了无法恢复的错误或异常情况,需要立即终止程序的执行。 用法࿱…...
解决Windows 10通过SSH连接Ubuntu 20.04时的“Permission Denied”错误
在使用SSH连接远程服务器时,我们经常可能遇到各种连接错误,其中“Permission denied, please try again”是较为常见的一种。本文将分享一次实际案例的解决过程,帮助你理解如何排查并解决这类问题。 问题描述 在尝试从Windows 10系统通过SS…...
Windows 下 PostgreSQL 图形化界面安装、配置详解
相信大家对PostgreSQL都不陌生吧,自从MySQL被Oracle所控制后,PostgreSQL就成为了国内去O的首选数据库了,并且PostgreSQL目前不受任何商业公司控制,所以国内很多厂商都是基于PostgreSQL做二次开发来实现数据库自主可控的目标(国内很…...
曾巩,散文的艺术与哲思
曾巩,字子固,世称南丰先生,南丰(今江西)人,生于北宋真宗天禧三年(公元1019年),卒于北宋元丰六年(公元1083年),享年64岁。他是中国北宋…...
【SpringBoot】怎么在一个大的SpringBoot项目中创建多个小的SpringBoot项目,从而形成子父依赖
父子项目工程创建 步骤 先创建父项目 具体操作步骤请看本文章:使用maven工程创建spring boot项目 创建子项目 file- project structure module–new module 剩下步骤请看创建父工程时的操作使用maven工程创建spring boot项目 应用 确认即可 之后创建启动类…...
vue3组件通信与props
title: vue3组件通信与props date: 2024/5/31 下午9:00:57 updated: 2024/5/31 下午9:00:57 categories: 前端开发 tags: Vue3组件Props详解生命周期数据通信模板语法Composition API单向数据流 Vue 3 组件基础 在 Vue 3 中,组件是构建用户界面的基本单位&#…...
并发和异步编程:详细概述
01 Concurrency and Asynchronous Programming: a Detailed Overview 并发和异步编程:详细概述 Asynchronous programming is one of those topics many programmers find confusing. You come to the point when you think you’ve got it, only to later realize that the …...
交易员摩拳擦掌,就在今年夏天,极端气候引爆商品?
有史以来最严重的高温炙烤下,从农业到能源到航运都可能受到严重负面影响,大宗商品市场波动将大幅加剧。 2024年有望成为有史以来最炎热的一年,随着北半球步入夏季,世界各地都将遭受由全球变暖造成的极端高温困扰。极端天气不仅给民…...
数据结构学习笔记
1. 数组 (Array) 定义 数组是一种线性数据结构,用于存储固定大小的相同类型元素集合。每个元素都有一个索引,用于快速访问。 特点 优点:访问速度快,通过索引直接访问O(1)时间复杂度。缺点:大小固定,插入…...
vscode导入自定义模块报错ModuleNotFoundError解决方案
问题描述 我的项目为great_gas_or_agents,目录结构如下: log_data_extract main.py math_algorithm 现在我运行main.py,报错:from math_algorithm.utils import parse_month_match_request,ModuleNotFoundError: No …...
go mod包管理与应用,常见错误排查方法
go mod包管理 go 中 包管理使用go mod 进行包管理 go mod init 项目名称 go mod init myproject_go生成的go.mod中有 module myproject_go 创建目录go_service 其下有两个go文件,go_request.go go_write.go . 根目录下有main.go入口文件。于是项目结构类似于&…...
数据结构作业
第1章 绪论 单选题 数据在计算机的存储器中表示时,逻辑上相邻的两个元素对应的物理地址也是相邻的,这种存储结构称之为________。 B. 顺序存储结构 算法指的是________。 D. 求解特定问题的指令有限序列 下面程序段的时间复杂度为:_______…...
项目纪实 | 版本升级操作get!GreatDB分布式升级过程详解
某客户项目现场,因其业务系统要用到数据库新版本中的功能特性,因此考虑升级现有数据库版本。在升级之前,万里数据库项目团队帮助客户在本地测试环境构造了相同的基础版本,导入部分生产数据,尽量复刻生产环境进行升级&a…...
富格林:应用正规技巧阻挠被骗
富格林悉知,随着如今入市现货黄金的朋友愈来愈多,不少投资者也慢慢开始重视起提高自身的正规投资技巧,希望能阻挠被骗更高效地在市场上获利。虽然目前黄金市场存在一定的受害风险,但只要投资者严格按照正规的交易规则来做单&#…...
【模型架构】学习RNN、LSTM、TextCNN和Transformer以及PyTorch代码实现
一、前言 在自然语言处理(NLP)领域,模型架构的不断发展极大地推动了技术的进步。从早期的循环神经网络(RNN)到长短期记忆网络(LSTM)、Transformer再到当下火热的Mamba(放在下一节&a…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...
从WWDC看苹果产品发展的规律
WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...
Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...
《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)
笔记整理:刘治强,浙江大学硕士生,研究方向为知识图谱表示学习,大语言模型 论文链接:http://arxiv.org/abs/2407.16127 发表会议:ISWC 2024 1. 动机 传统的知识图谱补全(KGC)模型通过…...
企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...
