骆驼任务Flowable + Apache Camel 集成

项目采用 Spring Boot 2.7 + Flowable 6.8 + Camel 3.20,演示如何让 BPMN 流程通过 Camel Task 调用外部 HTTP 接口。


一、项目结构

复制代码
flowable-camel-demo/
├── pom.xml
└── src/main/
    ├── java/com/example/demo/
    │   ├── FlowableCamelApplication.java
    │   ├── config/
    │   │   └── FlowableCamelConfiguration.java   ← 桥接配置
    │   ├── route/
    │   │   └── HttpCamelRoute.java               ← Camel路由
    │   └── controller/
    │       └── ProcessController.java            ← 测试接口
    └── resources/
        ├── application.yml
        └── processes/
            └── camel-http-demo.bpmn20.xml

二、pom.xml(关键依赖)

XML 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.18</version>
    </parent>
    
    <groupId>com.example</groupId>
    <artifactId>flowable-camel-demo</artifactId>
    <version>1.0.0</version>
    
    <properties>
        <java.version>1.8</java.version>
        <flowable.version>6.8.0</flowable.version>
        <camel.version>3.20.9</camel.version>
    </properties>
    
    <dependencies>
        <!-- Flowable 流程引擎 -->
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-spring-boot-starter-process</artifactId>
            <version>${flowable.version}</version>
        </dependency>
        
        <!-- Flowable Camel 集成模块(核心) -->
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-camel</artifactId>
            <version>${flowable.version}</version>
        </dependency>
        
        <!-- Camel Spring Boot 启动器 -->
        <dependency>
            <groupId>org.apache.camel.springboot</groupId>
            <artifactId>camel-spring-boot-starter</artifactId>
            <version>${camel.version}</version>
        </dependency>
        
        <!-- Camel HTTP 组件(用于演示调用外部接口) -->
        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-http</artifactId>
            <version>${camel.version}</version>
        </dependency>
        
        <!-- Web + H2 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

三、application.yml

XML 复制代码
server:
  port: 8080

spring:
  application:
    name: flowable-camel-demo

flowable:
  database-schema-update: true
  deployment:
    resources: classpath*:processes/*.bpmn20.xml
  async-executor-activate: true

# Camel 配置
camel:
  springboot:
    # 启动时自动加载 routes 目录下的路由
    routes-include-pattern: classpath:route/*.xml
  component:
    servlet:
      mapping:
        context-path: /camel/*

logging:
  level:
    org.flowable: info
    com.example.demo: debug
    org.apache.camel: info

四、核心:桥接 Flowable 与 Camel

Flowable 和 Camel 是两个独立的运行时,必须通过 FlowableComponent 建立通信桥梁。

java 复制代码
package com.example.demo.config;

import org.apache.camel.CamelContext;
import org.apache.camel.spring.boot.CamelContextConfiguration;
import org.flowable.camel.FlowableComponent;
import org.flowable.engine.RuntimeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 将 Flowable 的 RuntimeService 注入到 Camel 的 FlowableComponent 中
 * 这是两者能互相通信的关键
 */
@Component
public class FlowableCamelConfiguration implements CamelContextConfiguration {

    @Autowired
    private RuntimeService runtimeService;

    @Override
    public void beforeApplicationStart(CamelContext camelContext) {
        FlowableComponent flowableComponent = new FlowableComponent();
        flowableComponent.setRuntimeService(runtimeService);
        
        // 注册到 CamelContext,协议名为 "flowable"
        camelContext.addComponent("flowable", flowableComponent);
    }

    @Override
    public void afterApplicationStart(CamelContext camelContext) {
        // 启动后的回调,通常留空
    }
}

五、Camel 路由定义

这是最核心的业务代码。路由监听来自 Flowable 的消息,调用外部 HTTP 接口,再把结果返回给流程。

java 复制代码
package com.example.demo.route;

import org.apache.camel.Exchange;
import org.apache.camel.builder.RouteBuilder;
import org.springframework.stereotype.Component;

@Component
public class HttpCamelRoute extends RouteBuilder {

    @Override
    public void configure() throws Exception {
        
        // ============================================
        // 监听来自 Flowable 的消息
        // ============================================
        // URI 规则: flowable:processDefinitionKey
        // copyVariablesToProperties=true: 把流程变量复制到 Exchange properties
        // copyVariablesFromProperties=true: 把 Exchange properties 复制回流程变量
        // ============================================
        from("flowable:camelHttpDemo?copyVariablesToProperties=true&copyVariablesFromProperties=true")
            .routeId("flowable-http-route")
            .log(">>> [Camel] 收到 Flowable 消息")
            .log(">>> [Camel] 流程变量 orderId: ${exchangeProperty.orderId}")
            .log(">>> [Camel] 流程变量 userName: ${exchangeProperty.userName}")
            
            // 清理 Camel 自动添加的 HTTP 头,避免污染请求
            .removeHeaders("CamelHttp*")
            
            // 设置请求方法
            .setHeader(Exchange.HTTP_METHOD, constant("GET"))
            
            // 动态调用外部 HTTP 接口(这里用 httpbin.org 做演示)
            // exchangeProperty.xxx 可以读取 Flowable 传入的流程变量
            .toD("https://httpbin.org/get?orderId=${exchangeProperty.orderId}&user=${exchangeProperty.userName}")
            
            // 将 HTTP 响应体保存到 Exchange property,Flowable 可以取回
            .setProperty("httpResponseBody", body())
            .setProperty("httpResponseStatus", simple("${header.CamelHttpResponseCode}"))
            
            // 最终 Exchange Body 会被 Flowable 自动存入流程变量 "camelBody"
            .setBody(simple("HTTP调用完成,状态码: ${header.CamelHttpResponseCode}"));
    }
}

