写 Java 的人大多装过阿里的 p3c 插件,IDE 里一写 Executors.newFixedThreadPool 就给你标黄。
但现在很多代码是 Agent 写的。它不看 IDE 的黄线,Executors、new Thread()、Integer == Integer、foreach 里 remove,这些规约明令禁止的写法都可能出现在它提交的文件里。把规约写进 CLAUDE.md 或者做成 Skill 能缓解,但那要靠模型自己记得去遵守。
所以我做了 p3c-guard:一个 Claude Code 插件,在文件真正写入之前拦一道。
效果

我让它用 Executors.newFixedThreadPool(4) 写一个订单服务,再用 == 比较两个 Integer。写入被拒绝,Agent 收到的是:
text
p3c-guard:这次写入会给 OrderService.java 新增 2 处阿里 Java 规约违规,未写入。
- 第 8 行 [ThreadPoolCreationRule] 手动创建线程池,效果会更好哦。 改法:用 new ThreadPoolExecutor(...) 显式给出核心数、最大数、有界队列和带名字的 ThreadFactory;Spring 项目注入已配置的 ThreadPoolTaskExecutor。
- 第 10 行 [WrapperTypeEqualityRule] 应使用equals方法代替== 改法:用 Objects.equals(a, b) 比较包装类型。
行号是写入后文件里的行号。请修正后重新写入;文件原有的违规不在此列。
然后它自己换成 ThreadPoolExecutor 和 Objects.equals,第二次写入通过。
它是怎么做的
Claude Code 的 Mod 可以挂在工具调用上,在调用真正执行之前返回一个拒绝。核心逻辑是这样的(简化过,完整代码在仓库的 hooks/register.ts):
ts
on('tool.call', { tool: 'Write' }, async ($, e, next) => {
if (!e.file_path.endsWith('.java')) {
return next(e) // 不是 Java,放行
}
const before = await readIfExists($, e.file_path)
const deny = await judge($, e.file_path, before, e.content)
return deny === undefined ? next(e) : { deny }
})
judge 做三件事:
- 把「写入前的文件」和「写入后的文件」各放进一个临时目录。
- 用 PMD 跑一次 p3c 规则集,拿到两份违规列表。
- 两份相减,只留下这次写入新增的、级别够高的违规。
Edit 也一样,只是要先读原文件、把替换套上去,算出写入后的完整内容。
检查不经过模型,是 PMD 的语法树规则,同样的代码每次结果一样。在我的机器上单个文件约 1 秒,写入前后两份放在同一次运行里也是 1 秒左右。
几个取舍
只拦新增的违规。 这是能不能日常用的关键。老项目的文件里本来就有违规,全拦的话 Agent 连改一行都会被拒。我拿一个 65 个文件的 Spring Boot 项目扫了一遍,一共 65 条违规,其中 37 条是「缺 @author」。
默认只拦严重的。 p3c 的规则分三个优先级,一共 57 条:
| 优先级 | 条数 | 例子 | 默认 |
|---|---|---|---|
| 1 | 11 | 线程池创建、包装类用 ==、foreach 里修改集合、不写大括号 |
拦 |
| 2 | 21 | 显式建线程、ThreadLocal 不 remove、finally 里 return、命名 | 拦 |
| 3 | 25 | 缺 @author、魔法值、HashMap 不指定容量 | 不拦 |
上面那个项目里,优先级 1 是 0 条,1 和 2 合计 4 条。如果连优先级 3 也拦,65 条里有 61 条是注释和风格问题,那就没法用了。
守卫自己出错时放行。 没装 Java、规则包下载失败、PMD 崩了,都放行并在状态栏提示。把所有 Java 写入卡死,比漏查一次更糟。
语法错误的文件放行。 那是编译器的事。
一个绕不开的坑:官方 p3c 太旧了
alibaba/p3c 最后一次提交是 2024 年 8 月,规则引擎建在 PMD 6.15 上。这个版本解析不了 record、switch 表达式、文本块,而 Agent 写 Spring Boot 3 的代码会大量用这些。
所以规则引擎用的是社区把 p3c 移植到 PMD 7 和 JDK 21 的版本 loong95/p3c-cmd-jdk21(Apache-2.0)。插件本身不包含任何规则实现,首次使用时从 Maven Central 下载那个 13 MB 的 jar,校验 SHA-256 后缓存在 ~/.cache/p3c-guard/。
我用一个同时包含 sealed 接口、record、文本块、带 guard 的 switch 模式匹配的文件测过,能正常解析并查出里面的线程池问题。
没做到的
- 不是整本手册。 只覆盖 p3c 用代码实现了的 57 条。空
catch块、日志规范、SQL 规约这些没有对应规则的条目查不到。 - 个别规则会漏。 比如静态
SimpleDateFormat写在 lambda 里调用时查不出。 - 只看
Write和Edit。 Agent 用 shell 命令改文件不经过它。 - 每台机器的第一次 Java 写入不检查,因为规则包还在后台下载。
- 只在 macOS 上测过。Linux 没测,Windows 不支持。
- 测试样本不多:自动化测试 11 个,真实项目上只试过一轮,共 7 次写入,拦了 1 次。
安装
在 Claude Code 里输入:
text
/plugin install p3c-guard --marketplace alexlifexyz/p3c-guard
需要 JDK 21 以上,Claude Code 2.1.287 以上。更旧的版本里 Mod 默认是关的,要这样启动:
bash
CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 claude
装完输入 /p3c,有回应就是生效了。/p3c off 可以随时暂停。
最后
仓库在这里,MIT 协议: github.com/alexlifexyz...
很想知道写 Java 的各位怎么看:哪些规则你觉得拦了反而碍事,哪些该拦但 p3c 里没有。欢迎拿真实项目试,误拦和漏拦都可以直接提 issue。