OA 印章管理系统实战(二):底座搭建——若依 + Camunda 7.19 整合实录

第 2 / 12 章
OA 印章管理系统实战(二):底座搭建——若依 + Camunda 7.19 整合实录

调研文档写的是 SpringBoot 2.7 + Camunda 7.19 + MyBatis-Plus 3.5.x。但当我克隆了 camunda-study-demo(Gitee 明珠科技,34 Stars)整合进若依骨架时,发现三个意外。


事故现场

事故现场:调研偏差 + 骨架版本意外 + 一个没人写的 Xerces 坑

意外一:SpringBoot 版本不是 2.7,是 2.5.15。 若依 3.8.6 的根 pom.xml 继承的 spring-boot-dependencies 写死了 2.5.15:

<!-- ruoyi/pom.xml -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.5.15</version>
</dependency>

调研文档写 2.7 是我自己的记忆偏差——Camunda 7.19 官方声明支持 SpringBoot 2.3 到 3.x,2.5.15 完全在支持范围内,但版本号对不上意味着我之前脑补的"2.7 的新特性"(比如自动配置的条件注解变化)根本不存在。

意外二:没有 MyBatis-Plus,只有原生 MyBatis。 依赖树里拉出来的是:

ruoyi-common → pagehelper-spring-boot-starter:1.4.6
  → mybatis-spring-boot-starter:2.2.2
    → mybatis:3.5.9

没有 com.baomidou:mybatis-plus-*。ruoyi-oa 的 Mapper 是纯接口 + XML,没有 extends BaseMapper——这意味着我写的 OaSealApplyMapper 六个方法全是手写的,没有用到 MyBatis-Plus 的通用 CRUD。

意外三:ruoyi-admin/pom.xml 里有一行注释,我当时没注意。 真正让我花了 3 小时排查的,不是依赖冲突(Camunda starter 本身不带 MyBatis,根本没冲突),而是另一行:

<!-- JDK17下Camunda解析BPMN XSD需要Xerces(规避src-resolve校验失败) -->
<dependency>
    <groupId>xerces</groupId>
    <artifactId>xercesImpl</artifactId>
    <version>2.12.2</version>
</dependency>

这不是我加的,是 camunda-study-demo 的原作者加的。注释说的"JDK17 下 Camunda 解析 BPMN XSD 需要 Xerces"——我当时没理解,直到自己跑起来看到 ENGINE-09005 Could not parse BPMN process 才翻回来看:JDK 17 内置的 Xerces 有个 src-resolve 校验限制,Camunda 7.19 的 BPMN20.xsd 里有相对 import,JDK 17 内置 Xerces 默认拒绝;但用 XercesImpl 2.12.2 替换后,它的 src-resolve 行为更宽松,BPMN 就能正常解析。

三个意外,没有一个是 Camunda 和若依的依赖冲突——我之前脑补的"Camunda 自带 MyBatis 和若依 MyBatis-Plus 打架"根本不存在。Camunda starter 在 ruoyi-wowkflow/pom.xml 里就是干净的一行,没有任何 exclusion:

<dependency>
    <groupId>org.camunda.bpm.springboot</groupId>
    <artifactId>camunda-bpm-spring-boot-starter</artifactId>
</dependency>


排查:为什么 pom.xml 比调研文档写的简单得多?

我花了一天跑 mvn dependency:tree -Dincludes=org.camunda.bpm,org.mybatis,org.springframework.boot,结果:

com.ruoyi:ruoyi-admin:jar:3.8.6
├── org.springframework.boot:spring-boot-devtools:2.5.15
├── org.springframework.boot:spring-boot-starter-web:2.5.15
│   ├── spring-boot-starter:2.5.15
│   ├── spring-boot-starter-json:2.5.15
│   └── spring-boot-starter-tomcat:2.5.15
├── ruoyi-framework → spring-boot-starter-aop:2.5.15
├── ruoyi-quartz → spring-boot-starter-security:2.5.15
├── ruoyi-common → pagehelper-spring-boot-starter:1.4.6
│   └── mybatis-spring-boot-starter:2.2.2
│       └── mybatis:3.5.9
├── ruoyi-wowkflow → camunda-bpm-spring-boot-starter:7.19.0
│   └── camunda-engine-spring:7.19.0
│       └── camunda-engine:7.19.0
└── ruoyi-oa(自研)

