工作流容器化的依赖注入!Activiti集成CDI实现工作流的可配置性和可扩展性

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
简介: 本篇文章介绍了工作流集成CDI实现工作流的可配置性和可扩展性。详细说明的CDI的基本设置来配置流程引擎并发布流程。通过基于CDI的流程的执行,实现与流程实例的交互,声明式流程控制,在流程中使用Bean,注入流程变量和接收流程事件的功能。通过这篇文章学习,可以学会Activiti的使用进阶。

Activiti工作流集成CDI简介

  • activiti-cdi模块提供activiti的可配置型和cdi扩展
  • activiti-cdi的特性:

    • 支持 @BusinessProcessScoped beans, 绑定到流程实例的cdi bean
    • 流程为cdi bean支持自定义EL处理器
    • 使用注解为流程实例提供声明式控制
    • Activiti可以挂接在cdi事件总线上
    • 支持Java EEJava SE, 支持Spring
    • 支持单元测试
  • 要在maven项目中使用activiti-cdi,需要添加依赖:
<dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-cdi</artifactId>
        <version>5.8</version>
</dependency>
  • activiti-cdi 5.6以上的版本会自动加入activiti-entinspring

设置activiti-cdi

  • Activiti cdi可以安装在不同环境中

查找流程引擎

  • cdi扩展需要访问到ProcessEngine, 为了实现此功能:

    • 使用org.activiti.cdi.spi.ProcessEngineLookup接口在运行期间进行查找
    • cdi模块使用默认的名为org.activiti.cdi.impl.LocalProcessEngineLookup的实现,使用ProcessEngines这个工具类来查找ProcessEngine
    • 默认配置下,使用ProcessEngines#NAME_DEFAULT来查找ProcessEngine.这个类可能是使用自定义名称的子类
    • ==注意:== 需要把activiti.cfg.xml放在classpath
  • Activiti cdi使用java.util.ServiceLoader SPI处理org.activiti.cdi.spi.ProcessEngineLookup的实例

    • 为了提供接口的自定义实现,需要创建一个文本文件,名为META-INF/services/org.activiti.cdi.spi.ProcessEngineLookup, 在文件中需要指定实现的全类名
    • 如果你没有提供自定义的org.activiti.cdi.spi.ProcessEngineLookup实现,activiti会使用默认的LocalProcessEngineLookup实现,需要做的就是把activiti.cfg.xml放到classpath

配置Process Engine

  • 实际的配置依赖于选用的ProcessEngineLookup策略
  • 在这里主要结合LocalProcessEngineLookup讨论可用的配置,要求在classpath下提供一个springactiviti.cfg.xml
  • Activiti提供了不同的ProcessEngineConfiguration实现,主要是依赖实际使用的事务管理策略
  • activiti-cdi模块对事务的要求不严格,意味着任何事务管理策略都可以使用,即便是spring事务抽象层
  • cdi模块提供两种自定义ProcessEngineConfiguration实现:

    • org.activiti.cdi.CdiJtaProcessEngineConfiguration: activiti的JtaProcessEngineConfiguration的子类,用于在activiti使用JTA管理的事务环境
    • org.activiti.cdi.CdiStandaloneProcessEngineConfiguration: activiti的StandaloneProcessEngineConfiguration的子类,用于在activiti使用简单JDBC事务环境
  • JBoss7下的activiti.cfg.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

        <!-- lookup the JTA-Transaction manager -->
        <bean id="transactionManager" class="org.springframework.jndi.JndiObjectFactoryBean">
                <property name="jndiName" value="java:jboss/TransactionManager"></property>
                <property name="resourceRef" value="true" />
        </bean>

        <!-- process engine configuration -->
        <bean id="processEngineConfiguration"
                class="org.activiti.cdi.CdiJtaProcessEngineConfiguration">
                <!-- lookup the default Jboss datasource -->
                <property name="dataSourceJndiName" value="java:jboss/datasources/ExampleDS" />
                <property name="databaseType" value="h2" />
                <property name="transactionManager" ref="transactionManager" />
                <!-- using externally managed transactions -->
                <property name="transactionsExternallyManaged" value="true" />
                <property name="databaseSchemaUpdate" value="true" />
        </bean>