六、BPMN 流程定义

放到 src/main/resources/processes/camel-http-demo.bpmn20.xml

XML 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:flowable="http://flowable.org/bpmn"
             targetNamespace="http://example.com/camel-http-demo">

    <process id="camelHttpDemo" name="Camel HTTP 演示流程" isExecutable="true">
        
        <!-- 开始 -->
        <startEvent id="start" name="开始" />
        <sequenceFlow id="f1" sourceRef="start" targetRef="camelTask" />

        <!-- ========================================== -->
        <!--  Camel 服务任务:核心配置在这里              -->
        <!-- ========================================== -->
        <serviceTask id="camelTask" name="Camel调用外部API" 
                     flowable:type="camel">
            <extensionElements>
                <!-- 指定 CamelContext 的 bean 名称(默认就是 camelContext) -->
                <flowable:field name="camelContext">
                    <flowable:string>camelContext</flowable:string>
                </flowable:field>
            </extensionElements>
        </serviceTask>

        <sequenceFlow id="f2" sourceRef="camelTask" targetRef="checkResult" />

        <!-- 排他网关:根据 Camel 返回结果分流 -->
        <exclusiveGateway id="checkResult" name="判断结果" />
        
        <!-- 成功分支:camelBody 变量是 Camel 路由最终返回的 Body -->
        <sequenceFlow id="f3" sourceRef="checkResult" targetRef="successTask">
            <conditionExpression xsi:type="tFormalExpression">
                ${camelBody != null}
            </conditionExpression>
        </sequenceFlow>
        
        <!-- 失败分支 -->
        <sequenceFlow id="f4" sourceRef="checkResult" targetRef="failEnd">
            <conditionExpression xsi:type="tFormalExpression">
                ${camelBody == null}
            </conditionExpression>
        </sequenceFlow>

        <!-- 成功处理(模拟业务处理) -->
        <serviceTask id="successTask" name="处理成功结果" 
                     flowable:delegateExpression="${successHandler}" />
        <sequenceFlow id="f5" sourceRef="successTask" targetRef="endSuccess" />
        <endEvent id="endSuccess" name="成功结束" />

        <!-- 失败结束 -->
        <endEvent id="failEnd" name="失败结束" />

        <!-- 边界错误事件:捕获 Camel 路由中的异常 -->
        <boundaryEvent id="camelError" attachedToRef="camelTask" cancelActivity="true">
            <errorEventDefinition errorRef="CAMEL_ERROR" />
        </boundaryEvent>
        
        <sequenceFlow id="f6" sourceRef="camelError" targetRef="errorEnd" />
        <endEvent id="errorEnd" name="异常结束" />

    </process>
</definitions>

七、辅助类与启动类

SuccessHandler.java(成功后的业务处理)

java 复制代码
package com.example.demo.delegate;

import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component("successHandler")
public class SuccessHandler implements JavaDelegate {
    
    private static final Logger logger = LoggerFactory.getLogger(SuccessHandler.class);
    
    @Override
    public void execute(DelegateExecution execution) {
        String camelBody = (String) execution.getVariable("camelBody");
        String httpResp = (String) execution.getVariable("httpResponseBody");
        
        logger.info("[业务处理] Camel返回: {}", camelBody);
        logger.info("[业务处理] HTTP原始响应: {}", httpResp);
    }
}

启动类

java 复制代码
package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class FlowableCamelApplication {
    public static void main(String[] args) {
        SpringApplication.run(FlowableCamelApplication.class, args);
    }
}

八、测试接口

java 复制代码
package com.example.demo.controller;

import org.flowable.engine.RuntimeService;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/api")
public class ProcessController {

    @Autowired
    private RuntimeService runtimeService;

