上一篇:Spring學(xué)習(xí)之整合MyBatis
下一篇:Spring學(xué)習(xí)之整合Activiti(二)
1. 背景
Activiti是現(xiàn)在應(yīng)用很廣的一個(gè)流程框架,自己在學(xué)習(xí)過(guò)程中看到官網(wǎng)有Activiti Modeler可以使用頁(yè)面管理Activiti流程集漾,所以試著自己整合SpringMVC+Activiti Modeler裆站。
1.1. 工作流與工作流引擎
工作流(workflow)就是工作流程的計(jì)算模型铃慷,即將工作流程中的工作如何前后組織在一起的邏輯和規(guī)則在計(jì)算機(jī)中以恰當(dāng)?shù)哪P瓦M(jìn)行表示并對(duì)其實(shí)施計(jì)算。它主要解決的是“使在多個(gè)參與者之間按照某種預(yù)定義的規(guī)則傳遞文檔桂躏、信息或任務(wù)的過(guò)程自動(dòng)進(jìn)行宋渔,從而實(shí)現(xiàn)某個(gè)預(yù)期的業(yè)務(wù)目標(biāo),或者促使此目標(biāo)的實(shí)現(xiàn)”督暂。(我的理解就是:將部分或者全部的工作流程、邏輯讓計(jì)算機(jī)幫你來(lái)處理穷吮,實(shí)現(xiàn)自動(dòng)化)
所謂工作流引擎是指workflow作為應(yīng)用系統(tǒng)的一部分逻翁,并為之提供對(duì)各應(yīng)用系統(tǒng)有決定作用的根據(jù)角色、分工和條件的不同決定信息傳遞路由捡鱼、內(nèi)容等級(jí)等核心解決方案八回。
1.2. BPMN2.0規(guī)范
BPMN(Business Process Model and Notation)--業(yè)務(wù)流程模型與符號(hào)。
BPMN是一套流程建模的標(biāo)準(zhǔn)驾诈,主要目標(biāo)是被所有業(yè)務(wù)用戶容易理解的符號(hào)缠诅,支持從創(chuàng)建流程輪廓的業(yè)務(wù)分析到這些流程的最終實(shí)現(xiàn),知道最終用戶的管理監(jiān)控乍迄。
通俗一點(diǎn)其實(shí)就是一套規(guī)范管引,畫(huà)流程模型的規(guī)范。流程模型包括:流程圖闯两、協(xié)作圖褥伴、編排圖、會(huì)話圖漾狼。詳細(xì)信息請(qǐng)google重慢。
1.3. Activiti概述
1.3.1. Activiti由來(lái)
學(xué)習(xí)過(guò)Activiti的朋友都知道,Activiti的創(chuàng)始人也就是JBPM(也是一個(gè)優(yōu)秀的BPM引擎)的創(chuàng)始人逊躁,從Jboss離職后開(kāi)發(fā)了一個(gè)新的BPM引擎:Activiti似踱。所以,Activiti有很多地方都有JBPM的影子稽煤。所以核芽,據(jù)說(shuō)學(xué)習(xí)過(guò)JBPM的朋友學(xué)起Activiti來(lái)非常順手。
由于本人之前沒(méi)有工作流及JBPM的相關(guān)基礎(chǔ)念脯,剛開(kāi)始學(xué)習(xí)Activiti的時(shí)候可以說(shuō)是無(wú)比痛苦的狞洋,根本不知道從何下手,這里也建議大家先進(jìn)行工作流及BPMN2.0規(guī)范的學(xué)習(xí)绿店,有了一定的基礎(chǔ)后吉懊,再著手學(xué)習(xí)Activiti庐橙。
1.3.2. Activiti簡(jiǎn)介
Activiti是一個(gè)開(kāi)源的工作流引擎,它實(shí)現(xiàn)了BPMN 2.0規(guī)范借嗽,可以發(fā)布設(shè)計(jì)好的流程定義态鳖,并通過(guò)api進(jìn)行流程調(diào)度。
Activiti 作為一個(gè)遵從 Apache 許可的工作流和業(yè)務(wù)流程管理開(kāi)源平臺(tái)恶导,其核心是基于 Java 的超快速浆竭、超穩(wěn)定的 BPMN2.0 流程引擎,強(qiáng)調(diào)流程服務(wù)的可嵌入性和可擴(kuò)展性惨寿,同時(shí)更加強(qiáng)調(diào)面向業(yè)務(wù)人員邦泄。
Activiti 流程引擎重點(diǎn)關(guān)注在系統(tǒng)開(kāi)發(fā)的易用性和輕量性上。每一項(xiàng) BPM 業(yè)務(wù)功能 Activiti 流程引擎都以服務(wù)的形式提供給開(kāi)發(fā)人員裂垦。通過(guò)使用這些服務(wù)顺囊,開(kāi)發(fā)人員能夠構(gòu)建出功能豐富、輕便且高效的 BPM 應(yīng)用程序蕉拢。
2. 前期準(zhǔn)備
本文是在Spring學(xué)習(xí)之整合MyBatis的基礎(chǔ)上完成的特碳,所以不清楚的可以點(diǎn)擊查看
2.1. Activiti所需環(huán)境
使用Activiti,首先當(dāng)然要有jdk了晕换!6+版本就可以了午乓。其次,要有一款I(lǐng)DE闸准,我們當(dāng)然會(huì)使用Eclipse益愈。然后,web容器當(dāng)然也要有恕汇,這里使用Tomcat7.0版本腕唧。然后就是Activiti的Eclipse插件了,這個(gè)后面再介紹瘾英。
2.2. 下載Activiti Demo包
下載activiti-5.22.0.rar枣接,官網(wǎng)地址大家可以自行百度,但是下載會(huì)被墻缺谴,網(wǎng)盤(pán)地址:https://pan.baidu.com/s/1XVTammPbIrbzU1MK7TBFOA
2.3. 配置pom.xml文件
新增activiti依賴:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.version>4.10</junit.version>
<spring.version>4.1.3.RELEASE</spring.version>
<jackson.version>2.7.4</jackson.version>
<activiti.version>5.22.0</activiti.version>
</properties>
<!-- 其他依賴省略... -->
<!-- activiti start -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
<version>${activiti.version}</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring</artifactId>
<version>${activiti.version}</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-model</artifactId>
<version>${activiti.version}</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-layout</artifactId>
<version>${activiti.version}</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-common-rest</artifactId>
<version>${activiti.version}</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-crystalball</artifactId>
<version>${activiti.version}</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-diagram-rest</artifactId>
<version>${activiti.version}</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-explorer</artifactId>
<version>${activiti.version}</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-json-converter</artifactId>
<version>${activiti.version}</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-modeler</artifactId>
<version>${activiti.version}</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-simple-workflow</artifactId>
<version>${activiti.version}</version>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>xmlgraphics-commons</artifactId>
<version>1.2</version>
</dependency>
<!-- activiti end -->
Batik包 在添加activiti-modeler依賴后自動(dòng)加載但惶,不用顯式添加依賴
2.4. 配置spring-activiti.xml文件
在resources/spring/ 新建spring-activiti.xml配置文件:
spring-activiti.xml
文件內(nèi)容如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">
<!-- ==================== Activiti配置 start =================== -->
<!-- 單例json對(duì)象 -->
<bean id="objectMapper" class="com.fasterxml.jackson.databind.ObjectMapper"/>
<!-- activiti的processEngine配置 -->
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
<property name="dataSource" ref="jdbcDataSource" />
<property name="transactionManager" ref="transactionManager" />
<!-- 自動(dòng)創(chuàng)建表 -->
<property name="databaseSchemaUpdate" value="false" />
<!-- 是否激活A(yù)ctiviti的任務(wù)調(diào)度 -->
<property name="jobExecutorActivate" value="false" />
<!-- 是否開(kāi)啟工作的數(shù)據(jù)日志 -->
<!-- <property name="enableDatabaseEventLogging" value="true" /> -->
<!--<property name="history" value="full"/>-->
<property name="processDefinitionCacheLimit" value="10"/>
<!-- mail -->
<!-- <property name="mailServerHost" value="localhost"/>
<property name="mailServerUsername" value="kafeitu"/>
<property name="mailServerPassword" value="000000"/>
<property name="mailServerPort" value="2025"/> -->
<!-- UUID作為主鍵生成策略 -->
<!-- <property name="idGenerator" ref="uuidGenerator" /> -->
<!-- 生成流程圖的字體 -->
<property name="activityFontName" value="宋體"/>
<property name="labelFontName" value="宋體"/>
<!-- 緩存支持
<property name="processDefinitionCache">
<bean class="me.kafeitu.demo.activiti.util.cache.DistributedCache" />
</property>-->
<!-- 自動(dòng)部署 -->
<!-- <property name="deploymentResources">
<list>
<value>classpath*:/deployments/*</value>
</list>
</property> -->
<!-- 自定義表單字段類型 -->
<!-- <property name="customFormTypes">
<list>
<bean class="me.kafeitu.demo.activiti.activiti.form.UsersFormType"/>
</list>
</property> -->
<!--不創(chuàng)建identity表 -->
<property name="dbIdentityUsed" value="false"/>
<!-- 自定義用戶管理 -->
<property name="customSessionFactories">
<list>
<bean class="com.zr.workflow.activiti.utils.CustomUserEntityManagerFactory">
<property name="customUserEntityManager" ref="customUserEntityManager"></property>
</bean>
<bean class="com.zr.workflow.activiti.utils.CustomGroupEntityManagerFactory">
<property name="customGroupEntityManager" ref="customGroupEntityManager"></property>
</bean>
</list>
</property>
</bean>
<!-- 注冊(cè)自定義用戶管理類 -->
<bean id="customUserEntityManager" class="com.zr.workflow.activiti.utils.CustomUserEntityManager"></bean>
<bean id="customGroupEntityManager" class="com.zr.workflow.activiti.utils.CustomGroupEntityManager"></bean>
<!-- 加載activiti引擎processEngine -->
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean" destroy-method="destroy">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>
<!-- activiti的7大服務(wù)接口 -->
<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="formService" factory-bean="processEngine" factory-method="getFormService" />
<bean id="historyService" factory-bean="processEngine" factory-method="getHistoryService" />
<bean id="managementService" factory-bean="processEngine" factory-method="getManagementService" />
<!-- <bean id="identityService" factory-bean="processEngine" factory-method="getIdentityService" /> -->
<!-- ==================== Activiti配置 end =================== -->
</beans>
其中,需要注意的是:
1湿蛔、databaseSchemaUpdate
膀曾,項(xiàng)目啟動(dòng)是否自動(dòng)創(chuàng)建數(shù)據(jù)表(activiti的23張表),這里我設(shè)置成false阳啥,因?yàn)槲覜](méi)有用activiti的用戶管理表添谊,而是采用自定義的用戶管理表,所以在項(xiàng)目啟動(dòng)之前需要將所有需要的表創(chuàng)建完成察迟。
2斩狱、dbIdentityUsed
:是否創(chuàng)建identity用戶相關(guān)表耳高;由于項(xiàng)目已經(jīng)有一套用戶管理表,所以這里設(shè)置成false
所踊;
3泌枪、新增property customSessionFactories
指定自定義用戶管理工廠,包括:用戶管理和組管理秕岛。
其中班套,用戶管理工廠CustomUserEntityManagerFactory.java
如下:
package com.zr.workflow.activiti.util;
import javax.annotation.Resource;
import org.activiti.engine.impl.interceptor.Session;
import org.activiti.engine.impl.interceptor.SessionFactory;
import org.activiti.engine.impl.persistence.entity.UserIdentityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 自定義用戶管理罚舱,用戶session
* 這里的屬性需要與`spring-activiti.xml`中的該類的property一致
* 不能使用identityService
* @author Administrator
*
*/
@Service
public class CustomUserEntityManagerFactory implements SessionFactory {
@Resource
private CustomUserEntityManager customUserEntityManager;
@Override
public Class<?> getSessionType() {
return UserIdentityManager.class;
}
@Override
public Session openSession() {
return customUserEntityManager;
}
@Autowired
public void setCustomUserEntityManager(CustomUserEntityManager customUserEntityManager) {
this.customUserEntityManager = customUserEntityManager;
}
}
組管理工廠 CustomGroupEntityManagerFactory.java
:
package com.zr.workflow.activiti.util;
import javax.annotation.Resource;
import org.activiti.engine.impl.interceptor.Session;
import org.activiti.engine.impl.interceptor.SessionFactory;
import org.activiti.engine.impl.persistence.entity.GroupIdentityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 自定義用戶管理顷蟆,用戶組session
* 這里的屬性需要與spring-activiti.xml中的該類的property一致
* 不能使用identityService
* @author Administrator
*
*/
@Service
public class CustomGroupEntityManagerFactory implements SessionFactory {
@Resource
private CustomGroupEntityManager customGroupEntityManager;
@Override
public Class<?> getSessionType() {
return GroupIdentityManager.class;
}
@Override
public Session openSession() {
return customGroupEntityManager;
}
@Autowired
public void setCustomGroupEntityManager(CustomGroupEntityManager customGroupEntityManager) {
this.customGroupEntityManager = customGroupEntityManager;
}
}
4爽待、注冊(cè)自動(dòng)義用戶管理類。
由于我們這個(gè)項(xiàng)目中設(shè)置任務(wù)執(zhí)行人時(shí)沒(méi)有涉及到候選組遏考,都是指定具體的執(zhí)行人或獲取某個(gè)角色組中所有的人員叠殷,然后調(diào)用setCandidateUsers
方法將某個(gè)候選組所有的成員加進(jìn)去,如果項(xiàng)目需要設(shè)置候選組诈皿,請(qǐng)擴(kuò)展這兩個(gè)類:CustomUserEntityManager
和CustomGroupEntityManager
,重寫(xiě)其中的
hasUser()
(必須實(shí)現(xiàn))像棘、
getUserName()
(可選)稽亏、
getUserPassword()
(可選)、
getUserEmail()
(可選)缕题、
getRoleList()
(必須實(shí)現(xiàn))截歉。
然后將實(shí)現(xiàn)類注冊(cè)到spring_activiti.xml中,替換以下兩行:
<!-- 注冊(cè)自定義用戶管理類 -->
<bean id="customUserEntityManager" class="com.zr.workflow.activiti.utils.CustomUserEntityManager"></bean>
<bean id="customGroupEntityManager" class="com.zr.workflow.activiti.utils.CustomGroupEntityManager"></bean>
CustomUserEntityManager.java
代碼如下:
package com.zr.workflow.activiti.util;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.activiti.engine.identity.Group;
import org.activiti.engine.identity.User;
import org.activiti.engine.impl.persistence.entity.GroupEntity;
import org.activiti.engine.impl.persistence.entity.UserEntity;
import org.activiti.engine.impl.persistence.entity.UserEntityManager;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.stereotype.Component;
@Component
public class CustomUserEntityManager extends UserEntityManager {
public User findUserById(String userId) {
System.out.println("CustomUserEntityManager findUserById userId:" + userId);
if (userId == null)
return null;
try {
UserEntity userEntity = new UserEntity();
boolean hasUser = hasUser(userId);
if (!hasUser)return null;
userEntity.setId(userId);
userEntity.setFirstName(getUserName(userId));
userEntity.setPassword(getUserPassword(userId));
userEntity.setEmail(getUserEmail(userId));
userEntity.setRevision(1);
return userEntity;
} catch (EmptyResultDataAccessException e) {
e.printStackTrace();
return null;
}
}
public boolean hasUser(String userId) {
return true;
}
public List<Group> findGroupsByUser(String userId) {
System.out.println("CustomUserEntityManager findGroupsByUser userId:" + userId);
if (userId == null)
return null;
boolean hasUser = hasUser(userId);
if (!hasUser)return null;
List<Map<String,Object>> roleList = getRoleList(userId);
List<Group> groupEntitys = new ArrayList<Group>();
if(null != roleList) {
for (Map<String, Object> role : roleList) {
String roleCode = null == role.get("roleCode")?"":role.get("roleCode").toString();
String roleName = null == role.get("roleName")?"":role.get("roleName").toString();
GroupEntity groupEntity = toActivitiGroup(roleCode,roleName);
groupEntitys.add(groupEntity);
}
}
return groupEntitys;
}
public static GroupEntity toActivitiGroup(String roleCode,String roleName) {
GroupEntity groupEntity = new GroupEntity();
groupEntity.setRevision(1);
groupEntity.setType("assignment");
groupEntity.setId(roleCode);
groupEntity.setName(roleName);
return groupEntity;
}
public String getUserName(String userId) {
return "";
}
public String getUserPassword(String userId) {
return "";
}
public String getUserEmail(String userId) {
return "";
}
public List<Map<String, Object>> getRoleList(String userId) {
return null;
// List<Map<String,Object>> roleList = new ArrayList<>();
// List<MisUserRole> misUserRoleList = misUserRoleDao.findByUserId(userId);
// for (MisUserRole misUserRole : misUserRoleList) {
// final String roleId = misUserRole.getRoleId();
// boolean isExitRole = misRoleDao.findRoleById(roleId) != null && misRoleDao.findRoleById(roleId).size()>0;
// MisRole role = isExitRole ? misRoleDao.findRoleById(roleId).get(0) : null;
// Map<String, Object> roleMap = new HashMap<>();
// if(role != null){
// roleMap.put("roleCode", role.getCode());
// roleMap.put("roleName", role.getName());
//
// }
// roleList.add(roleMap);
// }
// return roleList;
}
}
CustomGroupEntityManager.java
代碼如下:
package com.zr.workflow.activiti.util;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.activiti.engine.identity.Group;
import org.activiti.engine.impl.persistence.entity.GroupEntity;
import org.activiti.engine.impl.persistence.entity.GroupEntityManager;
import org.springframework.stereotype.Component;
@Component
public class CustomGroupEntityManager extends GroupEntityManager {
public boolean hasUser(String userId) {
return true;
}
public List<Group> findGroupsByUser(String userId) {
System.out.println("CustomUserEntityManager findGroupsByUser userId:" + userId);
if (userId == null)
return null;
boolean hasUser = hasUser(userId);
if (!hasUser)return null;
List<Map<String,Object>> roleList = getRoleList(userId);
List<Group> groupEntitys = new ArrayList<Group>();
if(null != roleList) {
for (Map<String, Object> role : roleList) {
String roleCode = null == role.get("roleCode")?"":role.get("roleCode").toString();
String roleName = null == role.get("roleName")?"":role.get("roleName").toString();
GroupEntity groupEntity = toActivitiGroup(roleCode,roleName);
groupEntitys.add(groupEntity);
}
}
return groupEntitys;
}
public static GroupEntity toActivitiGroup(String roleCode,String roleName) {
GroupEntity groupEntity = new GroupEntity();
groupEntity.setRevision(1);
groupEntity.setType("assignment");
groupEntity.setId(roleCode);
groupEntity.setName(roleName);
return groupEntity;
}
public List<Map<String, Object>> getRoleList(String userId) {
return null;
// List<Map<String,Object>> roleList = new ArrayList<>();
// List<MisUserRole> misUserRoleList = misUserRoleDao.findByUserId(userId);
// for (MisUserRole misUserRole : misUserRoleList) {
// final String roleId = misUserRole.getRoleId();
// boolean isExitRole = misRoleDao.findRoleById(roleId) != null && misRoleDao.findRoleById(roleId).size()>0;
// MisRole role = isExitRole ? misRoleDao.findRoleById(roleId).get(0) : null;
// Map<String, Object> roleMap = new HashMap<>();
// if(role != null){
// roleMap.put("roleCode", role.getCode());
// roleMap.put("roleName", role.getName());
//
// }
// roleList.add(roleMap);
// }
// return roleList;
}
}
3. 開(kāi)始整合
3.1. 代碼拷貝
把Activiti-webapp-explorer2項(xiàng)目的resources下的stencilset.json文件拷至我的項(xiàng)目中的resources目錄下:
解壓出activiti-5.22.0.rar烟零,看到如下目錄:
- database:里面存放的是Activiti使用到的數(shù)據(jù)庫(kù)信息的sql文件瘪松,它支持的數(shù)據(jù)庫(kù)類型如下圖,使用時(shí)只需執(zhí)行你自己的數(shù)據(jù)庫(kù)類型的文件即可锨阿。如:你的數(shù)據(jù)庫(kù)是mysql宵睦,那么就執(zhí)行activiti.mysql.create.*.sql即可。( 注意: 這里由于采用的是自定義用戶管理墅诡,則去掉activiti其中創(chuàng)建用戶相關(guān)表的sql語(yǔ)句壳嚎,最終的sql文件請(qǐng)見(jiàn)項(xiàng)目中的init_activities_empty.sql:):
注意:
sql中新增表act_cus_user_task:
-- ----------------------------
-- Table structure for act_cus_user_task
-- ----------------------------
DROP TABLE IF EXISTS `act_cus_user_task`;
CREATE TABLE `act_cus_user_task` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`PROC_DEF_KEY` varchar(255) DEFAULT NULL COMMENT '流程id',
`PROC_DEF_NAME` varchar(255) DEFAULT NULL COMMENT '流程名',
`TASK_DEF_KEY` varchar(255) DEFAULT NULL COMMENT '節(jié)點(diǎn)id',
`TASK_NAME` varchar(255) DEFAULT NULL COMMENT '節(jié)點(diǎn)名',
`ACTIVITY_TYPE` varchar(255) DEFAULT '' COMMENT '當(dāng)前Activiti節(jié)點(diǎn)類型:N-普通用戶任務(wù);M-多實(shí)例節(jié)點(diǎn)',
`TASK_TYPE` varchar(255) DEFAULT NULL COMMENT '節(jié)點(diǎn)的處理人員類型:assignee(人員)末早、candidateUser(候選人)烟馅、candidateGroup(候選組)',
`CANDIDATE_NAME` varchar(255) DEFAULT NULL COMMENT '執(zhí)行人名',
`CANDIDATE_IDS` varchar(255) DEFAULT NULL COMMENT '執(zhí)行人id',
`GROUP_ID` varchar(255) DEFAULT NULL COMMENT '組id',
`GROUP_NAME` varchar(255) DEFAULT NULL COMMENT '組名稱',
PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=465 DEFAULT CHARSET=utf8;
act_cus_user_task:業(yè)務(wù)用戶信息關(guān)聯(lián)至工作流用戶任務(wù)表
docs:毫無(wú)疑問(wèn),api文檔然磷。
libs:使用Activiti所需要的所有的jar包和源文件郑趁。
wars:官方給我們提供的示例Demo,通過(guò)使用Demo可以更加快速的了解Activiti姿搜。
找到wars目錄下的 activiti-explorer.war寡润, 將其拷貝到Tomcat 的 webapps目錄下捆憎,然后運(yùn)行tomcat: /bin/statup.bat(如果啟動(dòng)時(shí)控制臺(tái)一閃而過(guò),請(qǐng)看Tomcat7中雙擊bin文件的startup.bat一閃而過(guò)解決辦法)悦穿,啟動(dòng)tomcat 之后攻礼,會(huì)自動(dòng)將activiti-explorer.war 解壓。
進(jìn)入到下圖目錄中栗柒,將diagram-viewer礁扮,editor-app和modeler.html拷貝到自己工程的webapp目錄下。
將下圖路徑中的StencilsetRestResource.class瞬沦。和下圖路徑中的ModelEditorJsonRestResource.class太伊,ModelSaveRestResource.class。反編譯.
在自己的項(xiàng)目中新建對(duì)應(yīng)的class逛钻,將反編譯內(nèi)容復(fù)制進(jìn)去僚焦,如圖:
其中,StencilsetRestResource
類,是加載模板設(shè)置,加載resources
下的stencilset.json
,其代碼如下:
package com.zr.activiti.controller.design.model;
import java.io.InputStream;
import org.activiti.engine.ActivitiException;
import org.apache.commons.io.IOUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class StencilsetRestResource
{
@RequestMapping(value={"/editor/stencilset"}, method={org.springframework.web.bind.annotation.RequestMethod.GET}, produces={"application/json;charset=utf-8"})
public String getStencilset()
{
System.out.println("StencilsetRestResource.getStencilset-----------");
InputStream stencilsetStream = getClass().getClassLoader().getResourceAsStream("stencilset.json");
try {
return IOUtils.toString(stencilsetStream, "utf-8");
} catch (Exception e) {
throw new ActivitiException("Error while loading stencil set", e);
}
}
}
ModelEditorJsonRestResource
類是根據(jù)模型名稱讀取以Json
存儲(chǔ)在act_ge_bytearray
表中的source
,編輯器接收到之后解析展示為圖片,其核心代碼如下:
package com.zr.activiti.controller.design.model;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Model;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ModelEditorJsonRestResource
implements ModelDataJsonConstants
{
protected static final Logger LOGGER = LoggerFactory.getLogger(ModelEditorJsonRestResource.class);
@Autowired
private RepositoryService repositoryService;
@Autowired
private ObjectMapper objectMapper;
@RequestMapping(value={"/model/{modelId}/json"}, method={org.springframework.web.bind.annotation.RequestMethod.GET}, produces={"application/json"})
public ObjectNode getEditorJson(@PathVariable String modelId) { ObjectNode modelNode = null;
System.out.println("ModelEditorJsonRestResource.getEditorJson---------");
Model model = this.repositoryService.getModel(modelId);
if (model != null) {
try {
if (StringUtils.isNotEmpty(model.getMetaInfo())) {
modelNode = (ObjectNode)this.objectMapper.readTree(model.getMetaInfo());
} else {
modelNode = this.objectMapper.createObjectNode();
modelNode.put("name", model.getName());
}
modelNode.put("modelId", model.getId());
ObjectNode editorJsonNode = (ObjectNode)this.objectMapper.readTree(new String(this.repositoryService
.getModelEditorSource(model
.getId()), "utf-8"));
modelNode.put("model", editorJsonNode);
}
catch (Exception e) {
LOGGER.error("Error creating model JSON", e);
throw new ActivitiException("Error creating model JSON", e);
}
}
return modelNode;
}
}
ModelSaveRestResource
就是將經(jīng)過(guò)編輯器編輯過(guò)的模型保存起來(lái),核心代碼如下:
package com.zr.activiti.controller.design.model;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Model;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.PNGTranscoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ModelSaveRestResource
implements ModelDataJsonConstants
{
protected static final Logger LOGGER = LoggerFactory.getLogger(ModelSaveRestResource.class);
@Autowired
private RepositoryService repositoryService;
@Autowired
private ObjectMapper objectMapper;
@RequestMapping(value={"/model/{modelId}/save"}, method={org.springframework.web.bind.annotation.RequestMethod.PUT})
@ResponseStatus(HttpStatus.OK)
public void saveModel(@PathVariable String modelId, @RequestBody MultiValueMap<String, String> values) {
try {
Model model = this.repositoryService.getModel(modelId);
System.out.println("ModelSaveRestResource.saveModel----------");
ObjectNode modelJson = (ObjectNode)this.objectMapper.readTree(model.getMetaInfo());
modelJson.put("name", (String)values.getFirst("name"));
modelJson.put("description", (String)values.getFirst("description"));
model.setMetaInfo(modelJson.toString());
model.setName((String)values.getFirst("name"));
model.setKey(modelId);
this.repositoryService.saveModel(model);
this.repositoryService.addModelEditorSource(model.getId(), ((String)values.getFirst("json_xml")).getBytes("utf-8"));
InputStream svgStream = new ByteArrayInputStream(((String)values.getFirst("svg_xml")).getBytes("utf-8"));
TranscoderInput input = new TranscoderInput(svgStream);
PNGTranscoder transcoder = new PNGTranscoder();
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
TranscoderOutput output = new TranscoderOutput(outStream);
transcoder.transcode(input, output);
byte[] result = outStream.toByteArray();
this.repositoryService.addModelEditorSourceExtra(model.getId(), result);
outStream.close();
} catch (Exception e){
LOGGER.error("Error saving model", e);
throw new ActivitiException("Error saving model", e);
}
}
}
3.2. 核心組件介紹
3.2.1. 關(guān)鍵對(duì)象
Deployment:流程部署對(duì)象,部署一個(gè)流程時(shí)創(chuàng)建曙痘。
ProcessDefinitions:流程定義芳悲,部署成功后自動(dòng)創(chuàng)建。
ProcessInstances:流程實(shí)例边坤,啟動(dòng)流程時(shí)創(chuàng)建名扛。
Task:任務(wù),在Activiti中的Task僅指有角色參與的任務(wù)茧痒,即定義中的UserTask肮韧。
Execution:執(zhí)行計(jì)劃,流程實(shí)例和流程執(zhí)行中的所有節(jié)點(diǎn)都是Execution旺订,如UserTask弄企、ServiceTask等。
3.2.2. 服務(wù)接口
ProcessEngine:流程引擎的抽象区拳,通過(guò)它我們可以獲得我們需要的一切服務(wù)拘领。
RepositoryService:Activiti中每一個(gè)不同版本的業(yè)務(wù)流程的定義都需要使用一些定義文件,部署文件和支持?jǐn)?shù)據(jù)(例如BPMN2.0 XML文件劳闹,表單定義文件院究,流程定義圖像文件等),這些文件都存儲(chǔ)在Activiti內(nèi)建的Repository中本涕。RepositoryService提供了對(duì) repository的存取服務(wù)业汰。
RuntimeService:在Activiti中,每當(dāng)一個(gè)流程定義被啟動(dòng)一次之后菩颖,都會(huì)生成一個(gè)相應(yīng)的流程對(duì)象實(shí)例样漆。RuntimeService提供了啟動(dòng)流程、查詢流程實(shí)例晦闰、設(shè)置獲取流程實(shí)例變量等功能放祟。此外它還提供了對(duì)流程部署鳍怨,流程定義和流程實(shí)例的存取服務(wù)。
TaskService: 在Activiti中業(yè)務(wù)流程定義中的每一個(gè)執(zhí)行節(jié)點(diǎn)被稱為一個(gè)Task跪妥,對(duì)流程中的數(shù)據(jù)存取鞋喇,狀態(tài)變更等操作均需要在Task中完成。TaskService提供了對(duì)用戶Task 和Form相關(guān)的操作眉撵。它提供了運(yùn)行時(shí)任務(wù)查詢侦香、領(lǐng)取、完成纽疟、刪除以及變量設(shè)置等功能罐韩。
IdentityService: Activiti中內(nèi)置了用戶以及組管理的功能,必須使用這些用戶和組的信息才能獲取到相應(yīng)的Task污朽。IdentityService提供了對(duì)Activiti 系統(tǒng)中的用戶和組的管理功能散吵。
ManagementService: ManagementService提供了對(duì)Activiti流程引擎的管理和維護(hù)功能,這些功能不在工作流驅(qū)動(dòng)的應(yīng)用程序中使用蟆肆,主要用于Activiti系統(tǒng)的日常維護(hù)矾睦。
HistoryService: HistoryService用于獲取正在運(yùn)行或已經(jīng)完成的流程實(shí)例的信息,與RuntimeService中獲取的流程信息不同炎功,歷史信息包含已經(jīng)持久化存儲(chǔ)的永久信息顷锰,并已經(jīng)被針對(duì)查詢優(yōu)化。
現(xiàn)在至少要知道有這些對(duì)象和接口亡问。并結(jié)合Activiti Api這一章節(jié)來(lái)看,你就會(huì)對(duì)部署流程肛宋、啟動(dòng)流程州藕、執(zhí)行任務(wù)等操作有一個(gè)基本的概念。
3.3. 代碼修改
1酝陈、在editor-app
目錄下找到app-cfg.js
文件床玻,將'contextRoot' : '/activiti-explorer/service',修改為本項(xiàng)目的路徑。如下所示:
/*
* Activiti Modeler component part of the Activiti project
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
'use strict';
var ACTIVITI = ACTIVITI || {};
ACTIVITI.CONFIG = {
'contextRoot' : appContextRoot+'/service',
};
//ACTIVITI.CONFIG = {
// 'contextRoot' : '/activiti-explorer/service',
//};
2沉帮、修改modeler.html
去掉Activiti Afresco的logo標(biāo)題欄锈死,并且把樣式上的空白欄去掉:
注意不要把該文本刪除,建議加style=”display:none”
,刪除后其會(huì)造成底層下的一些內(nèi)容有40個(gè)像數(shù)的東西顯示不出來(lái)穆壕。:
在load app-cfg.js
之前加上以下腳本:
<script type="text/javascript">
var pathName = window.document.location.pathname;
var appContextRoot=pathName.substring(0,pathName.substr(1).indexOf('/')+1);;
</script>
如圖所示:
3.4. 測(cè)試
在瀏覽器中輸入以下地址:
http://localhost:8080/ActivitiWorkFlowDemo(自己項(xiàng)目名)/model/create 請(qǐng)求創(chuàng)建模型接口
獲取到modelId后待牵;
再輸入http://localhost:8080/ActivitiWorkFlowDemo/modeler.html?modelId=獲取到的modelId 即可進(jìn)入modeler.html創(chuàng)建模型頁(yè)面。
結(jié)果如下:
如果報(bào):
Failed to load resource: the server responded with a status of 404 (Not Found)
首先確認(rèn)路徑?jīng)]有錯(cuò)喇勋。
在請(qǐng)求分發(fā)時(shí)缨该,沒(méi)有找到"/ActivitiWorkFlowDemo/modeler.html"的映射(No mapping found for ...)。原來(lái)是spring mvc攔截了頁(yè)面對(duì)靜態(tài)資源的請(qǐng)求川背,但你的controller中又沒(méi)有這個(gè)路徑的映射贰拿,所以頁(yè)面對(duì)靜態(tài)資源文件的請(qǐng)求并沒(méi)有正確下發(fā)蛤袒,那么該怎么解決這個(gè)問(wèn)題呢?下面我給出參考的方法:
解決方案:
1.采用<mvc:default-servlet-handler />膨更。 在spring mvc的xml配置文件上加上一句:<mvc:default-servlet-handler />妙真。如下圖所示:
<!-- 靜態(tài)資源文件,不會(huì)被Spring MVC攔截 -->
<mvc:default-servlet-handler />
<mvc:resources location="/resources/" mapping="/resources/**" />
加入之后荚守,spring mvc就會(huì)對(duì)進(jìn)入DispatcherServlet的URL進(jìn)行篩查珍德,如果發(fā)現(xiàn)是靜態(tài)資源的請(qǐng)求,就將該請(qǐng)求轉(zhuǎn)由Web應(yīng)用服務(wù)器默認(rèn)的Servlet處理健蕊,如果不是靜態(tài)資源的請(qǐng)求菱阵,才由DispatcherServlet繼續(xù)處理。這個(gè)方法是最快捷的缩功。
2.采用<mvc:resources />晴及。可以使用<mvc:resources />嫡锌,并將靜態(tài)資源放在WEB-INF目錄下(或者其他你喜歡的地方)虑稼,然后在springMVC-servlet中添加如下配置:
<mvc:resources location="/文件路徑" mapping="/映射路徑"/>
根據(jù)實(shí)際情況填寫(xiě)路徑。
3.在web.xml文件中將spring mvc的攔截路徑改為/springmvc/*("springmvc"可以替換成你喜歡的路徑)
附加知識(shí)點(diǎn):Activiti工作流的自帶數(shù)據(jù)表的含義
資源庫(kù)流程規(guī)則表
1)act_re_deployment 部署信息表
2)act_re_model 流程設(shè)計(jì)模型部署表
3)act_re_procdef 流程定義數(shù)據(jù)表運(yùn)行時(shí)數(shù)據(jù)庫(kù)表
1)act_ru_execution 運(yùn)行時(shí)流程執(zhí)行實(shí)例表
2)act_ru_identitylink 運(yùn)行時(shí)流程人員表势木,主要存儲(chǔ)任務(wù)節(jié)點(diǎn)與參與者的相關(guān)信息
3)act_ru_task 運(yùn)行時(shí)任務(wù)節(jié)點(diǎn)表
4)act_ru_variable 運(yùn)行時(shí)流程變量數(shù)據(jù)表歷史數(shù)據(jù)庫(kù)表
1)act_hi_actinst 歷史節(jié)點(diǎn)表
2)act_hi_attachment 歷史附件表
3)act_hi_comment 歷史意見(jiàn)表
4)act_hi_identitylink 歷史流程人員表
5)act_hi_detail 歷史詳情表蛛倦,提供歷史變量的查詢
6)act_hi_procinst 歷史流程實(shí)例表
7)act_hi_taskinst 歷史任務(wù)實(shí)例表
8)act_hi_varinst 歷史變量表組織機(jī)構(gòu)表
1)act_id_group 用戶組信息表
2)act_id_info 用戶擴(kuò)展信息表
3)act_id_membership 用戶與用戶組對(duì)應(yīng)信息表
4)act_id_user 用戶信息表
這四張表很常見(jiàn),基本的組織機(jī)構(gòu)管理啦桌,關(guān)于用戶認(rèn)證方面建議還是自己開(kāi)發(fā)一套溯壶,組件自帶的功能太簡(jiǎn)單,使用中有很多需求難以滿足通用數(shù)據(jù)表
1)act_ge_bytearray 二進(jìn)制數(shù)據(jù)表
2)act_ge_property 屬性數(shù)據(jù)表存儲(chǔ)整個(gè)流程引擎級(jí)別的數(shù)據(jù),初始化表結(jié)構(gòu)時(shí)甫男,會(huì)默認(rèn)插入三條記錄自定義數(shù)據(jù)表
act_cus_user_task:流程各節(jié)點(diǎn)執(zhí)行人信息表且改,啟動(dòng)流程之前初始化流程節(jié)點(diǎn)信息時(shí)插入節(jié)點(diǎn)key、節(jié)點(diǎn)名稱板驳、業(yè)務(wù)key和已知的各節(jié)點(diǎn)執(zhí)行人信息又跛。
注:代碼已上傳至GitHub:
https://github.com/ruoki/ActivitiWorkFlowDemo
歡迎star
上一篇:Spring學(xué)習(xí)之整合MyBatis
下一篇:Spring學(xué)習(xí)之整合Activiti(二)