</beans>
  • 在Glassfish 3.1.1,假设配置好名为jdbc/activiti的datasource:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

        <!-- lookup the JTA-Transaction manager -->
        <bean id="transactionManager" class="org.springframework.jndi.JndiObjectFactoryBean">
                <property name="jndiName" value="java:appserver/TransactionManager"></property>
                <property name="resourceRef" value="true" />
        </bean>

        <!-- process engine configuration -->
        <bean id="processEngineConfiguration"
                class="org.activiti.cdi.CdiJtaProcessEngineConfiguration">
                <property name="dataSourceJndiName" value="jdbc/activiti" />
                <property name="transactionManager" ref="transactionManager" />
                <!-- using externally managed transactions -->
                <property name="transactionsExternallyManaged" value="true" />
                <property name="databaseSchemaUpdate" value="true" />
        </bean>
</beans>
  • 注意: 上面的配置要引入spring-context模块依赖
<dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>3.0.3.RELEASE</version>
</dependency>

发布流程

  • 可以使用标准的activiti-api发布流程-RepositoryService
  • activiti-cdi也提供了自动发布classpathprocesses.xml中列出的流程的方式
  • processes.xml:
<?xml version="1.0" encoding="utf-8" ?>
<!-- list the processes to be deployed -->
<processes>
        <process resource="diagrams/myProcess.bpmn20.xml" />
        <process resource="diagrams/myOtherProcess.bpmn20.xml" />
</processes>

基于CDI环境的流程执行

  • BPMN业务流程通常是一个长时间运行的操作,包含了用户和系统任务的操作
  • 运行过程中,流程会分成多个单独的工作单元,由用户和应用逻辑执行
  • activiti-cdi中,流程实例可以分配到cdi环境中,关联展现成一个工作单元:

    • 这是非常有用的,如果工作单元太复杂:比如如果实现的用户任务是不同形式的复杂顺序,可以在这个操作中保持non-process-scoped状态
    • 默认配置下,流程实例分配到broadest激活环境,就会启动交互,如果交互环境没有激活,就会返回到请求中

与流程实例进行关联交互

  • 处理 @BusinessProcessScoped beans, 或注入流程变量时,实现了激活的cdi环境与流程实例的关联
  • Activiti-cdi提供了org.activiti.cdi.BusinessProcess bean来控制关联:

    • startProcessByXx(...): 对应activiti的RuntimeService中的相关方法,允许启动和随后向关联的业务流程
    • resumeProcessById(String processInstanceId): 允许通过提供的Id来关联流程实例
    • resumeTaskById(String taskId): 允许通过提供的Id来关联任务,也可以扩展关联流程实例
  • 一个工作单元完成后 ,completeTask() 方法可以调用来解除流程实例和会话或请求的关联.这会通知activiti当前任务已经完成,并让流程实例继续执行
  • BusinessProcess bean@Named bean, 意思是导出的方法可以通过表达式语言调用:

    • 比如在JSF页面中.下面的JSF 2 代码启动一个新的交互,分配给一个用户任务实例,Id作为一个请求参数传递:
<f:metadata>
<f:viewParam name="taskId" />
<f:event type="preRenderView" listener="#{businessProcess.startTask(taskId, true)}" />
</f:metadata>

声明式流程控制

  • Activiti-cdi允许通过注解声明启动流程实例和完成任务
  • @org.activiti.cdi.annotation.StartProcess注解允许通过keyname启动流程实例.流程实例会在注解的方法返回之后启动:
@StartProcess("authorizeBusinessTripRequest")
public String submitRequest(BusinessTripRequest request) {
        // do some work
        return "success";
}
  • 根据activiti的配置,注解方法的代码和启动流程实例会在同一个事务中执行 .@org.activiti.cdi.annotation.CompleteTask事务的使用方式相同:
@CompleteTask(endConversation=false)
public String authorizeBusinessTrip() {
        // do some work
        return "success";
}

@CompleteTask注解可以结束当前会话.默认行为会在activiti返回后结束会话.可以禁用结束会话的功能

在流程中引用bean

  • Activiti-cdi使用自定义解析器把CDI bean暴露到activiti El中,可以在流程中引用这些bean:
<userTask id="authorizeBusinessTrip" name="Authorize Business Trip"
                        activiti:assignee="#{authorizingManager.account.username}" />
  • authorizingManager可以是生产者方法提供的bean:
@Inject @ProcessVariable Object businessTripRequesterUsername;

