惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

N
Netflix TechBlog - Medium
I
InfoQ
Engineering at Meta
Engineering at Meta
Jina AI
Jina AI
Recent Announcements
Recent Announcements
T
The Blog of Author Tim Ferriss
P
Proofpoint News Feed
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
D
Docker
Microsoft Security Blog
Microsoft Security Blog
宝玉的分享
宝玉的分享
Last Week in AI
Last Week in AI
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
GbyAI
GbyAI
博客园 - Franky
博客园 - 聂微东
Microsoft Azure Blog
Microsoft Azure Blog
博客园 - 叶小钗
酷 壳 – CoolShell
酷 壳 – CoolShell
B
Blog RSS Feed
WordPress大学
WordPress大学
MyScale Blog
MyScale Blog
月光博客
月光博客
罗磊的独立博客

程序员

V2EX 看到讨论"跨域"的帖子,那个她好像回来了 codex 今天真的是不稳定呀。 火山方舟 Coding Plan 慎买 刚问了大家 openclaw 和 hermes 在什么机器上面玩,求推荐一个机器 GPT-image-2 生成 AI 图片防伪有感 codex pro 5 小时限制已经严重缩水 逆天 Antigravity 动态 JSON 序列化对强类型语言很难吗? 自建了 GPT Coding Plan,遇到了定价问题,请教大家 大家都是在什么设备上玩 openclaw 以及 hermes 的呀? 软考还有一个月就考试了,你们学习了吗? 大伙用 AI 会考虑在 user scope 的 CLAUDE.md/AGENTS.md 里交代 AI 说中文吗 我发现程序员这个群体很大部分其实挺抠的 最近使用 cc 总会莫名其妙的返工, codex 不会 目前体验最好的远程 vibe 工具 想知道大佬们抓包遇到 ssl pinning 都是咋优雅的 解决的? 工业软件的大佬们是怎么 vibe coding 的 最近 chrome 是不是有 bug 啊,一搜索就卡住 分布式异步系统在 vibe coding 下的困境 PHP Native AOT 编译器,支持将 PHP 代码编译为可执行文件,运算性能提高 150 倍 没想到 2026 年,还要浪费大量时间在跨域问题上 DeepSeek V4 这周会出吗? 中转站正式试营 欢迎试用 不掺不假 小米 mimo 升级 v2.5,并且重置了额度 Jenkins, SCM 轮询完全不工作是啥问题啊 赛博斗蛐蛐, AI 模型的简单对比(白嫖版) 使用中转站要擦亮眼睛!不说别的,倍率计算 充值好乱。 买了火山的 Coding Plan 测试得出计费模式 给我的 AI 生成了简历和状态卡, 大家帮忙看下 Ta 能找到啥样的
支付中台系统,基于 jeepay 重构了 2 天,目标是做企业部署发...
5wunian · 2026-05-22 · via 程序员

Spring Boot 3.x 循环依赖实战:从 allow-circular-references 到纯 DAG ,一个支付系统的重构之旅

项目背景:Jeepay 计全支付,Spring Boot 3.3.7 + MyBatis-Plus + Vue 3 ,多模块 Maven 项目。

  1. 前端集成引发的连锁反应

原本前后端分离部署( Nginx + 独立前端容器),想把前端 dist 直接打进 JAR 简化部署。几个坑:

  • Vue SPA 路由 fallback:Spring Boot 3.x 默认用 PathPatternParser ,addViewControllers 不支持 {spring:\w+} 正则。最终用 @Order(HIGHEST_PRECEDENCE) 的 Filter 拦截无后缀路径 forward 到 index.html 。
  • Spring Security 6 ignoring() 不生效:日志明确警告 You are asking Spring Security to ignore... This is not recommended 。改为 authorizeHttpRequests().permitAll() 才生效。
  • 字体文件 401:Security 的静态资源豁免模式漏了 .woff/.woff2/.ttf/.eot 。
  • 前端构建 API baseURL 双层 /api/api/:VITE_API_BASE_URL=/api 拼接 url: '/api/xxx' → /api/api/xxx 。改为空值。
  1. 发行包瘦身:260MB → 120MB

三个模块的 fat JAR 共 288MB ,其中 130 个公共依赖(72MB) 重复存储 3 次。方案:

  • 去掉未用依赖:jaxb-api(零 import)、mysql-connector-j(纯配置模块不需要)、activemq(仅编译,运行时用 RabbitMQ)
  • 共享 lib:maven-dependency-plugin 收集所有传递依赖到 lib/(自动去重 182 个),antrun 解压 fat JAR 提取 BOOT-INF/classes/ 打包为 flat thin JAR(1.7-5MB)
  • 启动方式:从 java -jar fat.jar 改为 java -cp "lib/*:apps/app.jar" MainClass
  1. 循环依赖根治

去掉 allow-circular-references: true 后直接启动,Spring Boot 3.x 严格检测报出完整依赖链:

┌─────┐ │ SysConfigService 自引用 (删 @Autowired self ,直接用 this) ↑ ↓ │ IsvInfoService 自引用 (同上)

修复策略:

  • 自引用:删字段,改用 this.xxx()
  • 三角循环( PayInterfaceConfigService ↔ MchAppService/MchInfoService ):PayInterfaceConfigService.selectAllPayIfConfigListByAppId() 内部反向调用了 mchAppService.getById() 和 mchInfoService.getById()——这些只是 MyBatis-Plus 的简单 CRUD 委托。将这两个查询上移到 Controller 层,Service 方法改为接收 MchInfo 参数,依赖方向恢复。
  • 两两循环:MchInfoService 注入 IsvInfoService 只是为了 getById(),IsvInfoService 注入 MchInfoService 只是为了 count()。直接替换为对应的 Mapper 注入,因为 MyBatis-Plus 的 ServiceImpl.getById() 底层就是 BaseMapper.selectById()。

关键认知:

  1. 依赖只能单向流动。找到"谁在反向调用"就是断环点。

  2. 大多数循环依赖是 CRUD 委托导致的。ServiceImpl 包装 BaseMapper ,循环往往是因为 A 需要 B 的 getById(),B 需要 A 的 count()——直接用 Mapper 替代 Service 注入,不改任何业务逻辑。

  3. 不要用 @Lazy 、不要用 allow-circular-references ,这些都是掩耳盗铃。Spring Boot 3.x 默认禁止循环是为了逼你写出正确的分层。

  4. 最终效果

┌───────────────────────────┬────────┬───────────────┐ │ 指标 │ 修复前 │ 修复后 │ ├───────────────────────────┼────────┼───────────────┤ │ 发行包大小 │ 260 MB │ 120 MB (-54%) │ ├───────────────────────────┼────────┼───────────────┤ │ 循环依赖 │ 5 个 │ 0 │ ├───────────────────────────┼────────┼───────────────┤ │ allow-circular-references │ true │ 已移除 │ ├───────────────────────────┼────────┼───────────────┤ │ @Lazy │ 2 处 │ 0 │ ├───────────────────────────┼────────┼───────────────┤ │ 启动时间 │ ~8s │ ~4s │ └───────────────────────────┴────────┴───────────────┘