大模型小助手,工程师如何拥有自己的人工智能

前言 历史的车轮滚滚向前,大模型的发展让 AI 离每个人都更近了一步。今年 3 月的时候简单聊了一下 AIGC,现如今半年多过去了,ChatGPT 依旧大放异彩。无论是百度的文心一言还是阿里的通义千问,在 GPTs 面前都变成了拙劣的模仿。 现如今每隔几天就有新鲜的技术出炉,让人目不暇接,同时具备可玩性和想象空间的各种应用和开源库,仿佛让自己回到了第一次设置 JAVA_HOME 的日子,于是我便蹦出了一个对自己的工作和生活可能有帮助的想法——“拥有自己的人工智能”。 目标是搭建出一个不依赖云端服务,可以在本地运行,且效果可以接受的类 ChatGPT 服务。为什么要在本地搭建而不是直接采用现成的云服务呢?从数据安全的角度看,一些数据还是不太方便随意上传至云端的,而且云端的问题回答也会经过各个服务商审核,不可避免的会出现降智的情况。另一方面,这些在线的云服务成本较高,chatGPT plus 每个月 20 美元,还要熟练掌握各种上网技巧,虽然能力很强大,但是再没有一个完美的变现渠道的情况下,对于我的荷包来说还是有很大负担。最后对于一个工程师来说,能自己搭建一个完整的方案,使用一个自己调教出来的 AI,也是出于对技术探索的本能使然。 方案概述 由于是在本地运行的,选择一个好的设备则是第一步要考虑的事情了。笔者家中刚好有一台 2020 年 M1 芯片的 Mac Mini,一直作为家里的 homelab 长期运行。除了平时在家里用他编译代码、还会用它来设置苹果的内容缓存,提高局域网内苹果服务的连通性。如今廉颇尚未老矣,还是可以再战 AI 的。 由于 M1 芯片的统一内存架构和开源社区对 Apple 芯片在 AI 方面的支持, 如今用它作为一个本地运行大模型的载体再合适不过了。 我的方案如下图所示 👇。 在这套方案中,我采用实力排上游、并且在使用上对学术和商业都友好的国产大模型 ChatGLM3-6B 对话模型,同时使用 chatglm.cpp 对 ChatGLM3-6B 进行量化加速以及 API 协议的兼容;通过 Cloudflare 的 Tunnels 将运行在家中 HomeLab 的 service 映射至公网;使用 ChatGPT Next Web 作为 UI 层并通过 Vercel 托管网页(当然这里也可以选择下载 ChatGPT Next Web 的 desktop App 直接使用)。最后的效果如下图所示。...

plainify

设计模式解码:软件工程架构的航标