@Produces
@Named
public Employee authorizingManager() {
        TypedQuery<Employee> query = entityManager.createQuery("SELECT e FROM Employee e WHERE e.account.username='"
                + businessTripRequesterUsername + "'", Employee.class);
        Employee employee = query.getSingleResult();
        return employee.getManager();

使用@BusinessProcessScoped beans

  • 使用activiti-cdi,bean的生命周期可以绑定到流程实例上:

    • 可以提供一个自定义的环境实现,命名为BusinessProcessContext.
    • BusinessProcessScoped bean的实例会作为流程变量保存到当前流程实例中
    • BusinessProcessScoped bean需要是PassivationCapable,比如序列化
  • 使用流程作用域bean的示例如下:
@Named
@BusinessProcessScoped
public class BusinessTripRequest implements Serializable {
        private static final long serialVersionUID = 1L;
        private String startDate;
        private String endDate;
        // ...
}
  • 有时,需要使用流程作用域bean,没有与流程实例关联:

    • 比如启动流程之前.如果当前流程实例没有激活 ,BusinessProcessScoped bean实例会暂时保存在局部作用域里:

      • 会话
      • 请求
      • 依赖环境
  • 如果作用域后来与业务流程实例关联了,bean实例会刷新到流程实例里

注入流程变量

  • 流程变量可以实现用于注入
  • Activiti-CDI支持以下注入流程变量的方式:

    • @BusinessProcessScoped使用 @Inject [附加修饰] 类型 属性名实现类型安全的流程变量的注入
    • 使用@ProcessVariable(name)修饰符实现对类型不安全的流程变量的注入
@Inject @ProcessVariable Object accountNumber;
@Inject @ProcessVariable("accountNumber") Object account
  • 为了通过EL引用流程变量, 可以使用如下方式:

    • @Named @BusinessProcessScoped beans可以直接引用
    • 其他流程变量可以使用ProcessVariables bean来使用
#{processVariables['accountNumber']}

接收流程事件

  • Activiti可以挂在CDI的事件总线上,就可以使用标准CDI事件机制来监听流程事件
  • 为了启用activiti的CDI事件支持,需要在配置中启用对应的解析监听器:
<property name="postBpmnParseHandlers">
        <list>
                <bean class="org.activiti.cdi.impl.event.CdiEventSupportBpmnParseHandler" />
        </list>
</property>
  • 这样activiti就配置成了使用CDI事件总线发布事件
  • CDI bean中处理事件的方式:

    • 使用@Observes注解声明特定的事件监听器
    • 事件监听是类型安全的
    • 流程事件类型是org.activiti.cdi.BusinessProcessEvent
  • 一个简单事件监听方法示例:
public void onProcessEvent(@Observes BusinessProcessEvent businessProcessEvent) {
        // handle event
}
  • 监听器可以监听所有事件.如果想限制监听器接收的事件类型,可以添加修饰注解:

    • @BusinessProcess: 限制指定流程定义的事件

      • @Observes @BusinessProcess("billingProcess")
    • @StartActivity: 限制指定进入环节的事件

      • @Observes @StartActivity("shipGoods")
    • @EndActivity: 限制指定结束环节的事件

      • @Observes @EndActivity("shipGoods")
    • @TakeTransition: 限制指定连线的事件
  • 修饰命名可以自由组合:

    • 为了接收shipmentProcess流程中所有离开shipGoods环节的事件:
public void beforeShippingGoods(@Observes @BusinessProcess("shippingProcess") @EndActivity("shipGoods") BusinessProcessEvent evt) {
        // handle event
}
  • 默认配置下,事件监听器是同步调用,并在同一个事务环境中
  • CDI事务性监听器可以控制监听器什么时候处理事件:

    • 可以保证监听器只在事件中的事务成功之后才处理
public void onShipmentSuceeded(@Observes(during=TransactionPhase.AFTER_SUCCESS) @BusinessProcess("shippingProcess") @EndActivity("shipGoods") BusinessProcessEvent evt) {
        // send email to customer.
}

Activiti CDI中的更多功能

  • 流程引擎和服务都可以注入: Inject ProcessEngine,RepositoryService,TaskService,...
  • 当前流程实例和任务可以注入: @Inject ProcessInstance, Task
  • 当前业务标识可以注入: @Inject @BusinessKey String businessKey
  • 当前流程实例id可以注入: @Inject @ProcessInstanceId String pid
相关文章
|
2月前
|
Java Maven
2022最新版超详细的Maven下载配置教程、IDEA中集成maven(包含图解过程)、以及导入项目时jar包下载不成功的问题解决
这篇文章是一份关于Maven的安装和配置指南,包括下载、环境变量设置、配置文件修改、IDEA集成Maven以及解决jar包下载问题的方法。
2022最新版超详细的Maven下载配置教程、IDEA中集成maven(包含图解过程)、以及导入项目时jar包下载不成功的问题解决
|
2月前
|
消息中间件 弹性计算 Kubernetes
RabbitMQ与容器化技术的集成实践
【8月更文第28天】RabbitMQ 是一个开源消息代理和队列服务器,用于在分布式系统中存储、转发消息。随着微服务架构的普及,容器化技术(如 Docker 和 Kubernetes)成为了部署和管理应用程序的标准方式。本文将探讨如何使用 Docker 和 Kubernetes 在生产环境中部署和管理 RabbitMQ 服务,同时保证高可用性和弹性伸缩能力。
39 3
|
3月前
|
NoSQL Java Nacos
SpringCloud集成Seata并使用Nacos做注册中心与配置中心
SpringCloud集成Seata并使用Nacos做注册中心与配置中心
82 3
|
2月前
|
持续交付 jenkins Devops
WPF与DevOps的完美邂逅:从Jenkins配置到自动化部署,全流程解析持续集成与持续交付的最佳实践
【8月更文挑战第31天】WPF与DevOps的结合开启了软件生命周期管理的新篇章。通过Jenkins等CI/CD工具,实现从代码提交到自动构建、测试及部署的全流程自动化。本文详细介绍了如何配置Jenkins来管理WPF项目的构建任务,确保每次代码提交都能触发自动化流程,提升开发效率和代码质量。这一方法不仅简化了开发流程,还加强了团队协作,是WPF开发者拥抱DevOps文化的理想指南。
51 1
|
2月前
|
jenkins 持续交付 网络安全
利用 Jenkins 实现持续集成与持续部署-代码拉取终端的配置
【8月更文挑战第30天】在Jenkins服务器中,git和Gitee是常用的代码拉取终端。Git作为分布式版本控制系统,具备出色的灵活性和可扩展性;而Gitee则在国内网络环境下表现更佳,适合团队协作。Git配置包括安装、设置用户信息及生成SSH密钥等步骤;Gitee配置也类似,需注册账号、创建仓库、配置基本信息并设置远程仓库地址。开发人员提交代码后,可通过Webhook、定时轮询或事件监听等方式触发Jenkins动作,确保持续集成和部署高效运行。正确配置这些触发机制并通过测试验证其有效性至关重要。
54 2
|
1月前
|
jenkins 持续交付 网络安全
利用 Jenkins 实现持续集成与持续部署-代码拉取终端的配置
安装Git、配置用户信息、生成SSH密钥以及在Gitee上创建项目仓库等。
54 0
|
2月前
|
持续交付 jenkins C#
“WPF与DevOps深度融合:从Jenkins配置到自动化部署全流程解析,助你实现持续集成与持续交付的无缝衔接”
【8月更文挑战第31天】本文详细介绍如何在Windows Presentation Foundation(WPF)项目中应用DevOps实践,实现自动化部署与持续集成。通过具体代码示例和步骤指导,介绍选择Jenkins作为CI/CD工具,结合Git进行源码管理,配置构建任务、触发器、环境、构建步骤、测试及部署等环节,显著提升开发效率和代码质量。
48 0
|
3月前
|
监控 druid Java
spring boot 集成配置阿里 Druid监控配置
spring boot 集成配置阿里 Druid监控配置
200 6
|
2月前
|
Kubernetes 监控 Shell
在K8S中,我们公司用户反应pod连接数非常多,希望看一下这些连接都是什么信息?什么状态?怎么排查?容器里面没有集成bash环境、网络工具,怎么处理?
在K8S中,我们公司用户反应pod连接数非常多,希望看一下这些连接都是什么信息?什么状态?怎么排查?容器里面没有集成bash环境、网络工具,怎么处理?
|
2月前
|
缓存 NoSQL 网络协议
【Azure Redis 缓存 Azure Cache For Redis】在创建高级层Redis(P1)集成虚拟网络(VNET)后,如何测试VNET中资源如何成功访问及配置白名单的效果
【Azure Redis 缓存 Azure Cache For Redis】在创建高级层Redis(P1)集成虚拟网络(VNET)后,如何测试VNET中资源如何成功访问及配置白名单的效果