Activiti流程导出工具:BPMN XML与流程图片生成实现
引言:解决流程可视化与标准化的双重挑战
在企业级工作流应用开发中,你是否经常面临以下痛点:流程设计完成后难以快速导出标准格式文档?开发与业务团队对流程理解存在偏差?线上问题排查时无法直观查看当前流程状态?Activiti作为基于BPMN 2.0的主流工作流引擎,提供了完善的流程导出机制,可同时生成标准BPMN XML文件与可视化流程图,完美解决这些问题。
读完本文后,你将掌握:
- BPMN XML文件的生成原理与定制方法
- 流程图片导出的核心实现与优化技巧
- 在Spring Boot环境中集成导出功能的完整步骤
- 高级应用场景(如动态流程调整、版本控制)的解决方案
技术原理:Activiti导出机制的底层架构
核心组件与交互流程
Activiti的流程导出功能基于两大核心模块构建:BPMN XML转换模块与流程图片生成模块。以下是其架构流程图:
BPMN XML转换核心类解析
BpmnXMLConverter是Activiti中负责BPMN模型与XML文件相互转换的核心类,位于activiti-bpmn-converter模块中。其主要功能包括:
- 模型序列化:将内存中的
BpmnModel对象转换为符合BPMN 2.0规范的XML字符串 - XML解析:将XML文件解析为
BpmnModel对象,支持流程引擎的部署与执行 - 扩展支持:通过自定义Converter支持Activiti特有的扩展属性
关键代码实现如下:
// BPMN模型转换为XML
BpmnModel model = ...; // 构建或从引擎获取BPMN模型
BpmnXMLConverter converter = new BpmnXMLConverter();
byte[] xmlBytes = converter.convertToXML(model);
String xmlString = new String(xmlBytes, StandardCharsets.UTF_8);
// XML转换为BPMN模型
InputStream xmlStream = ...; // 读取XML文件流
BpmnModel model = converter.convertToBpmnModel(new InputStreamProvider() {
@Override
public InputStream getInputStream() {
return xmlStream;
}
}, true, true);
流程图片生成机制
Activiti通过ProcessDiagramGenerator接口实现流程图的生成,默认实现为DefaultProcessDiagramGenerator,位于activiti-image-generator模块。其工作原理是:
- 从
BpmnModel中提取图形信息(GraphicInfo) - 根据不同的BPMN元素类型(用户任务、网关、事件等)应用相应的绘制规则
- 生成SVG格式的矢量图,确保在任何缩放级别下保持清晰
核心绘制逻辑在draw方法中实现,针对不同BPMN元素类型有专门的处理:
// 流程图片生成示例
ProcessDiagramGenerator generator = new DefaultProcessDiagramGenerator();
InputStream diagramStream = generator.generateDiagram(
model, // BPMN模型对象
"png", // 图片格式
Collections.emptyList(),// 高亮显示的流程节点
Collections.emptyList(),// 高亮显示的流程路径
"Arial", // 活动字体
"Arial", // 标签字体
null, // 自定义字体路径
1.0, // 缩放比例
false // 是否生成简化版流程图
);
数据流转关键节点
BPMN模型在导出过程中的数据流转涉及多个关键节点,以下是详细说明:
- BpmnModel构建:可通过XML文件解析、API编程构建或从Activiti Engine运行时获取
- 图形信息提取:从
BpmnModel的GraphicInfo集合中获取节点坐标、尺寸等布局信息 - XML生成:
BpmnXMLConverter遍历模型元素,生成符合BPMN 2.0规范的XML文档 - 图片渲染:
DefaultProcessDiagramGenerator根据图形信息绘制各个BPMN元素,生成最终图片
实现步骤:从基础集成到高级应用
环境准备与依赖配置
要在项目中集成流程导出功能,需在pom.xml中添加以下依赖:
<!-- BPMN XML转换依赖 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-converter</artifactId>
<version>7.1.0.M6</version>
</dependency>
<!-- 流程图片生成依赖 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-image-generator</artifactId>
<version>7.1.0.M6</version>
</dependency>
<!-- BPMN模型核心依赖 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-model</artifactId>
<version>7.1.0.M6</version>
</dependency>
基础实现:导出BPMN XML文件
以下是生成BPMN XML文件的完整代码示例:
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.StartEvent;
import org.activiti.bpmn.model.UserTask;
import org.activiti.bpmn.model.SequenceFlow;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
public class BpmnXmlExporter {
public static void main(String[] args) throws Exception {
// 1. 创建BPMN模型对象
BpmnModel model = new BpmnModel();
// 2. 创建流程定义
Process process = new Process();
process.setId("leave-process");
process.setName("请假流程");
model.addProcess(process);
// 3. 添加流程元素
StartEvent startEvent = new StartEvent();
startEvent.setId("start");
process.addFlowElement(startEvent);
UserTask approvalTask = new UserTask();
approvalTask.setId("approve");
approvalTask.setName("经理审批");
approvalTask.setAssignee("manager1");
process.addFlowElement(approvalTask);
// 添加流程连线
process.addFlowElement(new SequenceFlow("start", "approve"));
// 4. 生成BPMN XML
BpmnXMLConverter converter = new BpmnXMLConverter();
byte[] xmlBytes = converter.convertToXML(model);
// 5. 保存到文件
Files.write(Paths.get("leave-process.bpmn"), xmlBytes);
System.out.println("BPMN XML文件生成成功,内容如下:");
System.out.println(new String(xmlBytes, StandardCharsets.UTF_8));
}
}
生成的BPMN XML文件结构如下(简化版):
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" ...>
<process id="leave-process" name="请假流程">
<startEvent id="start"/>
<userTask id="approve" name="经理审批" activiti:assignee="manager1"/>
<sequenceFlow id="flow1" sourceRef="start" targetRef="approve"/>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_leave-process">
<!-- 图形信息省略 -->
</bpmndi:BPMNDiagram>
</definitions>
流程图片导出实现
以下是生成流程图片的完整代码,支持SVG和PNG两种格式:
import org.activiti.image.ProcessDiagramGenerator;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.parser.BpmnParse;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Collections;
public class ProcessImageExporter {
public static void exportToPng(String bpmnFilePath, String imageFilePath) throws Exception {
// 1. 读取BPMN XML文件
byte[] bpmnBytes = Files.readAllBytes(Paths.get(bpmnFilePath));
// 2. 转换为BpmnModel对象
BpmnXMLConverter converter = new BpmnXMLConverter();
BpmnModel model = converter.convertToBpmnModel(
() -> new ByteArrayInputStream(bpmnBytes),
true, true, "UTF-8"
);
// 3. 生成流程图
ProcessDiagramGenerator generator = new DefaultProcessDiagramGenerator();
try (InputStream imageStream = generator.generateDiagram(
model,
"png",
Collections.emptyList(),
Collections.emptyList(),
"Arial",
"Arial",
null,
1.0,
false)) {
// 4. 保存图片文件
try (FileOutputStream fos = new FileOutputStream(imageFilePath)) {
byte[] buffer = new byte[1024];
int len;
while ((len = imageStream.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
}
}
System.out.println("流程图片已保存至: " + imageFilePath);
}
public static void main(String[] args) throws Exception {
exportToPng("leave-process.bpmn", "leave-process.png");
}
}
Spring Boot集成:实现RESTful导出接口
在Spring Boot应用中集成流程导出功能,提供REST接口供前端调用:
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.RepositoryService;
import org.activiti.image.ProcessDiagramGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collections;
@RestController
@RequestMapping("/process-export")
public class ProcessExportController {
@Autowired
private RepositoryService repositoryService;
@Autowired
private ProcessDiagramGenerator processDiagramGenerator;
/**
* 导出BPMN XML文件
*/
@GetMapping("/{processDefinitionId}/xml")
public ResponseEntity<byte[]> exportBpmnXml(@PathVariable String processDefinitionId) {
BpmnModel model = repositoryService.getBpmnModel(processDefinitionId);
byte[] xmlBytes = new BpmnXMLConverter().convertToXML(model);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_XML);
headers.setContentDispositionFormData("attachment",
model.getMainProcess().getId() + ".bpmn");
return new ResponseEntity<>(xmlBytes, headers, HttpStatus.OK);
}
/**
* 导出流程图片
*/
@GetMapping("/{processDefinitionId}/image")
public void exportProcessImage(
@PathVariable String processDefinitionId,
HttpServletResponse response) throws IOException {
BpmnModel model = repositoryService.getBpmnModel(processDefinitionId);
response.setContentType("image/png");
response.setHeader("Content-Disposition",
"attachment; filename=\"" + model.getMainProcess().getId() + ".png\"");
try (OutputStream os = response.getOutputStream();
InputStream is = processDiagramGenerator.generateDiagram(
model, "png", Collections.emptyList(),
Collections.emptyList(), "Arial", "Arial", null, 1.0, false)) {
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
}
}
}
高级应用:定制化与性能优化
BPMN XML定制:扩展属性与命名空间
Activiti允许通过扩展属性定制BPMN XML,满足特定业务需求。以下是添加自定义扩展属性的实现方法:
// 为用户任务添加自定义表单属性
UserTask userTask = new UserTask();
userTask.setId("approve");
userTask.setName("经理审批");
// 创建扩展元素
ExtensionElement formExtension = new ExtensionElement();
formExtension.setName("formProperties");
formExtension.setNamespacePrefix("custom");
formExtension.setNamespaceUri("http://company.com/bpmn/custom");
// 添加扩展属性
formExtension.addChildElement(createExtensionElement("formKey", "leave-approval-form"));
formExtension.addChildElement(createExtensionElement("width", "800"));
formExtension.addChildElement(createExtensionElement("height", "600"));
// 将扩展元素添加到任务
userTask.addExtensionElement(formExtension);
要使自定义命名空间在导出的XML中正确显示,需在BpmnModel中注册:
model.addNamespace("custom", "http://company.com/bpmn/custom");
生成的XML将包含自定义命名空间和属性:
<userTask id="approve" name="经理审批">
<extensionElements>
<custom:formProperties>
<custom:formKey>leave-approval-form</custom:formKey>
<custom:width>800</custom:width>
<custom:height>600</custom:height>
</custom:formProperties>
</extensionElements>
</userTask>
图片生成优化:性能与清晰度平衡
对于复杂流程,图片生成可能面临性能挑战。以下是几种优化策略:
1. 按需生成与缓存机制
@Service
public class DiagramCacheService {
private final LoadingCache<String, byte[]> diagramCache;
public DiagramCacheService() {
// 创建缓存,有效期1小时,最大缓存100个流程
diagramCache = CacheBuilder.newBuilder()
.expireAfterWrite(1, TimeUnit.HOURS)
.maximumSize(100)
.build(new CacheLoader<String, byte[]>() {
@Override
public byte[] load(String processDefinitionId) throws Exception {
// 实际生成流程图的逻辑
return generateDiagramBytes(processDefinitionId);
}
});
}
public byte[] getDiagram(String processDefinitionId) throws ExecutionException {
return diagramCache.get(processDefinitionId);
}
// 其他方法...
}
2. 简化流程图生成
通过设置generateDefaultDiagram参数为true,生成简化版流程图:
InputStream imageStream = generator.generateDiagram(
model, "png", Collections.emptyList(),
Collections.emptyList(), "Arial", "Arial", null, 1.0, true); // 最后一个参数设为true
3. 异步生成与通知机制
对于特别复杂的流程,可采用异步生成策略:
@Async
public CompletableFuture<byte[]> generateDiagramAsync(String processDefinitionId) {
return CompletableFuture.supplyAsync(() -> {
try {
BpmnModel model = repositoryService.getBpmnModel(processDefinitionId);
return generator.generateDiagram(...);
} catch (Exception e) {
throw new CompletionException(e);
}
});
}
版本控制与差异比较
结合导出功能实现流程定义的版本控制:
@Service
public class ProcessVersionService {
@Autowired
private RepositoryService repositoryService;
@Autowired
private VersionRepository versionRepository;
public ProcessVersion compareVersions(String processDefinitionId, int version1, int version2) {
// 获取两个版本的流程模型
BpmnModel model1 = repositoryService.getBpmnModel(
repositoryService.createProcessDefinitionQuery()
.processDefinitionId(processDefinitionId)
.version(version1)
.singleResult()
.getId());
BpmnModel model2 = repositoryService.getBpmnModel(
repositoryService.createProcessDefinitionQuery()
.processDefinitionId(processDefinitionId)
.version(version2)
.singleResult()
.getId());
// 比较模型差异
ProcessDiff diff = compareBpmnModels(model1, model2);
// 生成差异报告
return createVersionReport(model1, model2, diff);
}
// 其他方法...
}
常见问题与解决方案
中文乱码问题
问题:生成的流程图中中文显示为方框或乱码。
解决方案:指定中文字体并确保字体文件可用:
// 方法1:使用系统已安装的中文字体
InputStream imageStream = generator.generateDiagram(
model, "png", Collections.emptyList(), Collections.emptyList(),
"SimHei", "SimHei", null, 1.0, false);
// 方法2:指定自定义字体文件路径
InputStream imageStream = generator.generateDiagram(
model, "png", Collections.emptyList(), Collections.emptyList(),
"CustomFont", "CustomFont", "classpath:fonts/simhei.ttf", 1.0, false);
大型流程性能问题
问题:包含数百个节点的大型流程导出时性能缓慢。
解决方案:
- 实现分批次导出机制
- 移除不必要的图形信息
- 使用高效的XML解析器
// 优化XML转换性能
BpmnXMLConverter converter = new BpmnXMLConverter();
converter.setValidateModel(false); // 禁用模型验证
byte[] xmlBytes = converter.convertToXML(model);
特殊元素不显示
问题:自定义BPMN元素或扩展元素在导出的图片中不显示。
解决方案:扩展DefaultProcessDiagramGenerator类:
public class CustomProcessDiagramGenerator extends DefaultProcessDiagramGenerator {
@Override
protected void drawActivity(DefaultProcessDiagramCanvas canvas, BpmnModel model, FlowNode flowNode) {
if (flowNode instanceof CustomTask) {
// 自定义任务的绘制逻辑
drawCustomTask(canvas, model, (CustomTask) flowNode);
} else {
super.drawActivity(canvas, model, flowNode);
}
}
private void drawCustomTask(DefaultProcessDiagramCanvas canvas, BpmnModel model, CustomTask task) {
GraphicInfo graphicInfo = model.getGraphicInfo(task.getId());
canvas.drawCustomTask(task.getId(), task.getName(), graphicInfo, task.getCustomIcon());
}
}
总结与最佳实践
Activiti的流程导出功能为工作流应用提供了标准化与可视化的关键支持。在实际应用中,建议遵循以下最佳实践:
- 标准化存储:始终将BPMN XML作为流程定义的权威来源,图片仅作为辅助展示
- 缓存策略:对频繁访问的流程图片实施缓存,减少重复生成开销
- 异步处理:对复杂流程采用异步生成机制,避免阻塞主线程
- 版本管理:结合导出功能实现流程定义的版本控制与变更追踪
- 安全性考虑:对导出接口实施权限控制,防止敏感流程信息泄露
通过合理利用Activiti的导出能力,不仅可以提升开发效率,还能增强系统的可维护性与可扩展性,为企业级工作流应用提供坚实的技术支撑。
未来发展方向:
- 结合AI技术实现流程图的智能优化
- 增强移动端适配的响应式流程图
- 与低代码平台深度集成,实现"所见即所得"的流程设计与导出
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



