返回介绍

16.2. 使用 CDI 的基于上下文的流程执行

发布于 2023-09-17 23:40:35 字数 6591 浏览 0 评论 0 收藏 0

本章节将介绍Flowable CDI扩展使用的基于上下文的流程执行模型(contextual process execution model)。BPMN业务流程通常是一个长期运行的交互动作,包含用户与系统的任务。在运行时,流程分割为独立工作单元的集合,由用户与/或应用逻辑操作。在flowable-cdi中,流程实例可以关联至一个CDI作用域,代表了一个工作单元。在工作单元很复杂的时候特别有用,比如用户任务由多个不同表单的复杂顺序组成,并需要在交互过程中保持"非流程作用域(non-process-scoped)"状态的场景。

16.2.1. 将一个会话关联至一个流程实例

解析@BusinessProcessScoped bean或注入流程变量,都依赖活动的CDI作用域与流程实例的关联。flowable-cdi提供了org.flowable.cdi.BusinessProcess bean用于控制该关联,并提供:

  • startProcessBy(…​)方法,镜像了Flowable RuntimeService服务暴露的对应方法,用于启动并关联一个业务流程,

  • resumeProcessById(String processInstanceId),用于将给定id关联至流程实例,

  • resumeTaskById(String taskId),用于将给定id关联至任务(以及扩展至相关的流程实例)。

当完成了一个工作单元(例如一个用户任务)时,可以调用completeTask()方法,解除流程实例与会话/请求的关联。这将通知Flowable完成当前任务,并继续运行流程实例。

请注意BusinessProcess bean是一个@Named bean,意味着可以使用表达式(比如在JSF页面中)调用它。下面的JSF2代码片段启动了一个新的会话,并将其关联至一个用户任务实例,其id作为请求参数传递(例如pageName.jsf?taskId=XX):

<f:metadata>
<f:viewParam name="taskId" />
<f:event type="preRenderView" listener="#{businessProcess.startTask(taskId, true)}" />
</f:metadata>

16.2.2. 声明式控制流程

Flowable可以使用注解,声明式地启动流程实例以及完成任务。@org.flowable.cdi.annotation.StartProcess注解可以通过"key"或"name"启动一个流程实例。请注意流程实例在注解的方法返回之后启动。例如:

@StartProcess("authorizeBusinessTripRequest")
public String submitRequest(BusinessTripRequest request) {
	// 进行一些操作
	return "success";
}

按照Flowable的配置,被注解的方法代码以及流程实例的启动将处于同一个事务中。@org.flowable.cdi.annotation.CompleteTask的使用方式相同:

@CompleteTask(endConversation=false)
public String authorizeBusinessTrip() {
	// 进行一些操作
	return "success";
}

@CompleteTask注解可以结束当前会话。默认行为是在调用Flowable返回后结束回话。但可以像上面的例子一样,禁用结束会话。

16.2.3. 从流程中引用Bean

flowable-cdi使用自定义解析器,将CDI bean暴露给Flowable El。因此可以像这样在流程中引用bean:

<userTask name="Authorize Business Trip"
			flowable: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();
}

可以使用flowable:expression="myEjb.method()"扩展,在服务任务中调用一个EJB中的业务方法。请注意这需要在MyEjb类上使用@Named注解。

16.2.4. 使用@BusinessProcessScoped bean

可以使用flowable-cdi将一个bean的生命周期绑定在一个流程实例上。为此提供名为BusinessProcessContext的自定义的上下文实现。BusinessProcessScoped bean实例将作为流程变量存储在当前流程实例中。BusinessProcessScoped bean需要是可持久化(PassivationCapable,例如Serializable)的。下面是一个流程作用域bean的例子:

@Named
@BusinessProcessScoped
public class BusinessTripRequest implements Serializable {
	private static final long serialVersionUID = 1L;
	private String startDate;
	private String endDate;
	// ...
}

