• 请不要在回答技术问题时复制粘贴 AI 生成的内容
5wunian
V2EX  ›  程序员

支付中台系统,基于 jeepay 重构了 2 天,目标是做企业部署发行版遇到的问题分享

  •  
  •   5wunian · 2h 38m ago · 235 views

    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 │ └───────────────────────────┴────────┴───────────────┘

    6 replies    2026-05-22 22:29:19 +08:00
    5wunian
        1
    5wunian  
    OP
       2h 35m ago
    还增加了 openlist 的 s3 存储支持。
    5wunian
        2
    5wunian  
    OP
       2h 34m ago
    有自建需求的留言邮箱。
    5wunian
        3
    5wunian  
    OP
       2h 34m ago
    从开源建立的 jeepay ,到商业片的发行包,中间有一大堆需要配置配置的东西。为此做了大量的自动化部署工具脚本。
    5wunian
        4
    5wunian  
    OP
       2h 19m ago
    目前已经完成了一个应用接入支付。有需要接入支付的,可以联系,提供技术支持。
    5wunian
        5
    5wunian  
    OP
       2h 16m ago
    已经完成了支付备案,网站备案,可以完成全链路的动作。
    Mandyer
        6
    Mandyer  
       1h 59m ago
    YW9odW4yMDE4QGdtYWlsLmNvbQ==
    楼主来一份,谢谢
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   1683 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 33ms · UTC 16:28 · PVG 00:28 · LAX 09:28 · JFK 12:28
    ♥ Do have faith in what you're doing.