Lombok原理揭秘:javac注解处理器如何修改AST生成代码

简介:Java 开发中,注解处理器是一类特殊的编译期工具,它基于 JSR 269 规范,在 javac 解析源码生成抽象语法树(AST)后介入编译流程,通过 SPI 机制被自动发现并执行。与反射在运行时影响程序行为不同,注解处理器能在字节码生成前直接修改 AST,从而在编译期完成代码注入,兼顾运行效率与代码简洁性。这一机制广泛应用于框架开发、编译期校验和代码生成等场景。Lombok 正是典型代表,它利用 javac 注解处理链路和 TreeMaker API,在编译阶段为标注了 @Getter、@Setter 等注解的类插入方法,省去手写样板代码。深入理解该原理,不仅有助于排查“程序包 lombok 不存在”“反射拿不到注解”等工程问题,也能为自定义编译期处理器提供坚实的技术基础。

1. Lombok 的原理真相:它不是运行时魔法,而是编译期拦截器

如果你到现在还以为 lombok 软件/插件是靠反射在运行时给类补方法,那可以先把结论反过来记:Lombok 是 javac 走编译流程时的一个注解处理器,它利用 JDK 提供的注解处理机制,在源码还没变成字节码之前直接修改抽象语法树。也就是说,@Getter 在源码里是给编译器看的记号,真正干活的是后面那个处理器。这篇笔记会把 Lombok 从注解定义、处理器装配到树改造的逻辑拆开,并给出一个可以跑通的自定义处理器。适合想搞清楚“Lombok 生成的代码到底在哪生成”的工程师,也适合打算自己写注解处理器的开发者。

2. Java 编译器的注解处理链路:javac 在哪一步对 AST 动手

2.1 注解处理器的装配:SPI 与服务发现

Java 6 之后引入 JSR 269 注解处理规范,核心接口是javax.annotation.processing.Processor。javac 在编译时会通过 ServiceLoader 机制读取 classpath 或 processorpath 下的META-INF/services/javax.annotation.processing.Processor文件,把这个文件里写的全限定类名加载进来。Lombok 的 jar 里自带这条 SPI 配置,所以你只要把 lombok 放进依赖里,javac 就会自动发现它,Java 源码里不需要任何 import 语句。

process()方法的返回值是一个需要理解的细节。返回true表示“这批注解我已经消费掉了,后续处理器不能再碰”,返回false表示“我只是看了一眼,你们继续处理”。实际写处理器时这地方特别容易误用,我一般会返回false,让没被消费的注解继续往后传递。

2.2 编译阶段与处理器触发时机

javac 编译不是一步到位的。解析阶段先把 .java 文件变成 JCTree 语法树;然后是输入阶段,判断有没有新源文件要读进来;注解处理阶段启动轮次 round,每个 round 会给每个处理器一次process()调用;当某一轮没有产生新源文件时处理阶段结束;最后才做语义分析和字节码生成。Lombok 的插入点就是注解处理阶段,因为在这个阶段树已经建好并且还可以改。

编译阶段树的状态注解处理器能做什么
解析语法树刚生成,类型未绑定可以读取元素,不适合改动
注解处理(可能多轮)树可读可写,可追加新文件在这里插入方法、字段、类
分析与生成树基本冻结无法再改变结构

javac 可能跑多轮。RoundEnvironment.processingOver()返回true的那一轮表示整个处理阶段即将结束,这一轮不要再做实质操作。这个时机判断在自定义注解处理器里是一个典型的翻车点。

2.3 javac 的关键参数:processor、processorpath、proc

命令行里要让自定义处理器生效,常见做法是这样的:

javac \ -processor com.example.processor.GetterProcessor \ -processorpath ./processor-jars \ -d ./out \ ./src/**/*.java

各参数的含义分别是:-processor指定处理器全类名,多个处理器用逗号分隔;-processorpath指定处理器本身所在的 jar 或 class 目录路径,比把处理器扔进 classpath 更可控;-d是字节码输出目录。调试阶段还会用到-proc:none跳过注解处理,以及-proc:only只跑注解处理器不生成最终字节码。IDE 里的 “Enable annotation processing” 选项,本质就是把这一组参数写进编译器的配置里。

2.4 多轮次机制:rootElements 与 getElementsAnnotatedWith 的取舍

