1. 第一次让 Claude Code 改 Jakarta EE 消息队列代码,我踩到的真实坑
Claude Code 是 Anthropic 推出的终端 AI 编程助手,能直接读写你本地的项目文件、执行命令、按指令批量改代码;Jakarta EE 是 Java 企业级开发的标准规范集合,其中的 JMS(Java Message Service)负责消息队列的生产与消费。把这两者凑到一起,就是本文要聊的事:用 Claude Code 去改一个 Jakarta EE 的 JMS 生产者示例,把原来同时支持 Queue 和 Topic 的代码,收敛成只处理 Queue,顺便把 settings 配置切到 TaoToken 上,让整个链路能跑通、能验证。
适合谁看?如果你手上有老旧的 Jakarta EE / JMS 项目,客户环境里只有 Queue 没有 Topic,代码里却还留着一堆 Topic 分支;或者你刚装好 Claude Code,想找个真实项目练手,而不是对着空目录发呆——这篇就是给你写的。我会把项目结构梳理、消息生产链路定位、settings 配置、Maven 依赖、编译运行、本地收发验证、以及几个真实报错的排查过程,全部按我实际操作的顺序写下来,你照着敲就能复现。
先说结论方向:Claude Code 在“读懂项目 + 批量改文件”这件事上确实省事,但它不会自动帮你搭测试环境,编译和运行验证这一步必须你自己补上。我第一次跑的时候,改完代码直接javac就报了一堆找不到符号的错,原因后面第 5 节会细讲。所以这篇不是“AI 一键搞定”的爽文,而是一份能跟做的操作记录。
2. 项目结构梳理与消息生产链路定位:Jakarta EE JMS 生产者示例怎么读
拿到一个 Jakarta EE 项目,第一步不是急着让 AI 改代码,而是先让它告诉你“这个项目到底在干嘛”。我进到项目目录后直接启动 Claude Code:
cd ~/projects/my-mq-app claude启动后界面会显示当前模型、工作目录和版本信息。我用的版本是 Claude Code v2.1.71,工作目录~/projects/my-mq-app。第一句指令很朴素:
这个项目做什么?Claude Code 会自己去搜索文件、读取内容,然后给出结论。它读完告诉我:这是一个消息队列生产者示例,用的是 Jakarta JMS API,通过@Resource注入连接工厂和目的地,用JMSContext的 try-with-resources 管理连接生命周期;命令行第一个参数指定 queue 或 topic,第二个可选参数指定发送条数,默认 1 条;发完文本消息后还会发一条空的控制消息表示结束。包名是jakarta.tutorial.producer,从版权头和包名看,这是 Jakarta EE 官方教程里的示例代码。
这一步的价值在于:它帮你把“消息生产链路”定位清楚了。链路是这样的——ConnectionFactory由容器注入 →JMSContext创建上下文 →createProducer().send(dest, message)发送 → 循环 N 次 → 最后发一条控制消息。消费端则是SynchConsumer或AsynchConsumer配合运行。你要改的任何东西,都落在这条链路上。
我实际的项目目录很干净,就一个QueueProducer.java(改之前叫Producer.java)。这种单文件示例最适合拿来练手,因为改动范围可控,出问题也好回滚。你可以先ls看一眼:
ls -la # 输出大致是: # QueueProducer.java # pom.xml(如果有 Maven 工程)如果只有.java没有pom.xml,说明它是纯源码示例,编译时要手动把 Jakarta EE 的 API jar 加进 classpath,这一点在第 4 节编译环节会踩到。定位链路时,我建议你让 Claude Code 明确回答三个问题:消息从哪来(ConnectionFactory)、发到哪去(Queue/Topic)、怎么发(JMSContext + Producer)。这三个问题答清楚了,后面改代码就是顺水推舟。
3. 把 settings 改到 TaoToken:可复制的配置片段与 Maven 依赖
Claude Code 默认走官方 API,但你可以通过 settings 文件把请求指向 TaoToken 的兼容端点。TaoToken 提供 OpenAI 兼容与 Anthropic 兼容两种接入方式,Claude Code 走的是 Anthropic 协议,所以 Base URL 用https://taotoken.net/api,Key 在控制台的 API Keys 页面生成。
配置文件位置按 Claude Code 的约定放在用户目录下。我实测下来,最省事的方式是直接写~/.claude/settings.json:
{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_AUTH_TOKEN": "sk-你的TaoToken密钥", "ANTHROPIC_MODEL": "claude-sonnet-4-5-20250929" } }三个字段的作用分别是:ANTHROPIC_BASE_URL指定请求端点,ANTHROPIC_AUTH_TOKEN放你的密钥,ANTHROPIC_MODEL指定默认模型 ID。注意 Base URL 后面不要带/v1,Claude Code 会自己拼接路径;带了反而容易 404。密钥去 TaoToken 控制台的 API Keys 页面创建,创建后只显示一次,记得当场复制。
如果你更习惯用环境变量而不是 settings 文件,也可以在 shell 里导出:
export ANTHROPIC_BASE_URL="https://taotoken.net/api" export ANTHROPIC_AUTH_TOKEN="sk-你的TaoToken密钥" export ANTHROPIC_MODEL="claude-sonnet-4-5-20250929"两种方式二选一即可,settings 文件的优先级更高,适合长期使用;环境变量适合临时切换。改完 settings 后重启 Claude Code,启动界面上的模型名会变成你配置的那个,这就说明生效了。
接下来是 Maven 依赖。Jakarta EE 的 JMS API 在 Maven 中央仓库里有独立坐标,不要引javax.jms,那是老版本。正确的依赖是:
<dependencies> <dependency> <groupId>jakarta.jms</groupId> <artifactId>jakarta.jms-api</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>jakarta.annotation</groupId> <artifactId>jakarta.annotation-api</artifactId> <version>2.1.1</version> </dependency> </dependencies>jakarta.jms-api提供ConnectionFactory、JMSContext、Queue、Destination这些接口;jakarta.annotation-api提供@Resource注解。两个都缺一不可,少了注解包编译时会报“找不到符号 Resource”。如果你用的是纯命令行javac而不是 Maven,就得手动下载这两个 jar 放进 classpath,第 4 节会给命令。
提示:模型 ID 会随版本更新,配置前建议先在 TaoToken 的模型对话页面确认当前可用的模型名,避免写了一个已下线的 ID 导致请求失败。
4. 让 Claude Code 改代码并编译运行:从 Producer 到 QueueProducer 的完整过程
配置好之后,回到项目里下指令。我的目标是:客户环境只有 Queue,没有 Topic,把代码改成只处理 Queue,提高可读性。指令写清楚意图就行:
客户的消息中间件通常只有 Queue,没有 Topic。 把这个生产者改成只处理 Queue,去掉 Topic 相关分支, 类名改成 QueueProducer,命令行参数只保留消息条数。Claude Code 会逐文件给出 diff。它实际做的改动包括:删掉import jakarta.jms.Topic、删掉@Resource(lookup = "jms/MyTopic")的 topic 字段、把类名从Producer改成QueueProducer、把main方法里判断queue/topic的分支全部删掉、把Destination dest = queue直接赋值、参数校验从“一到两个参数”改成“最多一个参数”,并且加了正整数校验。改完的main方法核心逻辑是这样:
public static void main(String[] args) { if (args.length > 1) { System.err.println("Program takes at most one argument: [<number-of-messages>]"); System.exit(1); } int numMsgs = 1; if (args.length == 1) { try { numMsgs = Integer.parseInt(args[0]); if (numMsgs <= 0) { System.err.println("Number of messages must be positive: " + numMsgs); System.exit(1); } } catch (NumberFormatException e) { System.err.println("Argument must be a valid number: " + args[0]); System.exit(1); } } final int NUM_MSGS = numMsgs; Destination dest = queue; try (JMSContext context = connectionFactory.createContext()) { int count = 0; for (int i = 0; i < NUM_MSGS; i++) { String message = "This is message " + (i + 1) + " from producer"; System.out.println("Sending message: " + message); context.createProducer().send(dest, message); count += 1; } System.out.println("Text messages sent: " + count); context.createProducer().send(dest, context.createMessage()); } catch (JMSRuntimeException e) { System.err.println("Exception occurred: " + e.toString()); System.exit(1); } System.exit(0); }改完后 Claude Code 会尝试跑javac -version之类的命令确认环境。这里有个坑:它默认可能只跑版本检查,不会真的编译你的文件。你得自己补编译命令。用 Maven 的话:
mvn -q clean compile如果项目没有pom.xml,用纯javac,需要把两个 API jar 加进 classpath:
javac -cp "lib/jakarta.jms-api-3.1.0.jar:lib/jakarta.annotation-api-2.1.1.jar" \ -d target/classes QueueProducer.java编译通过后,target/classes/jakarta/tutorial/producer/QueueProducer.class就生成了。这一步是很多人第一次用 Claude Code 会漏掉的——AI 改完代码不等于代码能编译,编译验证必须自己跑。
5. 本地消息收发验证与常见报错排查:401、local proxy failed、找不到符号
编译过了不代表能跑。JMS 生产者需要一个真正的 JMS Provider 才能运行,因为@Resource注入的连接工厂和队列是容器提供的。纯java命令直接跑会报NullPointerException,因为connectionFactory和queue都是 null。要本地验证,最轻量的做法是起一个嵌入式 JMS broker,比如 ActiveMQ Artemis 的嵌入式模式,或者用 GlassFish/Payara 这类 Jakarta EE 容器部署。
我实测下来,用 Payara Micro 最省事:
java -jar payara-micro.jar --deploy target/my-mq-app.war部署后容器会绑定java:comp/DefaultJMSConnectionFactory和jms/MyQueue,生产者就能正常注入了。运行生产者:
java -cp "target/classes:lib/*" jakarta.tutorial.producer.QueueProducer 3预期输出:
Sending message: This is message 1 from producer Sending message: This is message 2 from producer Sending message: This is message 3 from producer Text messages sent: 3看到Text messages sent: 3就说明改动生效了。再跑一次同步消费者,能收到这 3 条消息加 1 条控制消息,链路就闭环了。
下面是几个我真实撞到的报错和排查思路:
报错一:401 Unauthorized。这是 Claude Code 请求 TaoToken 时密钥不对或没生效。先确认~/.claude/settings.json里的ANTHROPIC_AUTH_TOKEN是不是完整复制了,有没有多余空格;再确认ANTHROPIC_BASE_URL是https://taotoken.net/api而不是别的路径。改完必须重启 Claude Code,环境变量不会热加载。
报错二:local proxy failed或连接超时。通常是 Base URL 写错,比如多写了/v1或者写成了别的域名。Claude Code 走 Anthropic 协议,端点就是https://taotoken.net/api,不要自己拼路径。另外检查本机网络是否能正常访问该域名。
报错三:error: cannot find symbol找不到Resource或JMSContext。这是 classpath 缺 jar。确认jakarta.annotation-api和jakarta.jms-api都在依赖里,纯javac时-cp参数要包含这两个 jar 的完整路径。注意别引成javax.annotation,Jakarta EE 9 之后全部迁到jakarta.*命名空间。
报错四:reading choices相关解析错误。这类报错一般出现在模型返回内容格式异常时,多半是模型 ID 写错或该模型不支持当前请求格式。去 TaoToken 模型对话页面确认模型名,换成明确可用的 ID 再试。
报错五:运行时NullPointerException指向connectionFactory.createContext()。说明没在容器里跑,@Resource没被注入。JMS 生产者不能脱离容器裸跑,必须部署到 Payara/GlassFish 或起嵌入式 broker。
注意:如果你在 Claude Code 里同时用了 Cline MCP 或 Codex 的
auth.json,记得三件套要一致——Base URL、Key、Model ID 三处都指向同一套配置,否则会出现“Claude Code 能连、Cline 连不上”的割裂情况。
6. 把这条链路用顺:TaoToken 接入与后续编码的衔接
代码改完、编译通过、本地收发验证成功,这一轮就算走完了。回头看,Claude Code 在“读项目 + 批量改文件 + 生成可编译代码”上确实省了我不少时间,尤其是删 Topic 分支这种机械但容易漏的活,它一次改到位,连注释和类名都同步更新了。但它不会替你搭测试环境,也不会自动跑编译,这两步是人的活。
如果你打算把 Claude Code 长期用在 Jakarta EE 或 Java 项目上,建议把 settings 配置固定下来,密钥放在~/.claude/settings.json里,模型 ID 选一个稳定的。需要生成新密钥或管理额度,去 TaoToken 控制台的 API Keys 页面操作;接入细节和协议说明在接入文档里;想先确认某个模型能不能用,直接在模型对话页面发一条测试消息最快。长期做编码和 Agent 类任务的话,Coding Plan 的额度模型比按次计费更适合高频使用。
我自己的习惯是:每次让 Claude Code 改完代码,先mvn compile过一遍,再部署到 Payara Micro 跑一次生产者,看到Text messages sent: N才算收工。这套流程跑顺之后,改下一个 JMS 消费者或者 MDB(Message-Driven Bean)就是同样的套路——先定位链路,再下指令,最后编译验证。