SnakerFlow是一款类似于Activiti和BPMN的流程引擎,2014年已停更了,但并不影响使用。建议使用2.4.0版本别问我为什么,可以分别看看他们的源码就明白了。
最近公司需要使用该流程插件,所以就去研究了一下
首先在pom文件中引入Snaker的三个主要的依赖还有一个定时任务的依赖根据需求加
<dependency>
<groupId>com.github.snakerflow</groupId>
<artifactId>snaker-core</artifactId>
<version>2.4.0</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.snakerflow</groupId>
<artifactId>snaker-spring</artifactId>
<version>2.4.0</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.snakerflow</groupId>
<artifactId>snaker-mybatis</artifactId>
<version>2.4.0</version>
</dependency>
接下来就是配置Snaker自定义一个配置文件application-snakerflow.xml
配置如下
<?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:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"
default-autowire="byName" default-lazy-init="true">
<context:component-scan base-package="org.snaker.engine"/>
<bean id="engine" class="org.snaker.engine.spring.SpringSnakerEngine">
<property name="processService" ref="processService"/>
<property name="orderService" ref="orderService"/>
<property name="taskService" ref="taskService"/>
<property name="queryService" ref="queryService"/>
<property name="managerService" ref="managerService"/>
</bean>
<bean id="dbAccess" class="org.snaker.engine.access.mybatis.MybatisAccess">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<bean id="processService" class="org.snaker.engine.core.ProcessService">
<property name="access" ref="dbAccess"/>
<property name="cacheManager" ref="cacheManager"/>
</bean>
<bean id="orderService" class="org.snaker.engine.core.OrderService">
<property name="access" ref="dbAccess"/>
</bean>
<bean id="taskService" class="org.snaker.engine.core.TaskService">
<property name="access" ref="dbAccess"/>
</bean>
<bean id="managerService" class="org.snaker.engine.core.ManagerService">
<property name="access" ref="dbAccess"/>
</bean>
<bean id="queryService" class="org.snaker.engine.core.QueryService">
<property name="access" ref="dbAccess"/>
</bean>
<bean id="cacheManager" class="org.snaker.engine.cache.memory.MemoryCacheManager"/>
<bean class="org.snaker.engine.impl.LogInterceptor"/>
<bean class="org.snaker.engine.spring.SpelExpression"/>
</beans>
之后还需要在spring中进行配置,在spring配置文件中需要引入mybaits的配置文件,而该配置文件理扫描的是snaker-core里的snaker所要用的mapper文件所以需要对其进行扫描再由spring容器进行事务管理。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<package name="org.snaker.engine.entity"/>
</typeAliases>
<mappers>
<mapper resource="mapper/process.xml"/>
<mapper resource="mapper/order.xml"/>
<mapper resource="mapper/task.xml"/>
<mapper resource="mapper/task-actor.xml"/>
<mapper resource="mapper/hist-order.xml"/>
<mapper resource="mapper/hist-task.xml"/>
<mapper resource="mapper/hist-task-actor.xml"/>
<mapper resource="mapper/query.xml"/>
<mapper resource="mapper/hist-query.xml"/>
</mappers>
</configuration>
而spring中在配置sqlSessionfactory bean时需要引入mybaits配置文件,之后就是对snaker中的方法进行事务管理
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="start*" propagation="REQUIRED"/>
<tx:method name="execute*" propagation="REQUIRED"/>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="remove*" propagation="REQUIRED" />
<tx:method name="assign*" propagation="REQUIRED" />
<tx:method name="create*" propagation="REQUIRED" />
<tx:method name="complete*" propagation="REQUIRED" />
<tx:method name="finish*" propagation="REQUIRED" />
<tx:method name="terminate*" propagation="REQUIRED" />
<tx:method name="take*" propagation="REQUIRED" />
<tx:method name="deploy*" propagation="REQUIRED" />
<tx:method name="undeploy*" propagation="REQUIRED" />
<tx:method name="redeploy*" propagation="REQUIRED" />
<tx:method name="get*" propagation="REQUIRED" read-only="true" />
<tx:method name="find*" propagation="REQUIRED" read-only="true" />
<tx:method name="query*" propagation="REQUIRED" read-only="true" />
<tx:method name="search*" propagation="REQUIRED" read-only="true" />
<tx:method name="is*" propagation="REQUIRED" read-only="true" />
<tx:method name="*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* org.snaker.engine.core..*.*(..))"/>
</aop:config>
<aop:aspectj-autoproxy proxy-target-class="true" />
这样你就可以正常运行了,至于使用springboot怎么使spring文件和snaker配置文件生效,这就是另外的方面了 个人使用注解引入外部配置文件,用着也很方便,
使用的话我提供一个引擎大家可以复制下来对其进行调整,其实就是在作者的基础上进行了改进而已
/* Copyright 2013-2015 www.snakerflow.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alijk.bq.workflow.engine.impl;
import com.alijk.bq.workflow.engine.SnakerEngineFacade;
import org.apache.commons.lang.StringUtils;
import org.snaker.engine.DBAccess;
import org.snaker.engine.IProcessService;
import org.snaker.engine.SnakerEngine;
import org.snaker.engine.access.Page;
import org.snaker.engine.access.QueryFilter;
import org.snaker.engine.core.AccessService;
import org.snaker.engine.core.SnakerEngineImpl;
import org.snaker.engine.entity.*;
import org.snaker.engine.entity.Process;
import org.snaker.engine.model.TaskModel.TaskType;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @Component(把普通pojo实例化到spring容器中,相当于配置文件中的 <bean id="" class=""/>)
*
*/
@Component("snakerEngineFacade")
public class SnakerEngineFacadeImpl implements SnakerEngineFacade, InitializingBean {
@Autowired
private SnakerEngine engine;
private IProcessService process;
public SnakerEngine getEngine() {
return engine;
}
@Override
public void afterPropertiesSet() throws Exception {
process = engine.process();
}
public IProcessService getProcess() {
return process;
}
/**
* 得到所有流程定义的名称
*
* @return
*/
public List<String> getAllProcessNames() {
List<Process> list = engine.process().getProcesss(new QueryFilter());
List<String> names = new ArrayList<String>();
for (Process entity : list) {
if (names.contains(entity.getName())) {
continue;
} else {
names.add(entity.getName());
}
}
return names;
}
public Order startInstanceById(String processId, String operator, Map<String, Object> args) {
return engine.startInstanceById(processId, operator, args);
}
public Order startInstanceByName(String name, Integer version, String operator, Map<String, Object> args) {
return engine.startInstanceByName(name, version, operator, args);
}
public Order startAndExecute(String name, Integer version, String operator, Map<String, Object> args) {
Order order = engine.startInstanceByName(name, version, operator, args);
List<Task> tasks = engine.query().getActiveTasks(new QueryFilter().setOrderId(order.getId()));
List<Task> newTasks = new ArrayList<Task>();
if (tasks != null && tasks.size() > 0) {
Task task = tasks.get(0);
newTasks.addAll(engine.executeTask(task.getId(), operator, args));
}
return order;
}
public Order startAndExecute(String processId, String operator, Map<String, Object> args) {
Order order = engine.startInstanceById(processId, operator, args);
List<Task> tasks = engine.query().getActiveTasks(new QueryFilter().setOrderId(order.getId()));
List<Task> newTasks = new ArrayList<Task>();
if (tasks != null && tasks.size() > 0) {
Task task = tasks.get(0);
newTasks.addAll(engine.executeTask(task.getId(), operator, args));
}
return order;
}
public List<Task> execute(String taskId, String operator, Map<String, Object> args) {
return engine.executeTask(taskId, operator, args);
}
public List<Task> executeAndJump(String taskId, String operator, Map<String, Object> args, String nodeName) {
return engine.executeAndJumpTask(taskId, operator, args, nodeName);
}
public List<Task> transferMajor(String taskId, String operator, String... actors) {
List<Task> tasks = engine.task().createNewTask(taskId, TaskType.Major.ordinal(), actors);
engine.task().complete(taskId, operator);
return tasks;
}
public List<Task> transferAidant(String taskId, String operator, String... actors) {
List<Task> tasks = engine.task().createNewTask(taskId, TaskType.Aidant.ordinal(), actors);
engine.task().complete(taskId, operator);
return tasks;
}
public Map<String, Object> flowData(String orderId, String taskName) {
Map<String, Object> data = new HashMap<String, Object>();
if (StringUtils.isNotEmpty(orderId) && StringUtils.isNotEmpty(taskName)) {
List<HistoryTask> histTasks = engine.query()
.getHistoryTasks(
new QueryFilter().setOrderId(orderId).setName(
taskName));
List<Map<String, Object>> vars = new ArrayList<Map<String, Object>>();
for (HistoryTask hist : histTasks) {
vars.add(hist.getVariableMap());
}
data.put("vars", vars);
data.put("histTasks", histTasks);
}
return data;
}
public void addSurrogate(Surrogate entity) {
if (entity.getState() == null) {
entity.setState(1);
}
engine.manager().saveOrUpdate(entity);
}
public void deleteSurrogate(String id) {
engine.manager().deleteSurrogate(id);
}
public Surrogate getSurrogate(String id) {
return engine.manager().getSurrogate(id);
}
public List<Surrogate> searchSurrogate(Page<Surrogate> page, QueryFilter filter) {
return engine.manager().getSurrogate(page, filter);
}
}
继承的接口
public interface SnakerEngineFacade {
/**
* 得到所有流程定义的名称
*
* @return
*/
public List<String> getAllProcessNames();
public Order startInstanceById(String processId, String operator, Map<String, Object> args);
public Order startInstanceByName(String name, Integer version, String operator, Map<String, Object> args);
public Order startAndExecute(String name, Integer version, String operator, Map<String, Object> args);
public Order startAndExecute(String processId, String operator, Map<String, Object> args);
public List<Task> execute(String taskId, String operator, Map<String, Object> args);
public List<Task> executeAndJump(String taskId, String operator, Map<String, Object> args, String nodeName);
public List<Task> transferMajor(String taskId, String operator, String... actors);
public List<Task> transferAidant(String taskId, String operator, String... actors);
public Map<String, Object> flowData(String orderId, String taskName);
public void addSurrogate(Surrogate entity) ;
public void deleteSurrogate(String id);
public Surrogate getSurrogate(String id);
public List<Surrogate> searchSurrogate(Page<Surrogate> page, QueryFilter filter);
}
service层进行使用或者controller层使用
@Autowired
private SnakerEngineFacadeImpl snakerEngineFacade;
这样你就可以调用其中的接口了 其实可以根据个人需求进行修改。
总结
在最初使用snaker时在网上找的博客文档都是存在一些问题的,几乎没有和springboot整合的,好像只有一个但很简单,其实不管是springmvc、Struts还是springboot都只是一个框架,都不印象它的使用,个人在对snaker还是存在一些看法,打算在公司忙完项目之后对其进行修整,比如剔除hibernate作为持久层,以及对个别需求进行改进。到时候会跟大家分享。同时也会尊重原创作者snaker。一位了不起的大佬。