资讯详情

JeecgBoot集成Activiti 5.22工作流引擎:避坑指南与实战配置

📅 2026/10/23 8:47:44 | 华诺云谱 👁 阅读
JeecgBoot集成Activiti 5.22工作流引擎:避坑指南与实战配置
简介这是一份面向 Java 企业级开发者的 JeecgBoot 与 Activiti5.22 集成资源包解决在快速开发平台中接入工作流引擎、实现业务流程自动化的实际需求。内容涵盖依赖配置、流程定义、部署启动、任务处理等集成环节便于开发者对照学习。资源包共 2000 个文件以 566 个 Java 源码、321 个 Vue 组件、360 个 JavaScript 脚本为主同时包含 299 个 SQL 脚本、109 个 XML 配置、78 个 JSON 数据及大量图片与样式文件覆盖后端逻辑、前端页面、数据库脚本和流程定义等完整层次压缩包约 26.83MB。已有 10321 人浏览学习适合熟悉 Spring Boot、希望快速掌握 JeecgBoot 与 Activiti 集成方法的开发者参考。通过学习可以获得可运行的集成示例包括流程建模 BPMN 文件、后端 Service/Task 处理代码、前端任务审批页面以及初始化数据库脚本有助于理解 Activiti 表结构、待办任务流转与权限结合方式缩短项目落地周期。1. 为什么要把 Activiti 塞进 JeecgBoot兼容性账本与真实场景接手过几个后台管理系统的开发者应该都有印象JeecgBoot 的代码生成器和在线表单确实能减少大量重复 CRUD但到了审批流这块团队往往会对自带的工作流实现不满意。某公司之前的老系统就是基于 Activiti 5.22 写的流程定义、监听器、任务分配器一整套都是老 API迁移到 JeecgBoot 时如果换掉引擎意味着流程 XML 全部重写这个成本没人愿意背。把 Activiti 5.22 集成进 JeecgBoot要解决的核心问题有三个依赖兼容、表结构初始化、用户体系打通。这三件事做完流程定义、任务流转、历史查询就都能跑在 JeecgBoot 的框架内同时保留系统原有的按钮权限和数据权限。适合的人群很明确需要把旧工作流迁移到 JeecgBoot 的后端开发以及想在 JeecgBoot 里独立搭建审批流的项目组。接下来从工程配置开始逐步拆到流程实例与业务表单绑定。2. 集成准备依赖引入与版本冲突排查2.1 版本选择Activiti 5.22 与 Spring Boot 的兼容边界Activiti 5.22 是 5.x 系列的后期版本它的核心依赖池里还是 Spring 4 和 MyBatis 3.2 那一套。而 JeecgBoot 基于 Spring Boot 2.x内嵌 Spring 5MyBatis 也换成了 MyBatis-Plus。直接把这个老引擎的依赖原样拖进来Maven 会同时把两套 Spring 和两套 MyBatis 都放进 classpath运行时经常出现 Bean 重复定义、mapper 扫描错乱这种玄学问题。所以版本选择上要明确一个原则只用 Activiti 5.22 的 engine 和 spring 模块其余传递依赖能排除就排除。我一般会先把项目里的 Spring Boot 版本确认好如果是 2.3.x 或 2.4.xActiviti 5.22 配合跑起来问题不太大如果是 2.7.x需要额外注意依赖冲突具体排除方式见下一节。还有一个容易忽略的点Activiti 5.22 默认带的 mybatis 版本很老JeecgBoot 里的 MyBatis-Plus 3.x 要求 mybatis 3.5 以上两者在 mapper 接口扫描上会打架。实际集成时主要用到 activiti-spring、activiti-engine、activiti-bpmn-model 这三个模块数据库连接池和事务管理继续用 JeecgBoot 自带的不另外引。2.2 引入依赖pom 里怎么写才不冲突在 JeecgBoot 的 pom.xml 里新增依赖时不能只贴一个普通坐标就完事。Activiti 5.22 的 activiti-spring 会传递引入 spring-beans、spring-context 等旧版组件这些必须显式排除。下面是我在模拟项目 X 里验证过的一份依赖配置dependency groupIdorg.activiti/groupId artifactIdactiviti-spring/artifactId version5.22.0/version exclusions exclusion groupIdorg.apache.commons/groupId artifactIdcommons-lang3/artifactId /exclusion exclusion groupIdorg.mybatis/groupId artifactIdmybatis/artifactId /exclusion exclusion groupIdorg.springframework/groupId artifactIdspring-context/artifactId /exclusion /exclusions /dependency这段配置里排除 commons-lang3 是因为 JeecgBoot 自己带的版本更新避免覆盖排除 mybatis 是让 Activiti 在使用 MyBatis-Plus 的连接环境里统一走 MyBatis-Plus 的 sqlSessionFactory否则会出现两个 SqlSessionFactory 互相抢数据源的情况排除 spring-context 则是因为 JeecgBoot 已经管理了 Spring 容器。加完依赖后建议先执行mvn dependency:tree看一眼完整的依赖树确认没有混入多余的 Spring 版本。我碰到过一次最诡异的报错是NoSuchMethodError: org.springframework.util.Assert.state最后查到原因是 Activiti 传递引入的 spring-core 把 JeecgBoot 的覆盖了。2.3 数据库初始化让 Activiti 自动建表Activiti 5.22 的表结构是固定的总共 70 多张如果手工建表一是麻烦二是容易漏掉 A 开头的运行时表。正确做法是让 Activiti 引擎在首次启动时自动建表。它支持通过databaseSchemaUpdate参数控制建表行为取值为true时如果表不存在就自动创建。在 JeecgBoot 的 application.yml 里可以先把数据源相关信息写好后给 Activiti 单独配一个配置项基本参数如下activiti: database-schema-update: true db-identity-used: false history-level: full说明一下这三个参数database-schema-update设为 true意味着只要数据库连接正常引擎启动时就会自动把ACT_GE_*、ACT_RE_*、ACT_RU_*、ACT_HI_*这一系列表全部建出来db-identity-used设为 false表示不使用 Activiti 自带的身份管理表ACT_ID_*因为我们要在后续步骤里对接 JeecgBoot 的用户表这个参数能避免 Activiti 启动时向身份表插入默认数据history-level设为 full是为了完整记录历史流程实例和变量方便后续做流程追踪。首次启动后用数据库客户端查一下表数量能查到ACT_RE_PROCDEF流程定义表、ACT_RU_TASK运行中任务表、ACT_HI_PROCINST历史流程实例表这些关键表就说明建表成功了。如果启动报错提示无法创建表先检查数据库账号有没有 DDL 权限这比排查代码更常见。3. 在 JeecgBoot 里把工作流跑起来配置与部署3.1 创建 ProcessEngine 配置类Activiti 5.22 在 Spring Boot 环境里没有官方 starter需要手动声明ProcessEngineConfiguration和ProcessEngine的 Bean。这一步是集成的核心配置错了整个引擎启动不起来。我把常用配置写成了一段 Java 代码可以直接放在 JeecgBoot 的 config 包下Configuration public class ActivitiConfig { Autowired private DataSource dataSource; Autowired private PlatformTransactionManager transactionManager; Bean public ProcessEngineConfiguration processEngineConfiguration() { SpringProcessEngineConfiguration config new SpringProcessEngineConfiguration(); config.setDataSource(dataSource); config.setTransactionManager(transactionManager); config.setDatabaseSchemaUpdate(true); config.setDbIdentityUsed(false); config.setHistoryLevel(HistoryLevel.FULL.getLevel()); config.setJobExecutorActivate(false); config.setActivityFontName(宋体); config.setLabelFontName(宋体); config.setXmlEncoding(UTF-8); return config; } Bean public ProcessEngine processEngine() { return processEngineConfiguration().buildProcessEngine(); } Bean public RepositoryService repositoryService() { return processEngine().getRepositoryService(); } Bean public RuntimeService runtimeService() { return processEngine().getRuntimeService(); } Bean public TaskService taskService() { return processEngine().getTaskService(); } Bean public HistoryService historyService() { return processEngine().getHistoryService(); } }这段配置里SpringProcessEngineConfiguration是 Activiti 集成 Spring 的关键入口它会把数据源和事务管理器交给引擎内部使用保证流程操作与业务操作在同一个事务里进行。setJobExecutorActivate(false)很重要如果你没打算用定时器事件最好关掉异步作业执行器否则引擎会起一个后台线程池日志里全是作业扫描输出而且在集群部署时容易踩到任务重复执行的坑。字体参数也得设否则流程图上中文会显示成方块这一点在 4.3 节再展开。setXmlEncoding(UTF-8)则是为了避免流程 XML 文件里定义中文名称时出现乱码。写完配置类后启动 JeecgBoot观察日志里有没有EnvironmentStandaloneProcessEngineConfiguration或ProcessEngine created的标记。如果启动成功用 RepositoryService 的对象能直接查询到流程定义列表说明引擎已经挂到 Spring 容器里了。3.2 用户体系对接让 Activiti 认账Activiti 5.22 自带一套用户和组表ACT_ID_USER、ACT_ID_GROUP但 JeecgBoot 里没有这些数据流程开始后任务分配给了某个用户的 usernameActiviti 会去自己的身份表里查查不到就会抛异常。解决办法是每次流程启动前把当前登录用户同步到 Activiti 的 IdentityService 里。同步逻辑不需要全量灌数据只要把进入流程的用户和角色写进去就行。下面是模拟项目 X 里用过的一段同步代码public void syncActivitiUser(String userId, String userName) { IdentityService identityService processEngine.getIdentityService(); User activitiUser identityService.createUserQuery().userId(userId).singleResult(); if (activitiUser null) { User newUser identityService.newUser(userId); newUser.setFirstName(userName); newUser.setLastName(); newUser.setPassword(); // 不存密码只做登录标识 identityService.saveUser(newUser); } }这段代码做的事情很直接先从 Activiti 身份表里按用户 ID 查一次不存在就创建。密码留空是因为认证完全走 JeecgBoot 的 Shiro 或 Spring Security 体系Activiti 不参与登录校验只负责任务分配和权限识别。组角色的同步逻辑类似调用identityService.saveGroup()即可。注意一点任务 assignee 字段存的是用户 ID而 candidate group 存的是角色编码。JeecgBoot 的角色编码通常在 sys_role 表里如果你在流程 XML 里写了candidateGroupsmanager务必保证manager这个角色编码同步到 Activiti 的组表里否则任务会变成无人认领。3.3 部署流程 XML 并启动流程实例Activiti 的流程定义以 BPMN 2.0 XML 文件为载体在 JeecgBoot 中常见做法是把 XML 放在resources/processes目录下用RepositoryService部署。部署代码很短Autowired private RepositoryService repositoryService; public void deployProcess(String bpmnPath) { Deployment deployment repositoryService.createDeployment() .addClasspathResource(bpmnPath) .name(流程部署-请购单审批) .deploy(); System.out.println(部署ID deployment.getId()); }这里bpmnPath传的是processes/purchase_approval.bpmn20.xml这种 classpath 路径。部署成功后流程定义表ACT_RE_PROCDEF里就有一条记录后续启动流程实例时用ProcessDefinitionKey指定用哪个定义。启动流程实例的关键是传递业务参数和当前用户Autowired private RuntimeService runtimeService; public String startProcess(String processKey, String businessKey, MapString, Object variables) { syncActivitiUser(LoginUser.getId(), LoginUser.getUsername()); ProcessInstance instance runtimeService.startProcessInstanceByKey( processKey, businessKey, variables); return instance.getId(); }businessKey是 Activiti 与业务单据的桥它可以把流程实例和 JeecgBoot 里的某张业务表主键绑在一起后续通过runtimeService.createProcessInstanceQuery().processInstanceBusinessKey(businessKey)反查流程。variables里通常放审批人、部门编码、金额等条件变量流程路径会根据这些值分支。启动后调用taskService.createTaskQuery().taskAssignee(userId).list()就能看到当前用户待办。到这里引擎跑通了但集成只是完成了一半真正让项目上线还得过一遍下面这些坑。4. 避坑排查集成后最容易翻车的六个现场4.1 启动报错表不存在或建表失败现象JeecgBoot 启动到一半日志里抛出Table act_ge_property doesnt exist或者ORA-00942: table or view does not exist。原因database-schema-update参数没传到引擎配置里。如果你没有使用上一章的配置类而是想在 application.yml 里用activiti.database-schema-update控制必须确认自定义的ProcessEngineConfigurationBean 里也调用了setDatabaseSchemaUpdate因为自定义 Bean 会覆盖默认配置。解决在配置类里硬编码config.setDatabaseSchemaUpdate(true)或者在使用SpringProcessEngineConfiguration时从Environment里读取该参数。我一般直接硬编码理由是建表只发生在首次启动生产环境开发完就该改成false。4.2 MyBatis-Plus 与 Activiti 的 Mapper 扫描冲突现象启动时出现Invalid bound statement (not found)或者org.apache.ibatis.binding.BindingException指向的是 Activiti 内部的一些 mapper 方法。原因Activiti 5.22 使用 mybatis 作为内部 SQL 映射而 JeecgBoot 的 MyBatis-Plus 配置了全局 mapper-locations扫描范围可能把 Activiti 的 mapper XML 也纳入进来或者反过来因为两个 SqlSessionFactory 导致绑定错乱。解决保证项目中只有一个SqlSessionFactory使用 MyBatis-Plus 的MybatisSqlSessionFactoryBeanMyBatis-Plus 提供的版本并且不要在全局 mybatis 配置里扫描 Activiti 的 mapper。另一种有效做法是在 2.2 节的依赖配置中排除 mybatis 传递依赖实测这两种方式组合使用最稳。4.3 流程图上中文变成方块现象调用repositoryService.getProcessDiagram()生成的流程图片上所有节点名称显示为空白方块或问号。原因JVMLocale 问题加上 Activiti 默认字体不支持中文字符。Activiti 在生成图片时用 Java AWT 渲染文本默认字体SansSerif在部分 Linux 中文环境下不可用。解决在配置类里设置config.setActivityFontName(宋体)和config.setLabelFontName(宋体)并在启动参数里加上-Dfile.encodingUTF-8。如果你服务器上连宋体都没有装个中文字体包或者改用Noto Sans CJK SC。这个坑在 Windows 本机上不容易发现一旦上线部署到 CentOS立刻现形。4.4 流程操作与业务操作不在同一事务现象流程操作成功但业务数据回滚后流程还是往下走了或者反过来业务流程断了但待办任务还存在。原因Activiti 的服务方法独立开了事务没有和 JeecgBoot 的Transactional控制的业务事务合并。在 3.1 配置类里如果没有把transactionManager传给SpringProcessEngineConfiguration引擎会自己另起事务。解决确认配置类里config.setTransactionManager(transactionManager)已设置并且processEngineConfiguration()里注入的 PlatformTransactionManager 与业务层Transactional用的是同一个。用一个简单测试验证在流程启动代码里故意抛一个RuntimeException看看流程实例有没有回滚掉。4.5 多数据源环境下 Activiti 表被建到了错误库现象JeecgBoot 配置了主库和从库启动后 Activiti 没有在主库建表反而在从库或报表库里生成了 ACT 前缀的表。原因DataSourceBean 注入时多数据源场景下 Spring 不知道该用哪个Primary标记没加或者注入的 dataSource 是动态数据源路由对象。解决在配置类注释掉多个数据源或者给主数据源显式加Primary注解保证processEngineConfiguration()里注入的是主数据源。涉及动态数据源时更麻烦Activiti 5.22 不适合直接在动态路由上跑我通常单独准备一个dataSourceBean 给 Activiti 专用路由切换交给业务层。4.6 流程历史数据一直膨胀现象上线三个月后ACT_HI_ACTINST表数据量巨大查询历史流程越来越慢。原因history-level配成了full并且没有做历史数据归档清理策略。在没有定时器清理的情况下每一步活动的实例都会留记录。解决按需降低历史级别为audit大部分场景够用少记录活动细节或者写一个定时任务每天清理三个月前的历史表数据。清理时注意先删ACT_HI_ACTINST再删父表避免外键约束问题。5. 进阶用法把流程实例和业务单据绑在一起5.1 用 businessKey 解决业务表单回查很多团队在集成工作流之后最大的困惑是流程跑起来了但流程实例和 JeecgBoot 里的业务单据对不上号。其实不需要额外建关联表直接在启动流程时把业务主键传给businessKey就够了。我这里把启程逻辑封装成一个公共服务方便所有业务模块复用。流程启动后通过下面的代码反查当前流程关联的业务单号public String getBusinessKeyByProcessInstanceId(String processInstanceId) { HistoricProcessInstance hisInstance historyService .createHistoricProcessInstanceQuery() .processInstanceId(processInstanceId) .singleResult(); return hisInstance.getBusinessKey(); }这段查询走的是历史表流程结束后依然能查到业务单据主键用来处理已办列表、流程追溯、业务单据详情回填都非常好使。5.2 待办任务与表单渲染的联动流程推进到某个用户节点的待办里通常要做两件事一是把流程变量带出最近一次审批意见二是将待办 URL 拼上单据 ID。这里的关键在于待办列表里的内容应该来自业务表而不是 Activiti 的任务表。因为业务字段总是比任务表字段丰富。我在项目里习惯用一个 VO 组合查询TaskService 查任务 ID 和流程实例 ID再用 businessKey 反查 JeecgBoot 的业务表。核心代码如下public ListTodoVO findTodoListByUser(String userId) { ListTask tasks taskService.createTaskQuery() .taskAssignee(userId) .orderByTaskCreateTime() .desc() .list(); ListTodoVO result new ArrayList(); for (Task task : tasks) { String processInstanceId task.getProcessInstanceId(); HistoricProcessInstance hisInstance historyService .createHistoricProcessInstanceQuery() .processInstanceId(processInstanceId) .singleResult(); TodoVO vo new TodoVO(); vo.setTaskId(task.getId()); vo.setProcessInstanceId(processInstanceId); vo.setBusinessKey(hisInstance.getBusinessKey()); vo.setTaskName(task.getName()); vo.setCreateTime(task.getCreateTime()); result.add(vo); } return result; }这段代码里有一个性能隐患循环里逐条调用 HistoryService 查询待办量大的时候会触发 N1 查询。我一般会先用runtimeService.createProcessInstanceQuery().processInstanceIds(ids)一次性把流程实例查出来再转成 Map 关联这里为了表达思路用了单条查询你在生产环境要改成批量查询。最后提醒一个容易忽略的点审批通过或驳回后要主动调用taskService.complete(taskId, variables)传入审批结论变量并在流程 XML 里配置好条件表达式连接线 Condition 上写${pass true}否则流程永远停在当前节点。每次流程结束后强制查一次ACT_HI_PROCINST表确认流程走到END_EVENT而不是卡在未知状态。希望这套集成笔记能帮你少走几趟弯路把那层黑匣子揭开让 JeecgBoot 里的流程链路跑得明明白白。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