多轮次带来的一个隐藏问题是:每个 round 里能看到的东西不一样。roundEnv.getRootElements()返回当前轮次所有顶层元素,roundEnv.getElementsAnnotatedWith(某个注解)返回的是“当前轮次里可见的”被该注解标记的元素。如果某个注解出现在上一轮处理器生成的新文件里,它可能只在下一轮才可见。

所以很多自定义处理器宁可先遍历 rootElements,再递归查成员,而不是直接依赖getElementsAnnotatedWith。这个取舍在 Lombok 源码里也很明显:Lombok 的 handler 拿到的是一棵完整的编译单元树,而不是零散的注解元素。理解了这个差异,再去读 Lombok 源码就能少走不少弯路。

3. 拆解 Lombok 核心源码:注解、Handler 与 JavacAST 的协作方式

3.1 源码分层:annotation、javac、eclipse、bytecode

Lombok 源码里核心模块按平台分了几块。

目录/包职责
lombok.annotation注解定义,比如 @Getter、@Setter、@Builder
lombok.core公共基础设施,含配置加载、SPI 装配
lombok.javacjavac 平台实现,包含 JavacAST、JavacHandler
lombok.eclipseEclipse 平台的对应实现
lombok.bytecode对 .class 的后期修补,例如 @SneakyThrows 的异常表

注解本身其实只是一个带@Target和@Retention的标记接口。比如@Getter标注为@Retention(SOURCE),意思是它只在编译期有意义,不会进入 class 字节码。很多人运行期用反射去拿 Lombok 的注解拿不到,原因就在这里,不是 Lombok 失效了,而是它本来就设计成不留存。

3.2 JavacAST 与 JavacNode:树的封装

Lombok 在 javac 平台没有直接操作裸的 JCTree,而是包了一层 JavacAST。JavacAST 持有一棵JCTree.JCCompilationUnit,并把树里的每个元素映射成 JavacNode。JavacNode 包装了 JCTree 节点的类型、位置、元素对象以及父子关系。为什么要自己包一层?因为 Lombok 同时要适配 Eclipse 编译器和 javac 两套 AST 体系,抽象出 JavacAST 后,上层 handler 写一套逻辑就能在两个平台复用。

handler 的调度流程是这样的:Lombok 通过注解名定位到对应 handler,比如看到 @Builder 就找到 HandleBuilder。handler 拿到注解对应的 JavacNode 后,通过 node 取得 JCTree,再用 TreeMaker 构造新节点插入现有语法树。JavacHandlerUtil 里提供了一堆现成方法,比如判断某个方法是否已存在、查找字段、检查注解是否重复标注等。这些工具方法避免了每个 handler 都自己写树的遍历逻辑。

3.3 TreeMaker 改树的常规套路

TreeMaker 是 javac 内部用来生成语法树节点的入口。Lombok 在 handler 里最常见的操作序列是:找到类 JCTree,创建方法定义节点,创建方法体里的表达式节点,然后把新方法追加进类的成员列表。这里有一个容易忽略的点:方法定义的构造参数非常多,包括访问标志、方法名、参数列表、返回值、throws 子句、方法体等。任何一个字段传错,后续分析阶段就会报“找不到符号”。

Lombok 对 handler 的封装做了很多容错。例如加方法之前会先检查该类是否已有同名方法,避免重复生成。这个看似不起眼的防御逻辑,在大型工程里能挡掉一大批冲突问题。

3.4 一个生成 getter 的树改造路径

拿最简单的 getter 生成来说,处理逻辑大致是:解析字段名,首字母大写得到方法名;确定返回类型;构造方法体和 return 语句;插入类的成员列表。其中 boolean 字段要生成 isXxx 而不是 getXxx,这个分支在 Lombok 源码里就是一个很不起眼的 if 判断,但它决定了生成方法是 get 前缀还是 is 前缀。很多人自定义处理器时忘了这个分支,生成的代码风格就乱了。

还有一点值得注意:Lombok 对字段的访问权限、static 修饰符、泛型类型都有一整套处理规则。如果字段是List<String>,返回类型必须保留泛型信息,不能只取裸类型List。这要求 handler 操作树节点时复制完整类型表达式,而不是简单的字符串拼接。

