本文基于一個(gè)簡(jiǎn)單的Demo流程介紹了Activiti框架啟動(dòng)、部署窟社、運(yùn)行過(guò)程祟滴。
Demo準(zhǔn)備
流程圖文件:
<?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:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
<process id="hello" name="hello" isExecutable="true">
<!-- 流程開(kāi)始節(jié)點(diǎn) -->
<startEvent id="start" name="Start" ></startEvent>
<!-- serviceTask:執(zhí)行me.likeyao.activiti.demo.HelloWorld的execute方法,打印hello world -->
<serviceTask id="helloworld" name="helloworld" activiti:class="me.likeyao.activiti.demo.HelloWorld"/>
<!-- 流程結(jié)束節(jié)點(diǎn) -->
<endEvent id="end" name="End"></endEvent>
<!-- 流程遷移線:開(kāi)始節(jié)點(diǎn)到serviceTask節(jié)點(diǎn) -->
<sequenceFlow id="sid-1" sourceRef="start" targetRef="helloworld"></sequenceFlow>
<!-- 流程遷移線:serviceTask節(jié)點(diǎn)到結(jié)束節(jié)點(diǎn) -->
<sequenceFlow id="sid-3" sourceRef="helloworld" targetRef="end"></sequenceFlow>
</process>
</definitions>
流程圖:
代碼:
public class App {
public static void main(String[] args) {
//創(chuàng)建流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//部署流程圖
processEngine.getRepositoryService().createDeployment().addClasspathResource("hello.bpmn20.xml").deploy();
//發(fā)起流程
processEngine.getRuntimeService().startProcessInstanceByKey("hello");
}
}
public class HelloWorld implements JavaDelegate{
public void execute(DelegateExecution execution) throws Exception {
System.out.println("Hello world!");
}
}
Demo實(shí)現(xiàn)的功能是發(fā)起一個(gè)流程这弧,執(zhí)行到流程的serviceTask節(jié)點(diǎn)時(shí),打印Hello world虚汛!
匾浪,然后流程結(jié)束。
源碼版本:5.22.0
框架初始化
ProcessEngine
ProcessEngine是Activiti框架的門(mén)面卷哩,ProcessEngine本身不提供任何功能户矢,通過(guò)getXXXService方法可以獲取到對(duì)應(yīng)的Service對(duì)象執(zhí)行操作。Demo中涉及到的兩個(gè)Service:
- RepositoryService:流程定義和流程部署相關(guān)功能殉疼。
- RuntimeService:流程實(shí)例相關(guān)功能(發(fā)起流程梯浪、獲取流程實(shí)例變量)。
ProcessEngineConfiguration
ProcessEngineConfiguration負(fù)責(zé)Activiti框架的屬性配置瓢娜、初始化工作挂洛,初始化入口是buildProcessEngine方法,所有Activiti框架運(yùn)行時(shí)需要用到的組件基本都在這里初始化:
public ProcessEngine buildProcessEngine() {
init();
return new ProcessEngineImpl(this);
}
protected void init() {
initConfigurators();
configuratorsBeforeInit();
initProcessDiagramGenerator();
initHistoryLevel();
initExpressionManager();
initDataSource();
initVariableTypes();
initBeans();
initFormEngines();
initFormTypes();
initScriptingEngines();
initClock();
initBusinessCalendarManager();
initCommandContextFactory();
initTransactionContextFactory();
initCommandExecutors();
initServices();
initIdGenerator();
initDeployers();
initJobHandlers();
initJobExecutor();
initAsyncExecutor();
initTransactionFactory();
initSqlSessionFactory();
initSessionFactories();
initJpa();
initDelegateInterceptor();
initEventHandlers();
initFailedJobCommandFactory();
initEventDispatcher();
initProcessValidator();
initDatabaseEventLogging();
configuratorsAfterInit();
}
這里有一個(gè)擴(kuò)展點(diǎn):ProcessEngineConfigurator眠砾。
public interface ProcessEngineConfigurator {
//組件初始化前
void beforeInit(ProcessEngineConfigurationImpl processEngineConfiguration);
//組件初始化后
void configure(ProcessEngineConfigurationImpl processEngineConfiguration);
//優(yōu)先級(jí)
int getPriority();
}
在init初始化方法中虏劲,initConfigurators方法通過(guò)ServiceLoader加載ProcessEngineConfigurator。隨后在configuratorsBeforeInit和configuratorsAfterInit方法中分別調(diào)用ProcessEngineConfigurator的beforeInit和configure方法,使用戶(hù)可以在ProcessEngineConfiguration初始化前后編程式的修改屬性柒巫,替換Activiti默認(rèn)組件励堡。
流程部署
流程部署實(shí)現(xiàn)的功能是將xml格式的流程圖,轉(zhuǎn)化為Activiti框架運(yùn)行時(shí)依賴(lài)的流程定義對(duì)象堡掏。
RepositoryService
Demo中通過(guò)以下代碼部署了一個(gè)流程:
processEngine.getRepositoryService().createDeployment().addClasspathResource("hello.bpmn20.xml").deploy();
createDeployment方法中創(chuàng)建了DeploymentBuilder對(duì)象应结,DeploymentBuilder對(duì)象負(fù)責(zé)讀取指定路徑的流程圖xml文件的內(nèi)容(byte數(shù)組),并緩存在DeploymentEntity對(duì)象中:
public DeploymentBuilder addInputStream(String resourceName, InputStream inputStream) {
...
byte[] bytes = IoUtil.readInputStream(inputStream, resourceName);
ResourceEntity resource = new ResourceEntity();
resource.setName(resourceName);
resource.setBytes(bytes);
deployment.addResource(resource);
return this;
}
最終DeploymentBuilder的deploy方法會(huì)調(diào)用RepositoryService的deploy方法泉唁,完成流程部署:
public Deployment deploy() {
return repositoryService.deploy(this);
}
CommandExecutor
在RepositoryService的deploy方法中鹅龄,使用了CommandExecutor對(duì)象:
public Deployment deploy(DeploymentBuilderImpl deploymentBuilder) {
return commandExecutor.execute(new DeployCmd<Deployment>(deploymentBuilder));
}
在Activiti中,大部分操作都以Command模式實(shí)現(xiàn)亭畜,例如部署流程圖的DeployCmd扮休。CommandExecutor封裝了一系列的CommandInterceptor,在內(nèi)部形成CommandInterceptor鏈拴鸵,在命令執(zhí)行前后做了攔截玷坠。Activiti框架提供了一些
CommandInterceptor實(shí)現(xiàn):
名稱(chēng) | 作用 |
---|---|
CommandContextInterceptor | 用于生成命令執(zhí)行的上下文(CommandContext)。 |
LogInterceptor | 開(kāi)啟日志Debug級(jí)別后劲藐,打印日志八堡。 |
JtaTransactionInterceptor | 開(kāi)啟Jta事務(wù) |
引入activiti-spring包,通過(guò)SpringTransactionInterceptor引入Spring的事務(wù)支持瘩燥。
CommandExecutor在ProcessEngineConfigurationImpl的initCommandExecutors方法中初始化:
protected void initCommandExecutors() {
initDefaultCommandConfig();
initSchemaCommandConfig();
initCommandInvoker();
initCommandInterceptors();
initCommandExecutor();
}
可以設(shè)置ProcessEngineConfigurationImpl的customPreCommandInterceptors和customPostCommandInterceptors屬性,添加自定義的CommandInterceptor:
protected void initCommandInterceptors() {
if (commandInterceptors==null) {
commandInterceptors = new ArrayList<CommandInterceptor>();
if (customPreCommandInterceptors!=null) {
commandInterceptors.addAll(customPreCommandInterceptors);
}
commandInterceptors.addAll(getDefaultCommandInterceptors());
if (customPostCommandInterceptors!=null) {
commandInterceptors.addAll(customPostCommandInterceptors);
}
commandInterceptors.add(commandInvoker);
}
}
這里的pre和post是指Activiti框架getDefaultCommandInterceptors()的前后不同。
CommandInvoker是CommandInterceptor鏈的最后一個(gè)對(duì)象厉膀,負(fù)責(zé)調(diào)用Command:
public class CommandInvoker extends AbstractCommandInterceptor {
@Override
public <T> T execute(CommandConfig config, Command<T> command) {
return command.execute(Context.getCommandContext());
}
}
CommandContext
CommandContext是Activit框架Command執(zhí)行的上下文,主要包含各種SessionFactory:
sessionFactories = processEngineConfiguration.getSessionFactories();
SessionFactory負(fù)責(zé)生成Session二拐,Session是Activiti操作持久化對(duì)象的統(tǒng)一接口:
名稱(chēng) | 作用 |
---|---|
ProcessDefinitionEntityManager | 流程定義相關(guān)讀寫(xiě)操作服鹅。 |
ExecutionEntityManager | 流程實(shí)例相關(guān)讀寫(xiě)操作。 |
DefaultHistoryManager | 歷史記錄相關(guān)讀寫(xiě)操作 |
CommandContext的生命周期
CommandConext在CommandContextInterceptor中創(chuàng)建百新,在finally代碼塊中銷(xiāo)毀:
public <T> T execute(CommandConfig config, Command<T> command) {
//首先嘗試從線程上下文的棧中獲取CommandContext
CommandContext context = Context.getCommandContext();
boolean contextReused = false;
//什么時(shí)候創(chuàng)建新的CommandContext企软?
//1、CommandConfig中指定了不復(fù)用CommandContext
//2饭望、當(dāng)前線程上下文中不存在CommandConext
//3仗哨、當(dāng)前線程上下文中的CommandConext已經(jīng)拋出異常
if (!config.isContextReusePossible() || context == null || context.getException() != null) {
context = commandContextFactory.createCommandContext(command); }
else {
contextReused = true;
}
try {
//將前面獲取到的CommandContext入棧
Context.setCommandContext(context);
Context.setProcessEngineConfiguration(processEngineConfiguration);
//執(zhí)行下一個(gè)interceptor,在CommandInvoker中可以通過(guò)Context.getCommandContext()獲取線程上下文中的CommandContext
return next.execute(config, command);
} catch (Exception e) {
//記錄異常信息
context.exception(e);
} finally {
try {
//如果CommandContext不可復(fù)用铅辞,用完直接關(guān)閉
if (!contextReused) {
context.close();
}
} finally {
//出棧操作
Context.removeCommandContext();
Context.removeProcessEngineConfiguration();
Context.removeBpmnOverrideContext();
}
}
return null;
}
Activiti的框架可以在一個(gè)Command的執(zhí)行過(guò)程中厌漂,調(diào)用另外一個(gè)Command,所以會(huì)出現(xiàn)是否需要復(fù)用CommandContext的選項(xiàng)斟珊,默認(rèn)值為true苇倡。
流程的解析
在DeployCmd中,首先調(diào)用DeploymentEntityManager持久化存儲(chǔ)DeploymentEntity對(duì)象:
commandContext.getDeploymentEntityManager().insertDeployment(deployment);
然后調(diào)用DeploymentManager部署流程(流程解析):
commandContext.getProcessEngineConfiguration().getDeploymentManager().deploy(deployment, deploymentSettings);
DeploymentEntityManager
DeploymentEntityManager的deploy方法中循環(huán)調(diào)用Deployer對(duì)象的deploy方法,Activiti默認(rèn)的Deployer是BpmnDeployer旨椒。
另外DeploymentEntityManager中還緩存了解析好的流程定義對(duì)象和Bpmn模型對(duì)象晓褪。
Activiti持久化的是流程圖xml文件,每次系統(tǒng)重新啟動(dòng)都要執(zhí)行一次“deploy”操作综慎,生成ProcessDefinitionEntity對(duì)象涣仿。
BpmnDeployer
BpmnDeployer的deploy方法中包含幾個(gè)操作(代碼縮略版):
public void deploy(DeploymentEntity deployment, Map<String, Object> deploymentSettings) {
...
BpmnParse bpmnParse = bpmnParser.createParse().sourceInputStream(inputStream).setSourceSystemId(resourceName).deployment(deployment).name(resourceName);
bpmnParse.execute();
for (ProcessDefinitionEntity processDefinition: bpmnParse.getProcessDefinitions()) {
if (deployment.isNew()) {
ProcessDefinitionEntity latestProcessDefinition = ...
if (latestProcessDefinition != null) {
processDefinitionVersion = latestProcessDefinition.getVersion() + 1;
}else{
processDefinitionVersion = 1;
}
processDefinition.setId(idGenerator.getNextId());
dbSqlSession.insert(processDefinition);
}
...
}
}
- 通過(guò)BpmnParser對(duì)象創(chuàng)建BpmnParse。
- 調(diào)用BpmnParse的execute方法寥粹,將inputStream中的流程圖轉(zhuǎn)化為ProcessDefinitionEntity变过。
- 持久化ProcessDefinitionEntity對(duì)象。
BpmnParse
在BpmnParse的execute中完成了xml文件到ProcessDefinitionEntity對(duì)象的轉(zhuǎn)化:
public BpmnParse execute() {
//xml->bpmnModel
bpmnModel = converter.convertToBpmnModel(streamSource, validateSchema, enableSafeBpmnXml, encoding);
//bpmnModel-> ProcessDefinitionEntity
transformProcessDefinitions();
}
protected void transformProcessDefinitions() {
for (Process process : bpmnModel.getProcesses()) {
bpmnParserHandlers.parseElement(this, process);
}
}
在流程定義解析過(guò)程中涝涤,會(huì)涉及到兩套模型:
- Bpmn模型(由BpmnXMLConverter完成轉(zhuǎn)換)
- PVM模型(由BpmnParseHandlers完成轉(zhuǎn)換)
Bpmn模型
PVM模型
Bpmn模型更偏向于xml節(jié)點(diǎn)的描述媚狰,PVM模型是運(yùn)行時(shí)模型。Bpmn模型中的ServiceTask阔拳、StartEvent等會(huì)統(tǒng)一映射轉(zhuǎn)換為PVM的ActivityImpl對(duì)象崭孤,ServiceTask和StartEvent等節(jié)點(diǎn)行為上的差別,體現(xiàn)在ActivityImpl對(duì)象持有的不同的ActivityBehavior上糊肠。
運(yùn)行流程
創(chuàng)建流程實(shí)例
在demo中通過(guò)RuntimeService發(fā)起流程實(shí)例:
processEngine.getRuntimeService().startProcessInstanceByKey("hello");
在startProcessInstanceByKey方法中執(zhí)行StartProcessInstanceCmd命令:
public class StartProcessInstanceCmd<T> implements Command<ProcessInstance>, Serializable {
...
public ProcessInstance execute(CommandContext commandContext) {
//獲取流程定義
ProcessDefinitionEntity processDefinition = ...
//創(chuàng)建流程實(shí)例
ExecutionEntity processInstance = processDefinition.createProcessInstance(businessKey);
//開(kāi)始流程
processInstance.start();
return processInstance;
}
...
}
在StartProcessInstanceCmd方中通過(guò)流程定義ProcessDefinitionEntity創(chuàng)建了流程實(shí)例ExecutionEntity:
ExecutionEntity實(shí)現(xiàn)了一些重要接口:
- PVM相關(guān)的接口辨宠,賦予了ExecutionEntity流程驅(qū)動(dòng)的能力,例如single货裹、start方法嗤形。
- 實(shí)現(xiàn)VariableScope接口讓ExecutionEntity可以持久上下文變量。
- ProcessInstance接口暴露了ExecutionEntity關(guān)聯(lián)的ProcessDefinitionEntity的信息弧圆。
- PersistentObject接口代表ExecutionEntity對(duì)象是需要持久化赋兵。
在ExecutionEntity中維護(hù)類(lèi)一個(gè)屬性:activity。activity屬性代表當(dāng)前執(zhí)行到哪個(gè)節(jié)點(diǎn)搔预,在創(chuàng)建ExecutionEntity過(guò)程中會(huì)設(shè)置activity霹期,使流程從某一個(gè)節(jié)點(diǎn)開(kāi)始,默認(rèn)是開(kāi)始節(jié)點(diǎn)拯田。
最后StartProcessInstanceCmd還調(diào)用ExecutionEntity的start方法開(kāi)始驅(qū)動(dòng)流程:
public void start() {
performOperation(AtomicOperation.PROCESS_START);
}
驅(qū)動(dòng)流程
Activiti框架的流程運(yùn)行于PVM模型之上历造,在流程運(yùn)行時(shí)主要涉及到PVM中幾個(gè)對(duì)象:ActivityImpl、TransitionImpl和ActivityBehavior船庇。
- ActivityImpl:ActivityImpl是流程節(jié)點(diǎn)的抽象吭产,ActivityImpl維護(hù)流程圖中節(jié)點(diǎn)的連線,包括有哪些進(jìn)線鸭轮,有哪些出線垮刹。另外還包含節(jié)點(diǎn)同步/異步執(zhí)行等信息。
- TransitionImpl:TransitionImpl包含source和target兩個(gè)屬性张弛,連接了兩個(gè)流程節(jié)點(diǎn)荒典。
- ActivityBehavior:每一個(gè)ActivityImpl對(duì)象都擁有一個(gè)ActivityBehavior對(duì)象酪劫,ActivityBehavior代表節(jié)點(diǎn)的行為。
ActivityImpl寺董、TransitionImpl和ActivityBehavior只是描述了流程的節(jié)點(diǎn)覆糟、遷移線和節(jié)點(diǎn)行為,真正要讓ExecutionEntity流轉(zhuǎn)起來(lái)遮咖,還需要AtomicOperation的驅(qū)動(dòng):
AtomicOperation PROCESS_START = new AtomicOperationProcessStart();
AtomicOperation PROCESS_START_INITIAL = new AtomicOperationProcessStartInitial();
AtomicOperation PROCESS_END = new AtomicOperationProcessEnd();
AtomicOperation ACTIVITY_START = new AtomicOperationActivityStart();
AtomicOperation ACTIVITY_EXECUTE = new AtomicOperationActivityExecute();
AtomicOperation ACTIVITY_END = new AtomicOperationActivityEnd();
AtomicOperation TRANSITION_NOTIFY_LISTENER_END = new AtomicOperationTransitionNotifyListenerEnd();
AtomicOperation TRANSITION_DESTROY_SCOPE = new AtomicOperationTransitionDestroyScope();
AtomicOperation TRANSITION_NOTIFY_LISTENER_TAKE = new AtomicOperationTransitionNotifyListenerTake();
AtomicOperation TRANSITION_CREATE_SCOPE = new AtomicOperationTransitionCreateScope();
AtomicOperation TRANSITION_NOTIFY_LISTENER_START = new AtomicOperationTransitionNotifyListenerStart();
AtomicOperation DELETE_CASCADE = new AtomicOperationDeleteCascade();
AtomicOperation DELETE_CASCADE_FIRE_ACTIVITY_END = new AtomicOperationDeleteCascadeFireActivityEnd();
在ExecutionEntity的start方法中滩字,調(diào)用了PROCESS_START,PROCESS_START做了幾件事:
- 獲取流程定義級(jí)別定義的監(jiān)聽(tīng)start事件的ExecutionListener御吞,調(diào)用notify方法麦箍。
- 如果開(kāi)啟了事件功能,發(fā)布ActivitiEntityWithVariablesEvent和ActivitiProcessStartedEvent陶珠。
- 調(diào)用PROCESS_START_INITIAL挟裂。
PROCESS_START_INITIAL也實(shí)現(xiàn)了類(lèi)似的功能:
- 獲取初始節(jié)點(diǎn)上定義的監(jiān)聽(tīng)start事件的ExecutionListener,調(diào)用notify方法揍诽。
- 調(diào)用ACTIVITY_EXECUTE诀蓉。
在Demo流程執(zhí)行中涉及的AtomicOperation的鏈路主要包括:
- ACTIVITY_EXECUTE:調(diào)用當(dāng)前activity的behavior。
- TRANSITION_NOTIFY_LISTENER_END:某個(gè)activity節(jié)點(diǎn)執(zhí)行完畢暑脆,調(diào)用節(jié)點(diǎn)上聲明的監(jiān)聽(tīng)end事件的ExecutionListener渠啤。
- TRANSITION_NOTIFY_LISTENER_TAKE:觸發(fā)線上的ExecutionListener。
- TRANSITION_NOTIFY_LISTENER_START:某個(gè)activity節(jié)點(diǎn)即將開(kāi)始執(zhí)行添吗,調(diào)用節(jié)點(diǎn)上的監(jiān)聽(tīng)start事件的ExecutionListener沥曹。
以Demo流程中的ServiceTask節(jié)點(diǎn)helloworld為例,在執(zhí)行ACTIVITY_EXECUTE時(shí)碟联,會(huì)獲取activity關(guān)聯(lián)的behavior:
public class AtomicOperationActivityExecute implements AtomicOperation {
public void execute(InterpretableExecution execution) {
...
ActivityImpl activity = (ActivityImpl) execution.getActivity();
ActivityBehavior activityBehavior = activity.getActivityBehavior();
activityBehavior.execute(execution);
...
}
}
ServiceTask解析時(shí)關(guān)聯(lián)的是ServiceTaskJavaDelegateActivityBehavior妓美,execution方法:
public void execute(ActivityExecution execution) throws Exception {
//execution中調(diào)用了me.likeyao.activiti.demo.HelloWorld
execute((DelegateExecution) execution);
//離開(kāi)當(dāng)前節(jié)點(diǎn)
leave(execution);
}
在leave方法中調(diào)用了:
bpmnActivityBehavior.performDefaultOutgoingBehavior(execution);
performDefaultOutgoingBehavior方法會(huì)在當(dāng)前activity的
出線中選擇一條,使流程流向下一個(gè)節(jié)點(diǎn)玄帕。在Demo中只有一條線存在:
protected void performOutgoingBehavior(ActivityExecution execution,
boolean checkConditions, boolean throwExceptionIfExecutionStuck, List<ActivityExecution> reusableExecutions) {
if (transitionsToTake.size() == 1) {
execution.take(transitionsToTake.get(0));
}
}
最終take方法會(huì)將流程驅(qū)動(dòng)權(quán)交還到AtomicOperation中:
public class ExecutionEntity{
...
public void take(PvmTransition transition, boolean fireActivityCompletionEvent) {
...
setActivity((ActivityImpl)transition.getSource());
setTransition((TransitionImpl) transition);
performOperation(AtomicOperation.TRANSITION_NOTIFY_LISTENER_END);
...
}
...
}
AtomicOperation的問(wèn)題
按照AtomicOperation的驅(qū)動(dòng)模式部脚,只有當(dāng)遇到UserTask等需要等待single信號(hào)的節(jié)點(diǎn)想邦,調(diào)用才會(huì)返回裤纹。這意味著當(dāng)調(diào)用RuntimeService啟動(dòng)一個(gè)流程實(shí)例時(shí),要一直等到流程運(yùn)行到一個(gè)UserTask節(jié)點(diǎn)調(diào)用才會(huì)返回丧没,如果流程比較長(zhǎng)耗時(shí)非常驗(yàn)證鹰椒。
另一個(gè)問(wèn)題是當(dāng)流程圖比較復(fù)雜,ExecutionListener數(shù)量比較多時(shí)呕童,AtomicOperation之間的互相調(diào)用會(huì)導(dǎo)致調(diào)用棧非常深漆际。
AtomicOperation驅(qū)動(dòng)模式與ExecutionEntity、Behavior等綁定的比較緊密夺饲,暫時(shí)沒(méi)有特別好的辦法替換掉奸汇。
小結(jié)
本文主要介紹了Activiti框架的啟動(dòng)施符、部署、運(yùn)行的主鏈路擂找,并沒(méi)有深入BPMN規(guī)范和Activit功能的具體實(shí)現(xiàn)戳吝,后續(xù)打算根據(jù)Activiti的用戶(hù)手冊(cè),詳細(xì)分析每個(gè)功能的使用和實(shí)現(xiàn)贯涎。