没有任何 excluded 节点。 没有 <exclusion> 标签。没有依赖冲突警告。

为什么 Camunda starter 不会和 MyBatis 冲突?因为 Camunda starter 根本不带 MyBatis。Camunda 7.19 的持久层走的是 JPA + Hibernate(或者它自己的 MyBatis 实现,但默认不引),若依的 MyBatis 是独立的 mybatis-spring-boot-starter,两者各管各的数据源和 ORM,互不干扰。

那 pom.xml 里为什么没有 MyBatis-Plus?因为若依 3.8.6 选择了 pagehelper-spring-boot-starter + dynamic-datasource-spring-boot-starter 的组合——pagehelper 做分页、dynamic-datasource 做多数据源切换,不需要 MyBatis-Plus 也能跑。而我在 ruoyi-oa 里写 Mapper 接口时,干脆按若依的原生 MyBatis 风格写了六个手写方法,没引入 MyBatis-Plus。


底层原理:Xerces 的 src-resolve 是什么?

JDK 内置的 XML 解析器是 Xerces-J。从 JDK 13 开始,Xerces 引入了一个新特性叫 src-resolve——它是 XSD schema 验证的一部分,用来防止 XXE 攻击。具体规则:当一个 XSD 文件里出现 <xsd:import schemaLocation="Semantic/BPMNDI.xsd"> 这种相对路径引用时,JDK 13+ 内置 Xerces 默认会校验这个相对路径是否"安全"。

问题在于 Camunda 7.19 的 BPMN20.xsd 是这样的:

<!-- camunda-engine-7.19.0.jar!/org/camunda/bpm/model/bpmn/schema/BPMN20.xsd -->
<xsd:import namespace="http://www.omg.org/spec/BPMN/20100524/DI"
            schemaLocation="Semantic/BPMNDI.xsd"/>

schemaLocation="Semantic/BPMNDI.xsd" 是相对于 BPMN20.xsd 所在位置的路径。但 JDK 17 内置 Xerces 的 src-resolve 校验器看到这个相对路径,会尝试在 classpath 里找 Semantic/BPMNDI.xsd——但 Camunda 的 XSD 文件在 jar 内部,classpath 扫描可能找不到,然后校验器就会静默拒绝加载这个 schema。

schema 加载失败 → Camunda 解析 BPMN 时找不到 BPMN 2.0 规范 → ENGINE-09005 Could not parse BPMN process。

解决方案:用 XercesImpl 2.12.2 替换 JDK 内置的。 XercesImpl 2.12.2 的 src-resolve 行为更宽松——它会先尝试 JDK 内置的 classpath 资源查找,找不到再用自己的 URLClassLoader 兜底。加了 xercesImpl:2.12.2 后,SpringBoot 的 XMLInputFactory / SAXParserFactory 会自动发现这个新实现并优先使用。

为什么不是 fat jar 问题? 这是我一开始搞混的两个不同的坑:

  • JDK 17 + Xerces src-resolve:mvn spring-boot:run(普通 classpath)也会触发,解决是加 xercesImpl

  • fat jar + BPMN XSD 相对 import:java -jar(nested jar classloader)才触发,解决是改用 classpath 启动


正确姿势:pom.xml 实际需要什么?

ruoyi-wowkflow/pom.xml(Camunda 整合层):

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.camunda.bpm</groupId>
            <artifactId>camunda-bom</artifactId>
            <version>7.19.0</version>
            <scope>import</scope>
            <type>pom</type>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>org.camunda.bpm.springboot</groupId>
        <artifactId>camunda-bpm-spring-boot-starter</artifactId>
        <!-- 一行搞定,无 exclusion -->
    </dependency>
</dependencies>

ruoyi-admin/pom.xml(应用入口,关键补充):

<!-- JDK17下Camunda解析BPMN XSD需要Xerces(规避src-resolve校验失败) -->
<dependency>
    <groupId>xerces</groupId>
    <artifactId>xercesImpl</artifactId>
    <version>2.12.2</version>
</dependency>

application.yml(Camunda 配置段,关键三行):