3.5 为什么不能直接用 Filer 生成一个新类

有读者会问:注解处理器里不是可以用 Filer 生成新源文件吗,Lombok 为什么不这么做?答案是:Filer 生成的是独立类,不是给原类塞方法。Lombok 的用法是在现有类字段上加注解,调用方写的代码是new User().getName(),User 类必须本身就带着 getName 方法。只有直接修改原类的 AST,才能让生成的 getter 出现在调用方预期的类里。这也是 Lombok 实现原理与普通注解处理器最大的分界点。

4. 仿写精简版 @Getter 处理器:从零跑通全流程

4.1 工程结构与准备工作

仿写一个简化版注解处理器,先列一下文件结构。

文件作用
com/example/Getter.java注解定义
com/example/processor/GetterProcessor.java注解处理器主体
META-INF/services/javax.annotation.processing.ProcessorSPI 配置,让 javac 能发现处理器
demo/User.java验证代码

JDK 版本建议在 8 到 17 之间。javac 默认会扫描 processorpath 下的所有 SPI 配置,所以只要处理器所在目录被-processorpath指到即可,不需要额外注册。

4.2 注解定义

package com.example; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.FIELD) @Retention(RetentionPolicy.SOURCE) public @interface Getter { }

这里把@Target限定为 FIELD,表示该注解只能标注在字段上。@Retention(SOURCE)表示它不会进入字节码。如果有人把它标到类上,处理器里要加一个类型判断做保护,否则后面的类型强转会直接抛异常。

4.3 处理器核心逻辑:标准 API 版本

下面是处理器主体。这个版本只用标准 API,不依赖 javac 内部类,所以可移植性最好。

