Java⼯作流引擎Activiti
1.简单介⼯作流引擎与Activiti
对于⼯作流引擎的解释请参考百度百科:
1.1 我与⼯作流引擎
在第⼀家公司⼯作的时候主要任务就是开发OA系统,当然基本都是有⼯作流的⽀持,不过当时使⽤的⼯作流引擎是公司⼀些⽜⼈开发的(据说是⽤⼀个开源 的引擎修改的),名称叫CoreFlow;功能相对Activiti来说⽐较弱,但是能满⾜⽇常的使⽤,当然也有不少的问题所以后来我们只能修改引擎的代 码打补丁。
现在是我⼯作的第⼆家公司,因为要开发ERP、OA等系统需要使⽤⼯作流,在项⽬调研阶段我先搜索资料选择使⽤哪个开源⼯作流引擎,最终确定了Activiti5并基于公司的架构做了⼀些DEMO。
1.2 Activiti与JBPM5?
对于Activiti、jBPM4、jBPM5我们应该如何选择,在InfoQ上有⼀篇⽂章写的很好,从⼤的层⾯⽐较各个引擎之间的差异,请参考⽂章:1.3 Activiti资料
版本:Activiti的版本是从5开始的,因为Activiti是使⽤jBPM4的源码;版本发布:两个⽉发布⼀次。
Activit中⽂:5435716
2.初次使⽤遇到问题收集
因为Activiti刚刚退出不久所以资料⽐较空缺,中⽂资料更是少的可怜,所以开始的时候⼀头雾⽔(虽然之前⽤过⼯作流,但是感觉差距很多),⽽且官⽅的⼿册还不是很全⾯;所以我把我在学习使⽤的过程遇到的⼀些疑问都罗列出来分享给⼤家;以下⼏点是我遇到和想到的,如果你还有什么疑问可以在评论中和我交流再补充。
2.1 部署流程图后中⽂乱码
乱码是⼀直缠绕着国⼈的问题,之前各个技术、⼯具出现乱码的问题写过很多⽂章,这⾥也不例外……,Activiti的乱码问题在流程图中。流程图的乱码如下图所⽰:
解决办法有两种:
2.1.1 修改源代码⽅式
修改源码
ine.impl.bpmn.diagram.ProcessDiagramCanvas
在构造⽅法
public ProcessDiagramCanvas(int width, int height)
中有⼀⾏代码是设置字体的,默认是⽤Arial字体,这就是乱码产⽣的原因,把字改为本地的中⽂字体即可,例如:
Font font = new Font("WenQuanYi Micro Hei", Font.BOLD, 11);
当然如果你有配置⽂件读取⼯具那么可以设置在*.properties⽂件中,我就是这么做的:
Font font = new ("activiti.diagram.canvas.font"), Font.BOLD, 11);
2.1.2 使⽤压缩包⽅式部署
Activiti⽀持部署*.l、bar、zip格式的流程定义。
使⽤Activit Deisigner⼯具设计流程图的时候会有三个类型的⽂件:
.activiti设计⼯具使⽤的⽂件
.l设计⼯具⾃动根据.activiti⽂件⽣成的xml⽂件
.png流程图图⽚
解决办法就是把xml⽂件和图⽚⽂件同时部署,因为在单独部署xml⽂件的时候Activiti会⾃动⽣成⼀张流程图的图⽚⽂件,但是这样在使⽤的时候坐标和图⽚对应不起来……
java技术介绍百度百科所以把xml和图⽚同时部署的时候Activiti⾃动关联xml和图⽚,当需要获取图⽚的时候直接返回部署时压缩包⾥⾯的图⽚⽂件,⽽不是Activiti⾃动⽣成的图⽚⽂件
2.1.2.1 使⽤⼯具打包Bar⽂件
右键项⽬名称然后点击“Create deployment artifacts”,会在src⽬录中创建deployment⽂件夹,⾥⾯包含*.bar⽂件.
2.1.2.2 使⽤Ant脚本打包Zip⽂件
这也是我们采⽤的办法,你可以⼿动选择xml和png打包成zip格式的⽂件,也可以像我们⼀样采⽤ant target的⽅式打包这两个⽂件。
<?xml version="1.0" encoding="UTF-8"?>
<project name="foo">
<property name="workflow.definition" value="foo-common-core/src/main/resources/diagrams" />
<property name="workflow.deployments" value="foo-common-core/src/main/resources/deployments" />
<target name="workflow.package.oa.leave">
<echo>打包流程定义及流程图::OA-请假</echo>
<zip destfile="${workflow.deployments}/oa/leave.zip" basedir="${workflow.definition}/oa/leave" update="true"
includes="*.xml,*.png" />
</target>
</project>
brought to you by  .
这样当修改流程定义⽂件后只要运⾏ant命令就可以打包了:
ant workflow.package.oa.leave
现在部署bar或者zip⽂件查看流程图图⽚就不是乱码了,⽽是你的压缩包⾥⾯的png⽂件。
2.2 使⽤引擎提供的Form还是⾃定义业务Form
2.2.1 引擎提供的Form
定义表单的⽅式在每个Task标签中定义extensionElements和activiti:formProperty即可,到达这个节点的时候可以通过API读取表单元素。
Activiti官⽅的例⼦使⽤的就是在流程定义中设置每⼀个节点显⽰什么样的表单哪些字段需要显⽰、哪些字段只读、哪些字段必填。
但是这种⽅式仅仅适⽤于⽐较简单的流程,对于稍微复杂或者页⾯需要业务逻辑的判断的情况就不适⽤了。
对于数据的保存都是在引擎的表中,不利于和其他表的关联、对整个系统的规划也不利!
2.2.2 ⾃定义业务Form
这种⽅式应该是⼤家⽤的最多的了,因为⼀般的业务系统业务逻辑都会⽐较复杂,⽽且数据库中很多表都会有依赖关系,表单中有很多状态判断。
例如我们的系统适⽤jQuery UI作为UI,有很多javascript代码,页⾯的很多操作需要特殊处理(例如:多个选项的互斥、每个节点根据类型和操作⼈显⽰不同的按钮);基本每 个公司都有⼀套⾃⼰的UI风格,要保持多个系统的操作习惯⼀致只能使⽤⾃定义表单才能满⾜。
2.3 业务和流程的关联⽅式
这个问题在⾥⾯很多⼈都问过,这也是我刚刚开始迷惑的地⽅;
后来看了以下API发现RuntimeService有两个⽅法:
2.3.1 startProcessInstanceByKey
javadoc对其说明:
startProcessInstanceByKey(String processDefinitionKey, Map<string,object> variables)
Starts a new process instance in the latest version of the process definition with the given key
其中businessKey就是业务ID,例如要申请请假,那么先填写登记信息,然后(保存+启动流程),因为请假是单独设计的数据表,所以保存后得到实体ID就可以把它传给processInstanceBusinessKey⽅法启动流程。当需要根据businessKey查询流程的时候就可以通过API查询:
建议数据库冗余设计:在业务表设计的时候添加⼀列:PROCESS_INSTANCE_ID varchar2(64),在流程启动之后把流程ID更新到业务表中,这样不管从业务还是流程都可以查询到对⽅!
特别说明: 此⽅法启动时⾃动选择最新版本的流程定义。
2.3.2 startProcessInstanceById
javadoc对其说明:
startProcessInstanceById(String processDefinitionId, String businessKey, Map<string,object> variables)
Starts a new process instance in the exactly specified version of the process definition with the given id.
processDefinitionId:这个参数的值可以通过ateProcessDefinitionQuery()⽅法查询,对应数据库:
ACT_RE_PROCDEF;每次部署⼀次流程定义就会添加⼀条数据,同名的版本号累加。
特别说明: 此可以指定不同版本的流程定义,让⽤户多⼀层选择。
2.3.3 如何选择
建议使⽤startProcessInstanceByKey,特殊情况需要使⽤以往的版本选择使⽤startProcessInstanceById。
2.4 同步⽤户数据
这个问题也是⽐较多的⼈询问过,Activiti⽀持对任务分配到:指定⼈、指定组、两者组合,⽽这些⼈和组的信息都保存在ACT_ID..表中,有⾃⼰的⽤户和组(⾓⾊)管理让很多⼈不知所措了;原因是因为每个系统都会存在⼀个权限管理模块(维护:⽤户、部门、⾓⾊、授权),不知道该怎么和Activiti同步。
2.4.1 建议处理⽅式
Activiti有⼀个IdentityService接⼝,通过这个接⼝可以操控Activiti的ACT_ID_*表的数据,⼀般的做法是⽤业务系统的权限管理模块维护⽤户数据,当进⾏CRUD操作的时候在原有业务逻辑后⾯添加同步到Activiti的代码;例如添加⼀个⽤户时同步Activiti User的代码⽚段:
/**
* 保存⽤户信息 并且同步⽤户信息到activiti的identity.User,同时设置⾓⾊
* @param user
* @param roleIds
*/
public void saveUser(User user, List<Long> roleIds, boolean synToActiviti) {
accountManager.saveEntity(user);
String userId = Id().toString();
if (synToActiviti) {
List<ine.identity.User> activitiUsers = ateUserQuery().userId(userId).list();
if (activitiUsers.size() == 1) {
//更新信息
ine.identity.User activitiUser = (0);
activitiUser.Name());
activitiUser.setLastName("");
activitiUser.Password());
activitiUser.Email());
identityService.saveUser(activitiUser);
// 删除⽤户的membership
List<Group> activitiGroups = ateGroupQuery().groupMember(userId).list();
for (Group group : activitiGroups) {
identityService.deleteMembership(userId, Id());
}
// 添加membership
for (Long roleId : roleIds) {
Role role = Entity(roleId);
}
} else {
ine.identity.User newUser = wUser(userId);
newUser.Name());
newUser.setLastName("");
newUser.Password());
newUser.Email());
identityService.saveUser(newUser);
// 添加membership
for (Long roleId : roleIds) {
Role role = Entity(roleId);
}
}
}
}
brought to you by  .
删除操作也和这个类似!
不管从业务系统维护⽤户还是从Activiti维护,肯定要确定⼀⽅,然后CRUD的时候同步到对⽅,如果需要同步多个⼦系统那么可以再调⽤WebService实现。
2.5 流程图设计⼯具⽤什么
Activiti提供了两个流程设计⼯具,但是⾯向对象不同。
Activiti Modeler,⾯向业务⼈员,使⽤开源的BPMN设计⼯具,使⽤BPMN描述业务流程图
Eclipse Designer,⾯向开发⼈员,Eclipse的插件,可以让开发⼈员定制每个节点的属性(ID、Name、Listener、Attr等)
2.5.1 我们的⽅式
可能你会惊讶,因为我们没有使⽤Activiti Modeler,我们认为⽤Viso已经能表达流程图的意思了,⽽且项⽬经理也是技术出⾝,和开发⼈员也容易沟通。
⽬前这个项⽬是第⼀个使⽤Activiti的,开始我们在需求调研阶段使⽤Viso设计流程图,利⽤设计和客户沟通,确定后由负责流程的开发⼈员⽤Eclipse Designer设计得到l,最后部署。
2.6 Eclipse Designer存在的问题
这个插件有⼀个很讨厌的Bug⼀直未修复,安装了插件后Eclipse的复制和粘帖快捷键会被更换为(Ctrl+Insert、Shift+Insert);Bug描述请见:
所以最后我们只能单独开⼀个安装了Eclipse Designer的Eclipse专门⽤来设计流程图,这样就不影响正常使⽤Eclipse JAVAEE了。3.配置
3.1 集成Spring
对于和Spring的集成Activiti做的不错,简单配置⼀些Bean代理即可实现,但是有两个和事务相关的地⽅要提⽰:配置processEngineConfiguration的时候属性transactionManager要使⽤和业务功能的同⼀个事务管理Bean,否则事务不同步。
对于实现了ine.delegate包中的接⼝的类需要被事务控制的实现类需要被Spring代理,并且添加事务的Annotation或者在xml中配置,例如:
1 2 3 4 5 6 7 8 9 10/**
* 创建缴费流程的时候⾃动创建实体
*
* @author HenryYan
*/
@Service
@Transactional
public class CreatePaymentProcessListener implements ExecutionListener { ....
}
4.使⽤单元测试
单元测试均使⽤Spring的AbstractTransactionalJUnit4SpringContextTests作为SuperClass,并且在测试类添加:
1 2@ContextConfiguration(locations = { "/l"}) @RunWith(SpringJUnit4ClassRunner.class)
虽然Activiti也提供了测试的⼀些超类,但是感觉不好⽤,所以⾃⼰封装了⼀些⽅法。
4.1 验证流程图设计是否正确
4.2 业务对象和流程关联测试
5.各种状态的任务查询以及和业务对象关联

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。