camunda:
  bpm:
    # fat jar 环境下 autoDeployResources 扫描 nested jar 会失败,
    # 改用 OaFlowBootstrap 手动部署(第 03 章详写)
    auto-deployment-enabled: false
    database:
      schema-update: true

# 关掉 Camunda entity 的 debug 日志(生产必须关!)
logging:
  level:
    org.camunda.bpm.engine.impl.persistence.entity: warn


数据说话:三种启动方式对比

启动方式

classloader

Xerces 实现

BPMN 解析

Camunda 启动

耗时

mvn spring-boot:run

URLClassLoader(平铺)

XercesImpl 2.12.2(显式引入)

✅ 142ms

✅ Process Engine default created

13.5s

java -jar ruoyi-admin.jar

LaunchedURLClassLoader(nested jar)

XercesImpl 2.12.2

❌ ENGINE-09005

❌ 秒挂

—

mvn spring-boot:run + 不加 XercesImpl

URLClassLoader

JDK 17 内置 Xerces

❌ src-resolve 拒绝

❌ 秒挂

—

三行对比就把两个坑的边界划清了:

  • 第二行 vs 第一行 → fat jar 的 nested classloader 问题(第 03 章详写)

  • 第三行 vs 第一行 → JDK 17 Xerces src-resolve 问题(本章)

没有 MyBatis 冲突的证据: mvn compile 无 exclusion 无警告通过。依赖树里 Camunda 7.19.0 不带 MyBatis,若依 2.5.15 的 mybatis-spring-boot-starter:2.2.2 完全独立。


面试怎么答:整合 Camunda 和 SpringBoot 要注意什么?

"我整合的是若依 3.8.6(SpringBoot 2.5.15)+ Camunda 7.19.0。核心是三个点:

第一,依赖上没什么坑。 Camunda 7.19 的 spring-boot-starter 一行就能引入,因为它不带 MyBatis——Camunda 持久层走 JPA/Hibernate 或自己的实现,若依用的原生 MyBatis 互不干扰。我最开始脑补会有 MyBatis 冲突,跑了 mvn dependency:tree 才发现根本没冲突,也不需要加 exclusion。

第二,Xerces 补充依赖是隐藏要求。 JDK 17 内置 Xerces 有个 src-resolve 校验机制,会拒绝 Camunda BPMN20.xsd 里的相对 import,导致 BPMN 解析秒挂。解决方案是显式引入 xercesImpl:2.12.2 替换掉内置的——这是 camunda-study-demo 原作者踩过的坑,pom.xml 里写了注释。

第三,auto-deployment 必须关掉。 若依的 fat jar 打包后,Camunda 的 autoDeployResources 扫描 nested jar 找不到 BPMN 文件,所以改成 auto-deployment-enabled=false,自己写一个 OaFlowBootstrap(ApplicationRunner)用 repositoryService.createDeployment().addString("seal_approve.bpmn", xml).deploy() 手动部署。启动时还做了幂等检查——如果 pearl_flow_model 里已有 def_key=seal_approve 的记录就跳过。

整合后我跑了三种启动方式做对比:classpath 模式(mvn spring-boot:run)能通,fat jar 模式不行(nested classloader),不加 Xerces 也不行。这个对比结论在部署时很重要——生产环境不能直接 java -jar,要用 classpath 或 exploded 模式启动。"


落地清单

pom.xml 关键依赖清单:

pom

依赖

版本

说明

ruoyi(根)

spring-boot-dependencies

2.5.15

若依骨架自带,不是 2.7

ruoyi-wowkflow

camunda-bom

7.19.0

BOM 版本管理

ruoyi-wowkflow

camunda-bpm-spring-boot-starter

7.19.0

一行搞定,无 exclusion

ruoyi-admin

xercesImpl

2.12.2

JDK 17 + Camunda 7.19 必须

application.yml 必须改的两处:

camunda:
  bpm:
    auto-deployment-enabled: false   # 关掉自动部署,手动 addString
    database:
      schema-update: true            # 首次启动自动建 ACT_* 表

logging:
  level:
    org.camunda.bpm.engine.impl.persistence.entity: warn  # 生产关 debug


下一章第 03 章才是真正的噩梦——fat jar 下 Camunda BPMN 全炸。