package com.example.processor; import javax.annotation.processing.*; import javax.lang.model.SourceVersion; import javax.lang.model.element.*; import javax.tools.JavaFileObject; import java.io.IOException; import java.io.Writer; import java.util.Set; @SupportedAnnotationTypes("com.example.Getter") @SupportedSourceVersion(SourceVersion.RELEASE_8) public class GetterProcessor extends AbstractProcessor { @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // 最后一轮不再做任何事,避免重复生成 if (roundEnv.processingOver()) { return false; } TypeElement getterAnno = processingEnv.getElementUtils().getTypeElement("com.example.Getter"); if (getterAnno == null) { return false; } for (Element element : roundEnv.getElementsAnnotatedWith(getterAnno)) { if (element.getKind() != ElementKind.FIELD) { continue; } String fieldName = element.getSimpleName().toString(); String typeName = element.asType().toString(); // 约定俗成:原生 boolean 生成 isXxx,其他类型生成 getXxx String methodName = typeName.equals("boolean") ? "is" + capitalize(fieldName) : "get" + capitalize(fieldName); generateHelperClass(element, fieldName, typeName, methodName); } return false; } private void generateHelperClass(Element field, String fieldName, String typeName, String methodName) { Element enclosing = field.getEnclosingElement(); if (enclosing.getKind() != ElementKind.CLASS) { return; } String pkg = processingEnv.getElementUtils() .getPackageOf(enclosing).getQualifiedName().toString(); String className = enclosing.getSimpleName().toString() + "_Getter"; String qualifiedName = pkg.isEmpty() ? className : pkg + "." + className; String src = String.format( "package %s; class %s { public %s %s() { return this.%s; } }", pkg, className, typeName, methodName, fieldName); try { JavaFileObject jfo = processingEnv.getFiler().createSourceFile(qualifiedName); try (Writer w = jfo.openWriter()) { w.write(src); } } catch (IOException e) { processingEnv.getMessager().printMessage( Kind.ERROR, "生成 getter 失败: " + e.getMessage(), field); } } private String capitalize(String s) { if (s == null || s.isEmpty()) { return s; } return Character.toUpperCase(s.charAt(0)) + s.substring(1); } }

这段代码里有几个关键点。第一,process()里的状态检查:processingOver()为 true 时直接返回,避免最后一轮把生成逻辑再跑一遍。第二,通过getElementsAnnotatedWith精确拿到标了 @Getter 的字段,省去手动递归遍历成员的过程。第三,用 Filer 创建新源文件,这是标准 API 的写法。

注意:这里生成的是独立辅助类,演示的是注解处理器链路能跑通,跟 Lombok 直接改原类 AST 不同。想给原类补方法,看下一小节的 TreeMaker 版本。

4.4 贴近 Lombok 的 TreeMaker 改造

下面这段代码展示的是给原类直接插入 getter 方法的关键片段,逻辑上更接近 Lombok 真实做法。

// 需要通过强转拿到 JavacProcessingEnvironment 和 JavacTrees JavacProcessingEnvironment env = (JavacProcessingEnvironment) processingEnv; JavacTrees trees = JavacTrees.instance(env); TreePath path = trees.getPath(field); JCTree.JCClassDecl targetClass = (JCTree.JCClassDecl) path.getLeaf(); TreeMaker make = TreeMaker.instance(env.getContext()); Names names = Names.instance(env.getContext()); JCTree.JCMethodDecl getterMethod = make.MethodDef( make.Modifiers(Flags.PUBLIC), names.fromString(methodName), returnTypeExpr, List.nil(), List.nil(), List.nil(), make.Block(0, List.of( make.Return( make.Select( make.Ident(names.fromString("this")), names.fromString(fieldName))) )), null ); // 把新方法追加到类成员列表的末尾 targetClass.defs = targetClass.defs.append(getterMethod);

这段代码的逻辑是:把字段元素转成树路径,拿到它所属的类节点;用 TreeMaker 构造一个 public 方法定义;方法体直接返回this.字段名;最后把方法挂到类节点上。MethodDef的第二个参数是方法名,第三个是返回类型表达式,后面的List.nil()分别是泛型参数、形参、异常声明。任何一项没对齐,后续编译阶段就会出符号错误。

这里用的com.sun.tools.javac.*是 JDK 内部 API。JDK 9 之后模块系统把它们封在 jdk.compiler 模块里,编译和运行的时候需要加--add-exports参数。这也是社区里很多注解处理器项目在跨 JDK 版本升级时最头疼的问题,Lombok 自身也在持续适配这块变化。

4.5 编译与验证命令

javac \ --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \ --add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED \ --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED \ -processor com.example.processor.GetterProcessor \ -processorpath ./classes \ -d ./out \ src/com/example/demo/User.java

--add-exports的作用是允许当前编译任务访问 JDK 内部 API。Lombok 官方在发布 jar 时把这部分细节封装好了,所以用户不需要自己面对这些参数。但你自己仿写时,这组参数就是绕不开的前置条件。如果编译阶段看到 IllegalAccessError,先检查是不是漏了 exports。

5. 避坑记录:Lombok 在工程里的典型翻车现场

5.1 程序包 lombok 不存在

现象:命令行执行 maven 编译时,报“程序包 lombok 不存在”,但 IDE 里编译正常。

原因:IDE 通常默认开启了注解处理,命令行构建却可能没有把 lombok 挂到注解处理器链路上。如果 lombok 依赖只配了 provided scope,且没有在 maven-compiler-plugin 里声明 processor path,命令行构建就找不到它。

解决:在 maven-compiler-plugin 里用annotationProcessorPaths显式声明:

<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <annotationProcessorPaths> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>用你当前依赖的版本号</version> </path> </annotationProcessorPaths> </configuration> </plugin>

这个配置把 lombok 从普通依赖里剥离出来,单独作为注解处理器参与编译,类路径里就不会再出现“程序包不存在”的问题。

5.2 IDE 正常、命令行编译后没有 getter

现象:IDEA 里编译运行一切正常,但用脚本 javac 编译后,反编译 class 发现没有 getter 方法,运行期调用直接 NoSuchMethodError。

原因:IDEA 默认从 classpath 里扫描所有处理器,命令行则要看-processorpath是否正确指向 lombok 所在位置。另一个常见原因是 spi 配置文件没被打进处理器 jar,javac 根本发现不了处理器。

解决:先用javac -processorpath显式指定 jar,再用javap -p检查 class 内容;如果还是没有,就解压 lombok jar 确认META-INF/services/javax.annotation.processing.Processor文件里确实包含了处理器类名。

5.3 仿写处理器时生成的方法报“找不到符号”

现象:自定义注解处理器跑完,生成的 class 里能看到方法,但编译过程中报“找不到符号”。

原因:最常见的是MethodDef里的返回类型表达式引用不正确,或者方法插入后跟已有方法产生冲突。还有一种是构造方法体时用了字符串拼接类型名,而真正需要的是编译单元里同一个类型系统的符号引用。

解决:先用javac -Xprint打印树结构,对照 Lombok 的 JavacHandlerUtil 看它怎么构造节点。重点检查方法签名里的 JCExpression 是否来自同一个编译单元,不能从其他上下文直接拿一个裸的字符串。

5.4 多轮次处理器导致重复生成

现象:自定义处理器跑完后编译报“attempting to duplicate class definition”,或者类里出现两个一模一样的方法。

原因:处理器没有判断roundEnv.processingOver(),最后一轮又把生成逻辑执行了一遍。另一个可能是在 process 方法里返回了 true,导致本应传递给他人的注解被自己重复消费。

解决:process 方法最前面先判断 processingOver,true 就直接返回 false;业务处理结束后也返回 false,让注解继续传递。这样既避免重复生成,也避免误伤其他处理器。

5.5 反射拿不到 Lombok 注解

现象:运行期通过field.getAnnotation(Getter.class)判断字段是否存在 @Getter,结果全是 null。

原因:Lombok 的注解基本都是@Retention(SOURCE),编译后不会写进 class 文件的注解区。运行期反射从 class 字节码里找注解,自然找不到。

解决:如果运行期真的需要感知某个注解存在,不要复用 Lombok 的注解,自己定义一个@Retention(RUNTIME)的注解,然后在编译期处理器里把 Lombok 注解的信息转写到运行时注解上,或者生成一份常量索引类。这个思路也是很多框架做编译期元数据收集的常见做法。

6. 进阶核对:用三个验证手段确认生成代码确实存在

装配一个自定义 Lombok 风格处理器之后,光看编译通过还不够,怎么确认生成代码存在也值得核一遍。

第一个手段是-XD-printflat,它会打印经过完整注解处理流程后的源码,生成的中间文件里带_前缀。命令行里试一下:

javac -XD-printflat -processorpath ./processor-jars -d ./flat ./src/**/*.java

在输出目录里找到 User.java 对应的 flat 文件,里面能看到处理器生成的 getter 方法是否被并入了原类。这个手段对调试 TreeMaker 插入逻辑特别有用,能直观看到树改造的结果。

第二个手段是 javap 反编译字节码:

javap -p -c out/com/example/demo/User.class

-p显示所有成员,-c显示每个方法的字节码。如果看到对应的 getXxx 方法,说明生成逻辑真的跑通了;如果方法缺失,回去检查处理器是否被加载,或者 SPI 配置是否写对。

第三个手段是在处理器里主动打日志,用processingEnv.getMessager().printMessage输出每个字段的处理过程,再配合javac -verbose查看注解处理的轮次日志。这个手段能确认处理器进入了几轮、每轮看到了哪些元素,对排查多轮次重复生成问题很有效。

有一段时间我在调试一个仿写处理器,总在“方法生成了但调用不到”的怪圈里打转,后来发现是 javac 多轮次把旧树覆盖了。从那以后,我每次写完这类处理器都会强制走一遍三连验证:flat 文件看合并结果、javap 看字节码、再对照 Lombok 的 JavacHandler 实现复查逻辑,几乎不再遇到玄学问题。希望帮到你。

本文还有配套的精品资源,点击获取

相关新闻

基于OpenCV contrib的人脸关键点检测与美颜算法实现

基于OpenCV contrib的人脸关键点检测与美颜算法实现

简介&#xff1a;一套以OpenCV为基础的人脸美颜算法资料&#xff0c;围绕美白、扩眼、瘦脸三种典型人像美化需求展开&#xff0c;适合有一定图像处理基础、希望快速上手真实项目的开发者。包内共85个文件&#xff0c;压缩后约520MB&#xff0c;组成上兼顾课件、源码与依赖&…

2026/10/20 2:29:32 阅读更多 →
SmartSVN 9.2 许可证机制深度解析与离线合规部署指南

SmartSVN 9.2 许可证机制深度解析与离线合规部署指南

简介&#xff1a;本资源提供SmartSVN 9.2官方版本及配套Java环境下的本地化破解工具&#xff0c;面向需要长期使用功能完整SVN图形客户端的开发者、测试工程师与中小型团队技术负责人&#xff0c;解决正版授权成本高、试用期受限导致的版本迭代受阻与协作效率下降问题。压缩包为…

2026/10/20 2:29:32 阅读更多 →
Qt5.9 C++ GUI开发入门:从命令行到可交互窗口的实战指南

Qt5.9 C++ GUI开发入门:从命令行到可交互窗口的实战指南

简介&#xff1a;本资源是面向C初学者与Qt进阶开发者的系统性学习材料&#xff0c;聚焦Qt5.9跨平台GUI应用开发实战&#xff0c;解决从环境搭建、核心机制理解到复杂模块集成的学习断层问题。压缩包共2000个文件&#xff0c;总大小310.67MB&#xff0c;涵盖216个cpp与255个h头文…

2026/10/20 2:29:32 阅读更多 →

最新新闻

基于Win32 API与GDI+的捕鱼达人游戏开发实战:C++课程设计完整方案

基于Win32 API与GDI+的捕鱼达人游戏开发实战:C++课程设计完整方案

简介&#xff1a;这是一份面向高校C课程设计与Windows游戏开发初学者的实践项目源码&#xff0c;主题为经典小游戏“捕鱼达人”。项目以C语言为基础&#xff0c;结合Windows API完成窗口创建、消息循环与事件响应&#xff0c;并借助GDI绘制游戏背景、鱼群、炮台与子弹等元素&am…

2026/10/20 13:34:41 阅读更多 →
AI辅助教材编写全流程:从大纲到降重的实战指南

AI辅助教材编写全流程:从大纲到降重的实战指南

开头220字&#xff08;我需要认真思考标题和场景&#xff0c;构思一篇结构清晰、完整、专业的博文。先理解用户需求。&#xff09;开头&#xff08;220字&#xff09;写教材在很多人眼里是“专家才配干”的活儿——体例要规范、内容要严谨、查重要过关、还要有可教学性。但这两…

2026/10/20 13:34:41 阅读更多 →
内容创作的“rea”工作流:复盘、重建与行动,打造可持续内容体系

内容创作的“rea”工作流:复盘、重建与行动,打造可持续内容体系

“rea”这个标题&#xff0c;看起来很简短&#xff0c;但如果把它当成一个内容项目或者品牌栏目的起点&#xff0c;它其实是很多想法的容器。我自己的理解是&#xff0c;它更像一套“重新运转”的方法论——react&#xff0c;重新观察、重新拆解、重新行动。这篇博文不写宏大理…

2026/10/20 13:34:41 阅读更多 →
C++ Win32捕鱼达人课设:从零搭建与避坑指南

C++ Win32捕鱼达人课设:从零搭建与避坑指南

简介&#xff1a;这是一份面向高校C课程设计与Windows游戏开发初学者的完整实践项目&#xff0c;主题为“捕鱼达人”&#xff0c;适合正在学习面向对象编程、希望把语法知识落地为可运行图形程序的学生参考。项目围绕Windows API展开&#xff0c;涵盖窗口创建、消息循环与事件响…

2026/10/20 13:34:41 阅读更多 →
跨平台实时协作框架的架构设计与离线同步实践

跨平台实时协作框架的架构设计与离线同步实践

1. rea 这个代号&#xff0c;到底代表什么如果你在一个技术团队里待得够久&#xff0c;就会发现每个项目在正式命名之前&#xff0c;都会有一个特别随意的内部代号&#xff1a;可能是某个人随手敲的文件夹名&#xff0c;可能是会议室名字的缩写&#xff0c;也可能是三个字母的随…

2026/10/20 13:34:40 阅读更多 →
Qt命令行参数解析:从argc/argv到QCommandLineParser实战指南

Qt命令行参数解析:从argc/argv到QCommandLineParser实战指南

写这篇文章的缘由很简单&#xff1a;之前接手一个老项目的维护工作时&#xff0c;发现主程序里塞了满满一屏的手写参数解析代码&#xff0c;各种strcmp加if/else嵌套&#xff0c;看着头大不说&#xff0c;新加一个开关还得顺着调用链一路摸下去&#xff0c;搞得人战战兢兢。后来…

2026/10/20 13:33:32 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →