五、与Spring集成

实际项目中一般都有Spring的身影,与Spring集成使得Activiti的实用性得到提高。activiti和Spring整合需要activiti-spring的jar在类路径下面,整合类是:org.activiti.spring.ProcessEngineFactoryBean,它将加载配置对象和产生流程引擎对象。例如下面代码:

<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
...
</bean> <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>

5.1 事务的配置

在xml配置SpringProcessEngineConfiguration的dataSource的时候,实际上工作流使用的是org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy,它是dataSource的一个代理类,它的作用就是确定从dataSource中获取的数据库连接已经绑定了Spring的事务,所以你无需在进行配置dataSource的代理类,以防失去TransactionAwareDataSourceProxy的效果。

确保在配置中没有配置TransactionAwareDataSourceProxy,因为它在Spring的事务中不会有作用(比如DataSourceTransactionManager和JPATransactionManager不需要代理的dataSource)。

 <beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
<property name="driverClass" value="org.h2.Driver" />
<property name="url" value="jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000" />
<property name="username" value="sa" />
<property name="password" value="" />
</bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean> <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
<property name="dataSource" ref="dataSource" />
<property name="transactionManager" ref="transactionManager" />
<property name="databaseSchemaUpdate" value="true" />
<property name="jobExecutorActivate" value="false" />
</bean> <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean> <bean id="repositoryService" factory-bean="processEngine" factory-method="getRepositoryService" />
<bean id="runtimeService" factory-bean="processEngine" factory-method="getRuntimeService" />
<bean id="taskService" factory-bean="processEngine" factory-method="getTaskService" />
<bean id="historyService" factory-bean="processEngine" factory-method="getHistoryService" />
<bean id="managementService" factory-bean="processEngine" factory-method="getManagementService" /> ...

在其他的Spring配置文件中配置其他的,这样功能清晰,后期容易维护,例如:

 <beans>
...
<tx:annotation-driven transaction-manager="transactionManager"/> <bean id="userBean" class="org.activiti.spring.test.UserBean">
<property name="runtimeService" ref="runtimeService" />
</bean> <bean id="printer" class="org.activiti.spring.test.Printer" /> </beans>

在Spring中启动容器(application context)的方法有很多,比如下面利用类路径加载配置文件:

 ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(
"org/activiti/examples/spring/SpringTransactionIntegrationTest-context.xml");

而在单元测试中,我们可以这样:

 @ContextConfiguration("classpath:org/activiti/spring/test/transaction/SpringTransactionIntegrationTest-context.xml")

在Spring的IOC容器中我们可以轻易的获取我们需要的工作流的Service,ProcessEngineFactoryBean实现了对这些Service的Propagation.REQUIRED的事务级别的控制,例如下面这段代码:

 RepositoryService repositoryService =
(RepositoryService) applicationContext.getBean("repositoryService");
String deploymentId = repositoryService
.createDeployment()
.addClasspathResource("org/activiti/spring/test/hello.bpmn20.xml")
.deploy()
.getId();

在其他方面也有同样的效果,在下面这种情况中,Spring的事务是环绕在serBean.hello()中,这时Activiti的Service会加入事务当中。

UserBean userBean = (UserBean) applicationContext.getBean("userBean");
userBean.hello();

而在具体的UserBean中,我们通过前面的配置是注入了RuntimeService:

 public class UserBean {

   /** Spring注入 */
private RuntimeService runtimeService; @Transactional
public void hello() {
// here you can do transactional stuff in your domain model
// and it will be combined in the same transaction as
// the startProcessInstanceByKey to the Activiti RuntimeService
runtimeService.startProcessInstanceByKey("helloProcess");
} public void setRuntimeService(RuntimeService runtimeService) {
this.runtimeService = runtimeService;
}
}

5.2 Spring中使用表达式

在Spring的配置中也有像activiti的表达式使用,有可能表达式中的bean有限制,在使用Map配置发现没有bean可用,在SpringProcessEngineConfiguration的beans属性配置Map时,如果里面的bean不存在也会通过,只不过bean为空而已。下面这段代码配置beans属性:

<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
...
<property name="beans">
<map>
<entry key="printer" value-ref="printer" />
</map>
</property>
</bean> <bean id="printer" class="org.activiti.examples.spring.Printer" />

现在我们可以在xxxbpmn.xml文件中可以直接使用printer了:

<definitions id="definitions">

  <process id="helloProcess">

    <startEvent id="start" />
<sequenceFlow id="flow1" sourceRef="start" targetRef="print" /> <serviceTask id="print" activiti:expression="#{printer.printMessage()}" />
<sequenceFlow id="flow2" sourceRef="print" targetRef="end" /> <endEvent id="end" /> </process> </definitions>

而我们的Printer类代码:

public class Printer {

  public void printMessage() {
System.out.println("hello world");
}
}

在Spring中配置:

<beans>
... <bean id="printer" class="org.activiti.examples.spring.Printer" /> </beans>

5.3 使用Spring实现自动发布

 自动发布流程定义是整合Spring后的功能,在流程引擎被创建的时候就会将spring配置的资源加载发布。为了防止重复发布,整合的jar中添加了过滤功能,只有加载的资源真正的被改变,才会重新发布。好处在于一些场景(junit 测试)下,Spring容器经常重启。

例如下面配置,其中deploymentResources属性设置资源路径。

<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
...
<property name="deploymentResources"
value="classpath*:/org/activiti/spring/test/autodeployment/autodeploy.*.bpmn20.xml" />
</bean> <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>

在上面的配置当中,配置路径的所有资源将会进行匹配,然后将其资源发布到流程引擎中去。这种过滤手段一定程度上避免了一个资源文件的修改引发所有的流程定义资源文件再次发布的可能性。在一些场景并不会这样,例如你发布的资源中仅仅其中一个是修改的,但是发布是将所有过滤后的都发布出去。activiti提供了自定义发布的功能,在bean节点:SpringProcessEngineConfiguration添加属性deploymentMode,目前它有三种取值策略:

  • default:没有定义deploymentMode时,系统默认设置为default.
  • single-resource:每个资源文件单独发布,如果资源文件改变,单独发布。
  • resource-parent-folder:每个资源文件夹单独发布,也就是说在不同文件夹下的资源文件将会以不同的文件夹为单位,进行防重复发布功能的发布。它介于default(批量发布)和single-resource(单个发布)之间。

以上就是所有的deploymentMode取值,如果你觉得不能够满足你的需求,你可以继承SpringProcessEngineConfiguration,重写其中的getAutoDeploymentStrategy(String deploymentMode)方法实现自己的功能。

5.4 在Spring环境的单元测试

前面介绍过activiti自己提供的测试方法,而在Spring中进行单元测试的话,可以这样:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:org/activiti/spring/test/junit4/springTypicalUsageTest-context.xml")
public class MyBusinessProcessTest { @Autowired
private RuntimeService runtimeService; @Autowired
private TaskService taskService; @Autowired
@Rule
public ActivitiRule activitiSpringRule; @Test
@Deployment
public void simpleProcessTest() {
runtimeService.startProcessInstanceByKey("simpleProcess");
Task task = taskService.createTaskQuery().singleResult();
assertEquals("My Task", task.getName()); taskService.complete(task.getId());
assertEquals(0, runtimeService.createProcessInstanceQuery().count()); }
}

不过值得注意的是你测试之前需要将org.activiti.engine.test.ActivitiRule配置注册到Spring的文件中,它会在上述例子中自动注入到测试中。

<bean id="activitiRule" class="org.activiti.engine.test.ActivitiRule">
<property name="processEngine" ref="processEngine" />
</bean>

目前本人使用Spring-Boot不多(就没怎么用),至于Spring-Boot和Activiti的集成,感兴趣的同学可以自己去了解,反正我觉得太过于封装的东西不要太过于追求。

六、 工作流动态发布

将流程定义等资源文件以zip压缩格式打包后,activiti引擎将会去扫描含有.bpmn20.xml 或者 .bpmn扩展名的文件,这些文件将会被识别解析。但注意不要将java文件以及编译后文件也加到打包文件中,流程引擎只会加载当前类路径下面的类文件,同时加到打包文件的class文件也不会被加到类路径下面去。

这时后台需要代码实现发布功能,比如这样:

String barFileName = "path/to/process-one.bar";
ZipInputStream inputStream = new ZipInputStream(new FileInputStream(barFileName)); repositoryService.createDeployment()
.name("process-one.bar")
.addZipInputStream(inputStream)
.deploy();

在官方提供的Activiti Explorer项目中也有动态发布功能集成,将其项目部署到tomcat后,我使用了管理员Kermit登录系统,点击管理-->部署包--->添加新的部署包

弹出一个上传文件的弹出框:

而我们资源打包文件引用到的类需要放到流程引擎的classpath下面.

6.1 流程定义的版本

BPMN是没有版本的概念的,bpmn文件交给版本控制软件(比如SVN,git,Mercurial)进行管理,这样子设计方便我们对项目的集中管理,而存入数据库的流程定义的版本是工作流在发布期间标识的版本。每一个流程定义写进数据库需要初始化这些属性:key, version, name 和id。

id的值来自流程定义文件的key值。name值来自于流程定义文件的name值,第一次发布默认version是1,随后的发布会将版本递增。id的取值是{流程定义的key值}:{流程定义的版本}:{随机数},而这个随机数在分布式环境中也是保证是唯一的。

比如下面这个流程定义文件:

 <definitions id="myDefinitions" >
<process id="myProcess" name="My important process" >

发布过后在数据库中保存的数据就是这样:

id key name version

myProcess:1:676

myProcess

My important process

1

假使我们重新发布了流程定义文件,但文件中id的值并没有修改,数据库中的数据变成了这样子:

myProcess:1:676

myProcess

My important process

1

myProcess:2:870

myProcess

My important process

2

如果我们修改了id,再次发布数据库数据就成这样子:

id key name version

myProcess:1:676

myProcess

My important process

1

myProcess:2:870

myProcess

My important process

2

myNewProcess:1:1033

myNewProcess

My important process

1

可以看出发布区分新的流程定义会根据key进行判断,Activiti仅仅会根据key值的不同进行区分,所以流程版本变成了1。

6.2 添加流程图发布

发布过程中可以添加流程图进去, 流程图会被视为资源而保存起来,流程图主要用在Activiti Explorer的页面中显示给用户查看。假定我们有一个流程的xml文件在类路径下面,activiti使用命名规则进行获取流程图:

  • 如果流程图已经存在并且使用流程定义文件名作为图片名称,当然后缀名是git、jpg、png都可以,将会被获取加载,如果有其他名称的图片文件,将会被忽视。
  • 如果图片文件在当前流程定义文件的同一个目录里面不存在。

人工添加流程图代码实现就是如下所示:

 repositoryService.createDeployment()
.name("expense-process.bar")
.addClasspathResource("org/activiti/expenseProcess.bpmn20.xml")
.addClasspathResource("org/activiti/expenseProcess.png")
.deploy();

取出图片资源代码实现:

 ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.processDefinitionKey("expense")
.singleResult(); String diagramResourceName = processDefinition.getDiagramResourceName();
InputStream imageStream = repositoryService.getResourceAsStream(
processDefinition.getDeploymentId(), diagramResourceName);

如果bpmn文件中含有必要的信息,activiti将会生成流程图片,如果不想生成可以设置:

<property name="createDiagramOnDeploy" value="false" />

bpmn文件可以被用户进行自定义分类,帮助用户自己识别使用,在bpmn文件的:<definitions …​ targetNamespace="yourCategory" 可以设置。

如果使用代码实现这个功能可以这样:

 repositoryService
.createDeployment()
.category("yourCategory")
...
.deploy();