    /**
     * 启动流程,触发 Camel HTTP 调用
     * 
     * 测试请求:
     * curl -X POST http://localhost:8080/api/start \
     *   -H "Content-Type: application/json" \
     *   -d '{"orderId":"ORDER_9527","userName":"zhangsan"}'
     */
    @PostMapping("/start")
    public Map<String, Object> startProcess(@RequestBody Map<String, Object> params) {
        Map<String, Object> variables = new HashMap<>();
        
        // 这些变量会被自动传递到 Camel 路由的 Exchange properties 中
        variables.put("orderId", params.getOrDefault("orderId", "TEST_001"));
        variables.put("userName", params.getOrDefault("userName", "admin"));

        ProcessInstance instance = runtimeService
            .startProcessInstanceByKey("camelHttpDemo", variables);

        // 读取 Camel 返回的流程变量
        Map<String, Object> processVars = runtimeService.getVariables(instance.getId());

        Map<String, Object> result = new HashMap<>();
        result.put("processInstanceId", instance.getId());
        result.put("camelBody", processVars.get("camelBody"));           // Camel路由最终Body
        result.put("httpResponseBody", processVars.get("httpResponseBody")); // HTTP原始响应
        result.put("httpResponseStatus", processVars.get("httpResponseStatus")); // HTTP状态码
        return result;
    }
}

九、运行与验证

1. 启动项目

直接运行 FlowableCamelApplication.main(),控制台会看到:

复制代码
>>> [Camel] 收到 Flowable 消息
>>> [Camel] 流程变量 orderId: ORDER_9527

2. 发起测试

复制代码
curl -X POST http://localhost:8080/api/start \
  -H "Content-Type: application/json" \
  -d '{"orderId":"ORDER_9527","userName":"zhangsan"}'

3. 返回结果

JSON

复制代码
{
  "processInstanceId": "2501",
  "camelBody": "HTTP调用完成,状态码: 200",
  "httpResponseBody": "{\"args\":{\"orderId\":\"ORDER_9527\",\"user\":\"zhangsan\"},...}",
  "httpResponseStatus": "200"
}

十、变量映射对照表(核心理解)

这是学习 Flowable-Camel 集成的关键,必须掌握:

方向 Flowable 侧 Camel 侧 配置控制
Flowable → Camel 流程变量(如 orderId Exchange Properties copyVariablesToProperties=true
Flowable → Camel 流程变量 Map Exchange Body copyVariablesToBody=true(默认false)
Camel → Flowable 流程变量 camelBody Exchange Body 自动映射,无需配置
Camel → Flowable 流程变量(任意名) Exchange Properties copyVariablesFromProperties=true

Endpoint URI 参数说明:

复制代码
from("flowable:camelHttpDemo?copyVariablesToProperties=true&copyVariablesFromProperties=true")
  • copyVariablesToProperties=true :启动时把 Flowable 所有流程变量塞进 Camel Exchange 的 properties,路由中通过 ${exchangeProperty.xxx} 读取。

  • copyVariablesFromProperties=true:路由结束后,把 Exchange properties 里新增的/修改的值写回 Flowable 流程变量。


十一、进阶:换成 Kafka / MQ / FTP

Camel 的优势在于换组件只需改路由,BPMN 完全不用动

例如把 HTTP 换成发 Kafka 消息:

java 复制代码
@Component
public class KafkaCamelRoute extends RouteBuilder {
    @Override
    public void configure() throws Exception {
        from("flowable:camelHttpDemo?copyVariablesToProperties=true")
            .log("发送订单到Kafka: ${exchangeProperty.orderId}")
            // 只需改这一行,BPMN 中的 camelTask 完全不用改
            .to("kafka:order-topic?brokers=localhost:9092")
            .setBody(constant("KAFKA_SENT"));
    }
}

十二、总结

步骤 做什么
1. 加依赖 flowable-camel + camel-spring-boot-starter + 具体协议组件(http/kafka)
2. 建桥梁 FlowableComponentRuntimeService 注册到 CamelContext
3. 写路由 from("flowable:processDefinitionKey") 监听流程消息,.to("xxx") 调用外部系统
4. 画流程 BPMN 中 serviceTask 设置 flowable:type="camel"
5. 传变量 通过 copyVariablesToProperties / copyVariablesFromProperties 双向交换数据

一句话记忆 :Flowable 的 Camel Task 就是一个**"消息发射器"**,它把流程变量打包发给 Camel 路由;Camel 处理完后把结果写回流程变量。BPMN 负责编排,Camel 负责对接外部世界。

相关推荐
lichuangcsdn2 小时前
【Spring AI 学习(一)】spring ai是什么
人工智能·学习·spring·spring ai
代码不会写1 天前
Apache Iceberg:架构原理、读写机制、性能优化与生态
性能优化·架构·apache·iceberg
吃饱了得干活1 天前
@Transactional 又失效了?把这 8 个坑全填了!
java·后端·spring
今天AI了吗1 天前
Spring AI 框架实战:Java 后端集成大模型的架构设计与工程落地
java·人工智能·python·spring·机器学习
乐观的Terry1 天前
9、发布系统-Webhook自动发布
java·spring boot·spring·spring cloud·mybatis
一路向北North1 天前
Spring AI(5) :对话机器人-会话记忆
java·后端·spring
Q渡劫1 天前
MinIO 从下载到集成 Spring Boot 完整教程
spring boot·spring·maven
lymboy1 天前
Spring AI的工具调用循环,本质就是ReAct:一次源码级拆解
人工智能·spring·react.js
一路向北North1 天前
Spring AI(6) :对话机器人-会话历史
java·人工智能·spring