引言 软件工程领域的设计模式,就像是建筑师手中的设计蓝图,它们是经验的总结,指导开发者如何在面对层出不穷的编程难题时,构建出既稳固又灵活的软件结构。就像一座经过精心设计的大厦能够经受住风雨的考验一样,一个利用了恰当设计模式的软件系统也能在快速变化的技术世界中稳定运行。它们是从无数成功(和失败)的项目中提炼出来的知识精华,为软件开发者提供了一套通用的、可复用的解决方案框架。 这些模式不仅仅是静态的原则或者是一成不变的规则,它们更像是一种语言,让开发者之间能够有共同的理解和沟通基础。在这个基础上,设计模式允许团队成员更高效地协作,对现有问题提出经过验证的解决方案,同时也能够预见和规避潜在的设计陷阱。通过实施这些模式,开发者能够减少冗余,提升系统的模块化,从而使得代码更加清晰、扩展性更强,同时也更容易被后来者理解和维护。 在本文中,我们将探索设计模式的分类,它们在软件开发中的应用,并深入讨论在特定情境下选择和实施这些模式的最佳实践。通过具体的例子和场景分析,我们能够更好地理解设计模式在现代软件工程中的作用,以及如何运用这些模式来构建出既强大又优雅的代码结构。 设计模式的分类及应用 在软件工程的广阔舞台上,设计模式被分为三个主要类别,每个类别都解决一系列特定的问题,它们如同不同类型的工具,针对特定的工作选择合适的工具至关重要。 创建型模式,例如**单例和工厂方法**,主要关注对象的创建机制,以确保对于一个特定类而言,系统中只存在一个实例,或者将对象的创建和使用解耦,以增强系统的灵活性和可扩展性。在实际应用中,当我们需要控制客户如何创建一个对象,或者当我们知道对象的创建过程需要大量配置时,这些模式就显得尤为重要。例如,一个复杂的游戏中的资源管理器可能就会采用单例模式来确保所有的游戏组件都能够访问全局的、唯一的资源实例。 结构型模式,如**适配器和代理模式**,帮助设计系统中各个部件之间的组织方式,确保当系统的一部分发生变化时,不会影响到整个系统的功能。它们通过确保每个部分都能够独立地工作,来提高系统的整体灵活性。比如,在一个视频流服务中,适配器模式可以用来确保新的视频编码格式能够被现有的播放器支持,而不必对播放器进行大规模的重写。 行为型模式,例如**观察者和策略模式**,主要关注对象之间的交互和职责分配。这些模式不仅帮助定义对象间的通信模式,而且也使得系统更易于理解和扩展。观察者模式允许对象在无需知道其他对象具体实现的情况下,依旧能够相互通信,这在构建用户界面组件时尤其有用,其中一个动作可能需要更新多个界面元素。 通过深入分析这些模式,我们能够更好地理解它们在软件开发中的价值,以及如何将这些理论应用到实际开发的项目中。这不仅仅是一个理论上的练习;通过具体的代码示例和场景分析,我们将展示这些模式如何帮助开发团队构建更健壮、更可维护、更高效的软件系统。 设计模式的好处与挑战 设计模式的引入往往能够带来显著的好处。首先,它们提供了一种重用解决方案的方式,这可以节省时间和资源,并减少错误。例如,使用工厂模式可以创建一个集中的创建点,允许开发者轻松调整和维护创建逻辑,而无需遍布代码库的重复代码。同样,装饰器模式允许开发者扩展对象的行为,而无需修改现有类的代码,这是增强功能时尊重开闭原则的典范。 然而,设计模式的使用并非没有挑战。过度使用或不恰当使用设计模式可能会导致系统过于复杂,难以理解和维护。比如,一个简单的问题如果使用了一个复杂的模式来解决,可能会引入不必要的抽象层,从而导致代码的可读性和可维护性降低。因此,软件工程师必须具备判断何时使用设计模式的智慧,并且能够根据项目的具体需求和上下文来选择合适的模式。 此外,设计模式的实施需要团队成员有共同的理解,这意味着必须有一个良好的沟通和文档化过程。团队中的每个成员都需要理解这些模式的目的和实现方式,这样才能确保模式被正确地应用,并且整个团队能够有效地协作。 总结而言,设计模式在软件工程中的应用是一个平衡艺术。它们在提升代码质量、促进团队协作和增强软件的可持续发展方面发挥着关键作用。软件工程师需要不断地学习和实践,以便能够熟练地运用这些模式来解决日常开发中遇到的问题。通过理解设计模式的原理和适用场景,我们可以更加明智地选择何时以及如何使用它们,从而构建出更加健壮和可维护的软件系统。 设计模式的实际案例和代码示例 设计模式不仅在理论上具有吸引力,它们在实际应用中同样展现出巨大价值。例如,考虑一个电子商务平台,其中的购物车功能可以通过“单例模式”实现,以保证每个用户在浏览过程中都有一个且只有一个购物车实例。以下是一个简化的单例模式代码示例,用于创建一个购物车实例: public class ShoppingCart { private static ShoppingCart instance; private ShoppingCart() { // Private constructor to prevent instantiation. } public static ShoppingCart getInstance() { if (instance == null) { instance = new ShoppingCart(); } return instance; } } // Usage public class Main { public static void main(String[] args) { ShoppingCart cart1 = ShoppingCart....

plainify

【译】数据库是如何分片的?

本文翻译自 How does database sharding work?,如有疑问,请联系译者 了解什么是数据库分片,分片如何工作的,以及一些常见的分片框架和工具。 如果你使用过 Google 或 YouTube,那么你很可能已经访问过分片数据。分片通过将数据分区存储在多个服务器上,而不是将所有内容放在一个巨大的服务器上,以实现扩展数据库的目的。这篇文章将介绍数据库分片的工作原理、思考如何给你自己的数据库分片,以及其他一些有用的、可以提供帮助的工具,尤其是针对 MySQL 和 Postgres。 分片是扩展关系数据库的重要方式 试想以下场景:本季度你第三次扩大了 MySQL 版本 RDS 的实例规模,而 CFO 刚刚在会上上花了 30 分钟来“讨论预算”。也许是时候横向扩展而不是纵向扩展了! [1] RDS 中的读取副本似乎很简单,但读取数据只是问题的一半。一个心力憔悴的开发者该怎么办? 分片——这个术语可能最初来自视频游戏——一种扩展关系数据库的方式。你可能以前看过这张表格,这张表描述的是如何通过横向扩展来帮助你处理存储在单个服务器上的用户表: user_id first_name last_name email … ZpaDr20TTD4ZL7Wma Peter Gibbons peter@initech.net … bI32htQ1PsEQioC7G Bill Lumbergh bill@initech.net … 99J3x257SGP7J4IkF Milton Waddams stapler@initech.net … 0SH0pyi9bO5RM4I03 Lawrence two@onetime.com … … … … … 并将其转换为存储在 2 个(或 1,000 个)服务器上的用户表:...

plainify

聊一下AIGC

“UGC 不存在了”——借鉴自《三体》 ChatGPT 的横空出世将一个全新的概念推上风口——AIGC( AI Generated Content)。 GC 即创作内容(Generated Content),和传统的 UGC、PGC,OGC 不同的是,AIGC 的创作主体由人变成了人工智能。 xGC PGC:Professionally Generated Content,专业生产内容 UGC:User Generated Content,用户生产内容 OGC:Occupationally Generated Content,品牌生产内容。 AI 可以 Generate 哪些 Content? 作为淘宝内容线的开发,我们每天都在和内容打交道,那么 AI 到底能生成什么内容? 围绕着不同形式的内容生产,AIGC 大致分为以下几个领域: 文本生成 基于 NLP 的文本内容生成根据使用场景可分为非交互式文本生成与交互式文本生成。 非交互式文本生成包括摘要/标题生成、文本风格迁移、文章生成、图像生成文本等。 交互式文本生成主要包括聊天机器人、文本交互游戏等。 【代表性产品或模型】:JasperAI、copy.AI、ChatGPT、Bard、AI dungeon 等。 图像生成 图像生成根据使用场可分为图像编辑修改与图像自主生成。 图像编辑修改可应用于图像超分、图像修复、人脸替换、图像去水印、图像背景去除等。 图像自主生成包括端到端的生成,如真实图像生成卡通图像、参照图像生成绘画图像、真实图像生成素描图像、文本生成图像等。 【代表性产品或模型】:EditGAN,Deepfake,DALL-E、MidJourney、Stable Diffusion,文心一格等。 音频生成 音频生成技术较为成熟,在 C 端产品中也较为常见,如语音克隆,将人声 1 替换为人声 2。还可应用于文本生成特定场景语音,如数字人播报、语音客服等。此外,可基于文本描述、图片内容理解生成场景化音频、乐曲等。 【代表性产品或模型】:DeepMusic、WaveNet、Deep Voice、MusicAutoBot 等。 视频生成 视频生成与图像生成在原理上相似,主要分为视频编辑与视频自主生成。...

2023-03-21 627 words 3 min
plainify

三行代码让你的git记录保持整洁

前言 笔者最近在主导一个项目的架构迁移工作,由于迁移项目的历史包袱较重,人员合作较多,在迁移过程中免不了进行多分支、多次 commit 的情况,时间一长,git 的提交记录便混乱不堪,随便截一个图形化的 git 提交历史给大家感受一下。 各种分支疯狂打架宛如后宫争宠的妃子们,之所以会出现这种情况,主要还是因为滥用 git merge 命令并且不考虑后续的理解成本导致的。如今在大厂工作的程序员们,频繁接受变更的需求,一旦一开始考虑不周到,就一定会出现了大量无意义的 commit log,加上“敏捷”理念的推广,产品的快速迭代上线变成了核心指标,这些无意义的 commit log 便被“下次再处理”,久而久之就混乱不堪了。 而我们在看一些开源仓库时,会发现他们的 commit 记录十分整洁,其实这并不是社区的程序员能力更强,而是因为他们没有 KPI 大棒的鞭笞,在提交代码前会花时间整理自己的 commit log。而这就是本文的主角了——“Git Rebase”。 git rebase 和 git merge git rebase,中文翻译为“变基”,通常用于分支合并。既然提到了分支合并,那就一定离不开git merge这个命令。 相信每个新手程序员刚进入职场的时候,都会听到“xxx 你把这个分支 merge 一下”这样的话。那么问题来了,假如你有 6 个程序员一起工作, 你就会有 6 个程序员的分支, 如果你使用 merge, 你的代码历史树就会有六个 branch 跟这个主的 branch 交织在一起。 上图是 git merge 操作的流程示意图,Merge 命令会保留所有 commit 的历史时间。每个人对代码的提交是各式各样的。尽管这些时间对于程序本身并没有任何意义。但是 merge 的命令初衷就是为了保留这些时间不被修改。于是也就形成了以 merge 时间为基准的网状历史结构。每个分支上都会继续保留各自的代码记录,主分支上只保留 merge 的历史记录。子分支随时都有可能被删除。子分子删除以后,你能够看到的记录也就是,merge 某 branch 到某 branch 上了。这个历史记录描述基本上是没有意义的。 而 git rebase 中文翻译为“变基”,变得这个基指的是基准。如何理解这个基准呢?我们看一下下图。...

2023-02-25 252 words 2 min
plainify

如何设计一个消息中心

如今的内容型产品,不管提供的是什么类型的内容,在其主功能之外,不可避免的会有另一个十分重要的功能——消息中心。 而无论是信息流、论坛、信箱,还是私聊、群聊、通知,推拉模型是内容型(包括:社交型)产品架构的核心。做出正确选择的关键在于对产品形态和系统组件清晰的认识。 今天我们将重心放在消息中心上,聊一聊如何设计一个消息中心。 需求分析 消息中心通常会有两个功能(如下图所示): 用户通知(点赞、评论、关注、@等) 官方通知 接下来我们将会对这两类通知进行一个简单的抽象。 首先,可以确定的是,对于用户通知,每个用户都不一样(我的点赞列表和你的点赞列表肯定是不一样的),因此对于每个人我们都需要维护一个「收件箱」。 当 A 点赞了 B 的内容,后端系统在收到了这一个点赞消息后,会将点赞信息写入 B 的 「收件箱」,并标明这是 A 在 xxx 时点赞的 xxx 内容。这是一个系统将消息 推送 给 B 的过程。 而对于官方通知,每个人(几乎)都是一样的(用户有可能设置了屏蔽,系统也可能指定了发送人群),并且官方通知是由系统自然下发的,因此对于系统来说需要维护一个系统**「发件箱」**。 发件箱维护了官方想给用户的通知,每次打开消息中心时,用户都会主动来系统**「拉取」官方最新的消息,并和用户自己的「收件箱」里的官方通知进行比较,以确认是否已读该条通知。这是一个用户主动从系统「拉取」**通知的过程。 推拉模型 其实到这里就已经点出了这两个场景背后的一套模型——推拉模型。而之所以在这两种场景选择不同的运行机制,其实背后牵扯到的是读写扩散的问题。 推模型 先看推模型,对于任何一个内容创作者来说,最开心的事情莫过于打开软件会有一堆点赞/评论的小红点。对于大 V 来说,打开 App 查看点赞消息的频率根本比不过别人给你点赞的频率,这是一个很典型的读少写多的场景。每当有一个用户点赞该大 V 时,都会将索引信息(一般为内容 ID、类型、发表时间等索引数据)写到用户的收件箱中。 优点:读很轻。仅需要读取消息列表即可。 缺点:写很重。一旦用户的内容质量很高,可能会收到大量的点赞/评论,会有大量的写入操作。 拉模型 再看拉模型,以官方通知为例,一般官方通知是由运营人员发布的,一个月可能也不会有几条,但是每次用户进入 App 时都会看看是否有新的官方通知进来,这是一个很典型的读多写少的场景。 优点:写很轻,节省空间。系统只需维护一个属于自己的消息列表即可。 缺点:读很重,计算量大。假设可以发送官方通知的生产者较多(例如淘宝里的一系列官方业务),则每次都需要从这些消息生产者里拉取最新的内容。 流程设计 用户通知 对于用户通知,流程设计如下: 对于该流程,有几点需要注意的: 异步发送 当用户出发了点赞/关注/评论行为时,被点赞/评论/关注的用户,其实不需要立即感知,因此也不需要立即将互动信息写入该用户的收件箱中,因此可以考虑以消息队列的方式通知出去,缓解系统压力。 缓存前置 写入消息时,如果直接写入用户收件箱,可能会导致用户在请求消息列表时,将请求全部打到 DB,造成系统故障,因此通常会在更新用户收件箱时双写用户缓存。 官方通知 相较于用户通知,官方通知由于引入官方运营这一角色,操作上会稍微复杂一些(如上图所示),因此整个系统的设计也会稍微复杂一些。...

plainify

浅谈策略模式在消息转发场景下的应用

背景 在上一篇文章中,我们介绍了如何设计一个消息中心,传送门 👉《如何设计一个消息中心》 有了承载这些消息的地方后,接下来的问题便是,这些消息从哪里来? 通常对于一个内容型产品来说,在其互动体系中,为了增强消息的用户触达,增强用户的互动心智,在互动(评论、点赞等)行为发生后,会将互动消息推送至消息中心,然后根据不同的互动行为类型匹配不同的消息模版。 然而随着互动行为种类的增加(内容的点赞、评论的点赞……),不断的通过 if…else 来根据不同的消息类型生成不同的消息模版会使得业务代码愈发复杂,难以维护。 不仅如此,一旦需要增加一种新的互动消息时,需要对原有代码进行破坏性修改,违背了“开闭原则”。因此有必要对互动行为消息转发至消息中心这一场景进行抽象,让后续的维护者、建设者只需要关心某一特定的互动行为消息即可(我可不想未来被别人喷在 💩 山上拉 💩)。 策略模式 在说明具体的实现方案前,我们先介绍一个设计模式——策略模式。 策略模式,英文全称是 Strategy Design Pattern。在 GoF 的《设计模式》一书中,它是这样定义的: Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it. 翻译成中文就是:定义一簇算法类,将每个算法分别封装起来,让它们可以互相替换。策略模式可以使算法的变化独立于使用它们的客户端(这里的客户端代指使用算法的代码)。 策略模式用来解耦策略的定义、创建、使用。实际上,一个完整的策略模式就是由这三个部分组成的。 策略类的定义比较简单,包含一个策略接口和一组实现这个接口的策略类。 策略的创建由工厂类来完成,封装策略创建的细节。 策略模式包含一组策略可选,客户端代码如何选择使用哪个策略,有两种确定方法:编译时静态确定和运行时动态确定。其中,“运行时动态确定”才是策略模式最典型的应用场景。 实现方案 在对策略模式有了基本的了解后,我们尝试在本节将其运用起来。 仔细分析了第一章的应用场景后我们发现其实实现链路并不复杂,整体流程如下图所示: 在本例中,根据不同的互动行为类型,我们将点赞消息和评论消息分成以下几类: 点赞类: 内容点赞 评论点赞 评论类: 内容评论 内容评论的回复 转发策略的定义 整个方案中最重要的一环是对转发策略的匹配,因此第一步我们要做的应该是定义一个策略。为了方便后续的扩展(未来可能会有多种转发策略),我们此处定义一个策略接口MsgTransmitStrategy。...

plainify

数据检索的玄铁剑——索引

从搬运 DTO 到 CRUD 在如今的开发模式下,服务端程序员离原始数据越来越远,和农夫山泉一样,他们不生产数据,他们只是 DTO 的搬运工。从各种 service 中获取数据,再使用 Lambda 进行拆分组装成为了他们的日常工作。 然而,随着各家大厂都开始“降本增效”,DTO 的搬运工越来越不具备竞争力,“技多不压身”变成了下一阶段的 OKR,于是「CRUD 工程师」便“应运而生”了。 本文的内容便是围绕着 CRUD 中的 R(ead)展开的。 数据检索的玄铁剑——索引 在现实生活中,如果你想使用新华字典查询一个字,在没有背下来具体页码的情况下,第一步多半是打开目录,根据拼音首字母快速的锁定目标数据所在的位置范围。如下图 👇 索引究竟是什么? 百度百科是从数据库的角度出发给出了一个索引的定义,维基百科也并没有为 CS 中的索引做一个概述,而是细分了多个领域来介绍 👉https://en.wikipedia.org/wiki/Index 本质上,索引是一种用于提高数据检索效率的技术,它可以是一种复杂的数据结构(Hash,B Tree……),也可以就是一个简单的下标。 为了更好的理解索引,先看一下没有索引的查询是什么样的? 没有索引的查询 上班路上,你和一个长发姐姐擦肩而过,来到公司,惊喜的发现她竟然也在这栋楼上班,此时电梯停在了 3 楼,小姐姐出去之后你便继续乘到了 6 楼,虽然你一直写着代码,但此时你的心早已飞去了三楼。 OK,那么问题来了,如果你想再见到那个长发姐姐,第一想法是什么?一定不是发表白墙吧。 在纠结了半天之后,最后你还是选择了最原始但也是最简单的办法,去三楼的工位一个个找。 你一边遍历着所有工位上的人,一边幻想着等再见面时的场景。终于皇天不负苦心人,在你离她还有六个工位的时候,你见到了她。就在你以为终于能发出“一起喝咖啡”的邀约时,一位靓仔从你的后面“瞬移”到她面前,然后说出了那句“有时间一起喝咖啡吗?”。 事了拂尘去,靓仔最后回头看了你一眼,然后说到:“小伙子,爷有索引”。 微观视角的索引——什么才是有意义的索引 上面这个例子就是一个很典型的场景,在没有索引的情况下,查询就变得简单粗暴——全表扫描。查询耗时完全由数据量决定,海量数据的查询基本无法满足需求。 由于遍历的时间复杂度是 O(n),那么为了让索引变得有意义,其时间复杂度必定是小于 O(n)。 常刷算法题的小伙伴们都知道,经常出现查找的两类数据结构就是数组和树,其实也对应着两种最常见的索引。 哈希索引:复杂度为 O(1) 树索引:复杂度为 O(log n) 哈希索引原理是根据属性组合直接通过哈希函数计算出结果数据的地址,一般来说更快(包括建索引的效率和查询效率),具体性能依赖于数据集和哈希函数的匹配程度。 树索引原理是基于属性组合建立树再根据二分查找定位数据,虽然建索引和查找速度都慢一些,但优势是可以支持范围查询和 front-n 属性匹配(前缀匹配)的查询。其中 front-n 属性的查询意思是,属性组合中的前 1 到前 n 个属性组成的子组合的查找。例如属性组合是 A-B-C,那么树索引可以支持 A、A-B、A-B-C 三个属性组合的查找。...

2022-07-30 97 words 1 min
plainify

LowCode窥探

前言 本文是笔者在团队内部做分享整理的资料的一部分,本次分享主要是站在一个服务端开发的视角对(前端)低代码平台的一些调研,已经剔除了一些敏感数据和信息,可放心食用。 太阳底下无新事 Dreamweaver -> Low-Code 将时钟拨回到 20 年前,那个时候的开发者对于 html/css/js 还处在望而生畏的阶段,Dreamweaver 的出现仿佛让他们看见了曙光。通过简单的拖拽就可以实时预览编排的页面,点击按钮就可以自动生成对应的前端代码,配置好机器信息就可以一键部署访问…… 这些特性让无数开发者趋之若鹜,然后现在他的境况 现如今,Dreamweaver 几乎已经退出了历史舞台,但 Low-Code 似乎又有卷土重来的迹象…… Low-Code 是什么? A low-code development platform (LCDP) is software that provides a development environment used to create application software through graphical user interfaces and configuration instead of traditional hand-coded computer programming. 一句话概括就是 👉 用 GUI+配置取代传统手工编码 技术上,实现低代码平台的关键要素是模型驱动设计、代码自动生成和可视化编程,通过这些手段来隐藏下层的代码细节。 更激进的—— No-Code(零代码) Low-Code 更激进的演进方向是 No-Code,主要的差异点如下: 平台用户:任何业务人员都能使用无代码平台,而低代码平台面向开发者(尽管专业要求不那么高) 核心设计:无代码平台倾向于让用户通过拖拽或简单的表达式来操纵完成应用设计,而低代码平台更倾向于通过人工编码来指定应用程序的核心结构 用户界面:无代码平台为了简化应用设计,一般只支持内置的 UI 库,而低代码平台可能会提供更灵活的 UI 选项,但代价是需要额外编码,使用上的复杂性有所增加...

plainify

Lombok原理探析

前言 对于一个 Java 开发者来说,Lombok 应该是使用最多的插件之一了,他提供了一系列注解来帮助我们减轻对重复代码的编写,例如实体类中大量的 setter,getter 方法,各种 IO 流等资源的关闭、try…catch…finally 模版等,虽然可以通过 IDE 的快捷帮我们生成这些方法,但这些冗长的代码仍会影响代码的简洁性与可阅读性。 如今,随着使用者数量越来越多,Lombok 甚至成为 IDEA 的内置插件了(2020.3 版本+),可见其影响力。 但不知道使用 Lombok 的你,是否思考过这种自动生成代码究竟是什么原理呢?🤔 这些代码又是怎么产生的呢?而这就是本文要展开介绍的内容了。 Lombok 的安装与使用网络上相关的介绍已经很多了,这里就不多说了,自行查阅相关资料即可。 注解解析的两种方式 关于注解,我在之前的文章里有过详细的介绍,在解释 Lombok 的原理之前,推荐你先阅读 👉《给编译器看的注释——「注解」》 这里主要回顾一下 Lombok 注解的解析方式。 运行时解析 这是最常见的注解解析的方式,运行时能够解析的注解,必须将@Retention设置为RUNTIME,这样就可以通过反射拿到该注解。 例如使用最多的@RequestMapping注解就是一个运行时注解。 @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Mapping public @interface RequestMapping { String name() default ""; @AliasFor("path") String[] value() default {}; @AliasFor("value") String[] path() default {}; RequestMethod[] method() default {}; String[] params() default {}; String[] headers() default {}; String[] consumes() default {}; String[] produces() default {}; } 运行时注解的解析原理也很简单,在 java....