Activiti工作流学习-----基于5.19.0版本(5)的更多相关文章

  1. Activiti工作流学习-----基于5.19.0版本(8)

    8.1.5 Start Event 继续上一篇的事件的分享笔记,Start Event指明该处是流程开始,至于开始事件的类型(消息到达开始,指定的事件循环开始等),定义如何开始是在开始事件圆圈图标里面 ...

  2. Activiti工作流学习-----基于5.19.0版本(2)

    二.activiti.cfg.xml的其他bean节点配置 2.1 新特性:Job Executor和Async Executor 从5.17.0版本的activiti开始提供作业执行者(Job Ex ...

  3. Activiti工作流学习-----基于5.19.0版本(6)

    七. BPMN的简介 读者了解到这里,应付一般的工作流开发已经足够了.此处应该有华丽的分割线,在工作流项目中核心开发人员主要是对工作流业务设计以及实现,而初级开发人员是对业务功能的代码实现.以后将主要 ...

  4. Activiti工作流学习-----基于5.19.0版本(1)

    该版本的Activiti运行须知: 1.JDK 6+,Eclipse最好是Kepler以上版本. 2.试验功能都有EXPERIMENTAL标注,被标注的部分不应该视为稳定的. 有兴趣的同学可以去了解下 ...

  5. Activiti工作流学习-----基于5.19.0版本(4)

    四.使用工作流开发 org.activiti.engine.ProcessEngine提供的Service作用在工作流引擎上面,如果所示是模仿一个公司简单的审批流程,你可以下载这个Demo:Activ ...

  6. Activiti工作流学习-----基于5.19.0版本(7)

    八.BPMN 2.0流程图详解 BPMN 2.0的标准的出现是好事,用户不在被某个工作流开发商绑架或者在工作流中开发妥协,Activiti作为BPMN标准的一套解决方案,使得用户在选择工作流框架时可以 ...

  7. Activiti工作流学习-----基于5.19.0版本(3)

    前面关于eventType的属性值的配置简单的说了一下,activiti支持的值如下表所示:这是我摘抄的activiti官网的 Event 的名字 描述 Event的类名 ENGINE_CREATED ...

  8. Activiti工作流学习之流程图应用详解

    Activiti工作流学习之流程图应用详解 1.目的  了解Activiti工作流是怎样应用流程图的. 2.环境准备2.1.相关软件及版本    jdk版本:Jdk1.7及以上 IDE:eclipse ...

  9. Activiti工作流学习之概述(一)

    一.工作流介绍 我第一次听到这个词,是蒙逼的,再看百度百度,更傻眼了,完全说的不像人话啊,举几个生活中的例子,就明白多了比如:请假.报销等等,如果文字太过抽象,请看图: 二.工作流引擎 Process ...

随机推荐

  1. Gridview中奇偶数行颜色设置

    在gridview中的RowDataBound事件里面写 switch (e.Row.RowType) {case DataControlRowType.Header: e.Row.BackColor ...

  2. 部分无线终端不响应键盘事件(keydown,keypress,keyup)的解决办法

    在无线侧实现搜索显示smartbox功能的时候,会对输入框绑定keydown.keyup.keypress事件,从而在检测到输入框的值发生改变时,发出请求拉取smartbox的内容. 但是,在iPho ...

  3. 超酷创意HTML5动画演示及代码

    HTML5是未来的网页开发神器,今天分享的这些HTML5动画大部分利用了CSS3的动画属性来实现,废话不多说,直接上演示和代码. HTML5/CSS3实现大风车旋转动画 这次我们要来分享一款很酷的HT ...

  4. Laravel5.2 下使用Form

    laravel到了5.1.*以上版本,便没有了illuminate/html类库的支持, 我试着把illuminate/html类库加入了laravel5.2,依然没有用, 但是laravelcoll ...

  5. RequireJS进阶(三)

    进阶的前面两篇讲述了r.js如何通过命令行把所有的模块压缩为一个js文件或把所有的css压缩为一个css文件.其中包括一些压缩配置参数的使用. 但以上两种方式有几个问题 1.通过命令手动配置压缩选项显 ...

  6. Js中执行变量中的命令语句,也就是所谓的宏替换(很实用的例子)

    Js中执行变量中的命令语句,也就是所谓的宏替换(很实用的例子) 由其做动态编程时非常有用,必须符合js中的语法,用eval能够执行. var aaa="alert('这是变量中的语句')&q ...

  7. Linux(CentOS 5.5) Redis安装

    一,什么是redis redis是一个key-value存储系统. 和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合)和zset ...

  8. Linux Resin 安装配置

    Resin是一个非常流行的application server,对servlet和JSP提供了良好的支持,性能优良,resin自身采用Java语言开发.Resin Pro版本支持缓存和负载均衡,收费最 ...

  9. UE4学习笔记(三): 为什么使用C++替代UnrealScript?

    原文链接: https://forums.unrealengine.com/showthread.php?2574-Why-C-for-Unreal-4&p=16252&viewful ...

  10. Yii PHP Framework有用新手教程

    说明:由于近期工作工作关系,须要开发一个在Linux下执行的Web Application,须要对如今比較流行的一些PHP框架做一个了解和评估,以下的这篇文章是笔者近期学习一个比較新的PHP Fram ...