有时也需要在没有关联至流程实例的情况下(例如在流程启动前)使用流程作用域bean。如果当前没有激活的流程实例,则BusinessProcessScoped bean的实例将临时存储在本地作用域(也就是会话或请求中,取决于上下文)。如果该作用域之后关联至一个业务流程实例,则会将bean实例刷入该流程实例。

16.2.5. 注入流程变量

flowable-cdi支持以下方式注入流程变量

  • 使用@Inject [additional qualifiers] Type fieldName类型安全地注入@BusinessProcessScoped bean

  • 使用@ProcessVariable(name?)限定名不安全地注入其它流程变量:

@Inject @ProcessVariable Object accountNumber;
@Inject @ProcessVariable("accountNumber") Object account

要在EL中引用流程变量,有类似的选择:

  • 可以直接引用@Named @BusinessProcessScoped bean,

  • 可以通过ProcessVariables bean引用其它流程变量:

#{processVariables['accountNumber']}

16.2.6. 接收流程事件

Flowable可以关联至CDI事件总线。这样就可以使用标准CDI事件机制获取流程事件。要为Flowable启用CDI事件支持,需要在配置中启用相应的处理监听器:

<property name="postBpmnParseHandlers">
	<list>
		<bean class="org.flowable.cdi.impl.event.CdiEventSupportBpmnParseHandler" />
	</list>
</property>

这样Flowable就被配置为使用CDI事件总线发布事件。下面介绍如何在CDI bean中接收流程事件。事件通知是类型安全的。流程事件的类型是org.flowable.cdi.BusinessProcessEvent。 下面是一个简单的事件观察者方法的例子:

public void onProcessEvent(@Observes BusinessProcessEvent businessProcessEvent) {
	// 处理事件
}

所有事件都会通知观察者。如果需要限制观察者接收的事件,可以添加限定注解:

  • @BusinessProcess: 限制事件为特定的流程定义。例如:@Observes @BusinessProcess("billingProcess") BusinessProcessEvent evt

  • @StartActivity: 使用特定的活动限制事件。例如:@Observes @StartActivity("shipGoods") BusinessProcessEvent evt将在进入id为"shipGoods"的活动时调用。

  • @EndActivity: 使用特定的活动限制事件。例如:@Observes @EndActivity("shipGoods") BusinessProcessEvent evt将在离开id为"shipGoods"的活动时调用。

  • @TakeTransition: 使用特定的路径限制事件。

  • @CreateTask: 使用特定任务的创建限制事件。

  • @DeleteTask: 使用特定任务的删除限制事件。

  • @AssignTask: 使用特定任务的指派限制事件。

  • @CompleteTask: 使用特定任务的完成限制事件。

上面的限定名可以自由组合。例如,要接收离开"shipmentProcess"中的"shipGoods"活动时生成的所有事件,可以撰写下面的观察者方法:

public void beforeShippingGoods(@Observes @BusinessProcess("shippingProcess") @EndActivity("shipGoods") BusinessProcessEvent evt) {
	// 处理事件
}

在默认配置下,事件监听器将在上下文相同的事务中同步调用。CDI事务性观察者(CDI transactional observer,只能与JavaEE/EJB一起使用)可以在将事件交给观察者方法时进行控制。使用事务性观察者,可以保证比如只在触发事件的事务成功时才通知观察者:

public void onShipmentSuceeded(@Observes(during=TransactionPhase.AFTER_SUCCESS) @BusinessProcess("shippingProcess") @EndActivity("shipGoods") BusinessProcessEvent evt) {
	// 给客户发送邮件。
}

16.2.7. 其他功能

  • 可以注入流程引擎与服务:@Inject ProcessEngine, RepositoryService, TaskService, …​

  • 可以注入当前的流程实例与任务:@Inject ProcessInstance, Task,

  • 可以注入当前的businessKey:@Inject @BusinessKey String businessKey,

  • 可以注入当前的流程实例id:@Inject @ProcessInstanceId String pid+

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文