Activity-工作流引擎

文件目录

一. 相关概念

1. EventSource(事件源)

1. 概念

触发事件的对象,即任何具有行为的Java 对象,具有行为是为了能触发事件。

2.EventObject(事件对象)

1.概念

事件对象(Event Object)是带有 EventSource 信息的事件对象,是对EventSource的包装

2. 常用方法

Object getSource();//返回事件源

String toString();//返回该事件类名与事件源名

3.ApplicationEvent(事件驱动)

1.概念

ApplicationEvent是Spring框架中定义的一个事件类,继承EventObject,用于在应用程序中发布和监听事件。它是一个抽象类,可以被继承来创建自定义的事件。

2.ApplicationEvent包含以下几个重要的方法

1. getTimestamp():获取事件的时间戳。

2. getApplicationContext():获取事件所属的应用程序上下文。

当创建自定义事件时,需要扩展ApplicationEvent类,并提供必要的构造方法和自定义的属性。在触发事件时,可以通过传递事件源和其他相关的数据来创建事件对象,并调用ApplicationContext的publishEvent()方法来发布事件。

4. 自定义事件源

/**
 * 事件源
 *
 */
public class BpmStartModel {

	/**
	 *  流程实例
	 */
	private BpmProcessInstance bpmProcessInstance;

	/**
	 * 流程参数
	 */
	private ActionCmd cmd;


	/**
	 * 事件类型:前置/后置
	 */
	private AopType aopType=AopType.PREVIOUS;
	}

5. 自定义事件对象-封装事件源

public class BpmStartEvent extends ApplicationEvent {
    private static final long serialVersionUID = -9180179153792522401L;

    public BpmStartEvent(BpmStartModel source) {
        super(source);
    }
}

6. 自定义工具类发布事件

// 事件源
BpmStartModel startModel = new BpmStartModel(instance, baseActionCmd, AopType.PREVIOUS);
// 封装事件源到事件对象中
BpmStartEvent startEvent = new BpmStartEvent(startModel);
// 发布事件
public static void publishEvent(ApplicationEvent event){
	if(applicationContext!=null){
		applicationContext.publishEvent(event);
	}
}

7. 自定义监听器监听发布的事件

// 当事件被发布时,监听器就会收到事件,并执行相应的处理逻辑。
@Service
@Transactional
public class BpmStartEventListener implements ApplicationListener<BpmStartEvent>, Ordered {
    @Override
    public void onApplicationEvent(BpmStartEvent ev) {
    	// 获取事件源
        BpmStartModel model = (BpmStartModel) ev.getSource();
        //设置上下文。
        setBuinessKey(model);
        // 根据设置的处理器类型-前置事件-流程启动之前执行该方法
        if (AopType.PREVIOUS.equals(model.getAopType())) {
            try {
                before(model);
            } catch (Exception e) {
                throw new BaseException(e.getMessage(), e);
            }
        } else {
            try {
                // 后置事件-流程启动之后执行该方法
                after(model);
            } catch (Exception e) {
                throw new BaseException(e.getMessage(), e);
            }
        }
    }

    private void before(BpmStartModel model) throws Exception {
        // 执行前置处理器
        executeHandler(model, true);
        // 添加业务表的中间数据库表-保存业务数据,建立流程实例与业务表的关联关系
        addBusLink(model);
    }

    private void after(BpmStartModel model) throws Exception {
        // 执行后置处理器
        executeHandler(model, false);
        // 记录流程实例表单-当前实例下各个节点对应的表单
        handleInstForm(model);
    } 
}

二.BPMN 2.0-业务流程模型标记-Business Process Model And Notation

1. 概念

是由BPMI(BusinessProcess Management Initiative)开发的一套标准的业务流程建模符号,使用BPMN提供的符号可以创建业务流程。

2.组成

2.1 事件—Event

在这里插入图片描述

2.2 活动-Activity

在这里插入图片描述

2.3 网关-Gateway:用来处理决策

在这里插入图片描述

2.4 流向-Flow

在这里插入图片描述

三. Activiti监听器

3.1 概念

监听器用于监听事件-根据事件类型触发对应的监听器,从而处理各种业务.

3.2 使用场景

在使用Activiti时,其通常作为底层引擎使用,而引擎通常与业务是分开的,因此需要引擎去推动业务,即流程执行到某个阶段时,我们才去执行相对应的业务逻辑,这便依赖Activiti的监听器。常见的场景有:

1、流程节点审批人员动态分配

2、某个环节开始或结束时需要记录环节信息,或者插入一些记录数据()

3、任务开始或结束时需要做一些自定义的业务

总之监听器就是用来满足各种各样复杂的业务需求

3.3 监听器类别

按照监听的类型来划分,大致有三种:

● 执行监听器
● 任务监听器
● 事件监听器

1. 执行监听器-ExecutionListener

1.1 监听的事件类型
该监听器监听流程的开始节点、结束节点、连线,主要有三种事件类型:

● start:开始时触发-开始事件
● end: 结束时触发-结束事件
● take:主要用于监控流程线,当流程流转该线时触发-执行事件

1.2 生命周期

在这里插入图片描述

1.3 触发时机

执行监听器是一种允许你在流程实例的不同生命周期事件上执行操作的机制。执行监听器在流程实例创建、开始、结束等事件发生时可以被触发。

1.4 实现步骤
// 1.自定义抽象类 implements ExecutionListener
public abstract class AbstractExecutionListener implements ExecutionListener{

// 2.根据事件类型触发对应的监听器子类:start-StartEventListener

// 3.执行监听器业务
@Override
public void notify(DelegateExecution delegateExecution) throws Exception {
}
}
/**
 * 执行事件脚本。
 *
 * @param bpmDelegateExecution void
 * @throws Exception
 */
private void exeEventScript(BpmDelegateExecution bpmDelegateExecution) throws Exception {
}

2. 任务监听器-TaskListener

该监听器监听流程的任务节点,开始和结束节点由于在Activiti中没有任务所以无法监听,主要有四种事件类型

2.1 监听的事件类型
1. create:创建事件

2. assignment:指派事件

3. complete:完成事件

4. delete:删除事件
2.2 生命周期

在这里插入图片描述

2.3 触发时机

当流程引擎触发这四种事件类型时,对应的任务监听器会捕获其事件类型,再按照监听器的处理逻辑进行处理。

2.4 实现步骤
public abstract class AbstractTaskListener implements TaskListener{
// 2.根据事件类型触发对应的监听器子类:create-TaskCreateListener-主要处理
 人员分配-通过自定义事件-发布-事件监听-处理相应业务

@Override
public void notify(DelegateTask delegateTask) {
   }
private void exeEventScript(BpmDelegateTask delegateTask) throws Exception {
}

3. 全局事件监听器-ActivitiEventListener

它是引擎范围的事件监听器,可以捕获所有的Activiti事件。

3.1 监听的事件类型
ActivitiEventType 枚举类中包含全部事件类型
3.2 生命周期
适用于所有流程,而不仅仅是某一个流程.
3.3 触发时机
3.4 实现步骤
利用RuntimeService的addEventListener方法进行监听器的注册,在初始化流程引擎时,我们通过RuntimeService进行添加,
之后再部署和执行流程,可观察效果:

public void getFromProcessEngineConfiguration() {
    ProcessEngineConfiguration pec = ProcessEngineConfiguration
            .createProcessEngineConfigurationFromResource("activiti.cfg.xml");
    ProcessEngine pe = pec.buildProcessEngine();
    RuntimeService rs = pe.getRuntimeService();
    rs.addEventListener(new MyEventListener());
}

四. Groovy脚本

1. 概念

基于Java平台的动态编程语言,是一种面向对象的脚本语言。

2. 特性

2.1	动态脚本语言,允许在运行时动态添加、修改、删除类和方法;
2.2	与Java兼容,语法简单;

3. 原理

  1. JVM类加载器动态将Groovy代码编译成JavaClass,然后生成Java对象在JVM执行
  2. GroovyClassLoader类支持从文件、URL或字符串中加载解析Groovy类,实例化该对象,反射调用指定方法

4. 并发问题与内存泄漏问题

1.内存泄漏问题

1.1 问题描述

每次调用parseclass()方法都会生成一个对象,同一个脚本每次被调用都会生成新的对象,导致内存泄漏。

1.2 解决方案

通过static 修饰一个map对象,key为脚本,value为生成的新对象,这样同一个脚本多次被调用map只会存储一个对象

2. 并发问题

2.1 问题描述

同一段脚本被多个线程调用可能出现并发问题

2.2 解决方案

使用synchronized来锁定脚本-string.intern()方法,保证同一时刻,只能有一个线程执行该脚本

5. 适用场景

流程开始节点-设置开始事件

流程任务节点-设置前置事件/后置事件

流程结束节点-设置结束事件

6. 实现步骤

1.引入依赖

 <dependency>
    <groupId>org.codehaus.groovy</groupId>
    <artifactId>groovy-all</artifactId>
    <version>2.1.6</version>
  </dependency>

2. 创建脚本引擎配置类

@Configuration
public class ScriptEngineConfig {
  @Bean
  //	1.创建脚本引擎Bean----通过@Bean注解将GroovyScriptEngine实例声明为Spring Bean,
  // 使其可供其他组件注入和使用
  public GroovyScriptEngine getGroovyScriptEngine() {
      GroovyScriptEngine engine = new GroovyScriptEngine();
      //	2.设置绑定接口属性-使得所有实现了以下两个接口的实现类会自动被注入到Groovy脚本引擎中直接使用
      List<String> bindingInterface = new ArrayList<String>();
      bindingInterface.add("com.xx");
      bindingInterface.add("com.xxx");
      engine.setBindingInterface(bindingInterface);
      return engine;
  }
}

3. 创建脚本引擎-定义方法供流程监听器调用

public class GroovyScriptEngine implements BeanPostProcessor {
// 定义map集合用于存储脚本md5值,以及脚本生成的类
private static ConcurrentHashMap<String, Class<Script>> zlassMaps = new ConcurrentHashMap<String, Class<Script>>();


// 流程节点调用该方法,触发脚本执行自定义的节点事件
public Object executeObject(String script, Map<String, Object> vars) {
try {
// 1.获取指纹	
String key = fingerKey(script);
Class<Script> scriptShell = zlassMaps.get(key);
if (scriptShell == null) {
// 2.使用synchronized来防止多线程调用同一脚本产生的并发问题
  synchronized (key.intern()) {
      // Double Check
  	scriptShell = zlassMaps.get(key);
      if (scriptShell == null) {
      	try(GroovyClassLoader classLoader = new GroovyClassLoader()){
      		// 3.使用GroovyClassLoader来加载Script类
      		scriptShell = classLoader.parseClass(script);
      		zlassMaps.put(key, scriptShell);
      	}
      }
  }
}
this.setParameters(vars);
// 4. 实例化脚本对象
Script scriptObj = InvokerHelper.createScript(scriptShell, binding);
// 5. 执行脚本
Object rtn =  scriptObj.run();
return rtn;
}catch (Exception e) {
e.printStackTrace();
throw new BaseException("执行脚本" + script + "报错,请检查脚本");
}
}

// 为脚本代码生成md5指纹
private String fingerKey(String scriptText) {
 try {
     MessageDigest md = MessageDigest.getInstance("MD5");
     byte[] bytes = md.digest(scriptText.getBytes("utf-8"));

     final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray();
     StringBuilder ret = new StringBuilder(bytes.length * 2);
     for (int i=0; i<bytes.length; i++) {
         ret.append(HEX_DIGITS[(bytes[i] >> 4) & 0x0f]);
         ret.append(HEX_DIGITS[bytes[i] & 0x0f]);
     }
     return ret.toString();
 } catch (Exception e) {
     throw new RuntimeException(e);
 }
}
}

4. Groovy 绑定

/**
* Groovy 绑定
*/
public class GroovyBinding extends Binding implements ContextThread {
  @SuppressWarnings("unused")
  private Map<?, ?> variables;
  private static ThreadLocal<Map<String, Object>> localVars = new ThreadLocal<Map<String, Object>>();

  private static Map<String, Object> propertyMap = new HashMap<String, Object>();

  public GroovyBinding() {
  }

  public GroovyBinding(Map<String, Object> variables) {
      localVars.set(variables);
  }

  public GroovyBinding(String[] args) {
      this();
      setVariable("args", args);
  }

5. 创建脚本接口

/**
* 脚本接口
*
* <pre>
* 仅为一个标识接口,实现类会自动被注入到Groovy脚本引擎中直接使用。
* 该接口的实现类被初始化到spring容器中以后,在Groovy脚本中可直接使用实现类的实例名来编写脚本
* 在脚本中可直接使用:
* scriptImpl.getCurrentUser();
* </pre>
*/
public interface IScript {
}

7. 总结

  1. 在流程界面设置事件脚本-前置事件/后置事件-到Bpmn中
  2. 通过监听器触发脚本引擎执行.从而执行对应的业务
  3. 在事件中直接通过:自定义脚本实现.方法()来触发对应的脚本执行
ScriptImpl.getCurrentUser()来获取用户信息

五. 流程使用步骤

5.1 流程定义

  1. 使用activiti流程建模工具/流程设计器(activity-designer)定义业务流程(.bpmn文件) 。即-绘制流程图

  2. .bpmn文件就是业务流程定义文件,通过xml定义业务流程。

5.2 流程定义发布/部署

  1. activiti发布/部署业务流程定义(.bpmn文件)。
  2. 使用activiti提供的api把流程定义内容存储起来,在Activiti执行过程中可以查询定义的内容(流程定义表-存储流程定义基本信息; 流程定义数据表-存储流程bpmn数据)。

5.3 启动一个流程实例

  1. 流程实例也叫:ProcessInstance
  2. 启动一个流程实例表示开始一次业务流程的运行。
  3. 在员工请假流程定义部署完成后,如果张三要请假就可以启动一个流程实例,如果李四要请假也启动一个流程实例,两个流程的执行互相不影响。

5.4 用户查询待办任务(Task)

因为现在系统的业务流程已经交给activiti管理,通过activiti就可以查询当前流程执行到哪了,当前用户需要办理什么任务了,这些activiti帮我们管理了,而不需要开发人员自己编写在sql语句查询。

5.5 用户办理任务

用户查询待办任务后,就可以办理某个任务,如果这个任务办理完成还需要其它用户办理,比如请假单创建后由部门经理审核,这个过程也是由activiti帮我们完成了。

5.6 流程结束

当任务办理完成没有下一个任务结点了,这个流程实例就完成了。

六. 表结构介绍-共计25张表

6.1 命名规则和作用

1.Activiti 的表都以 ACT_ 开头

2.第二部分是表示表的用途的两个字母标识。用途也和服务的 API 对应。

ACT_RE :'RE’表示 repository。这个前缀的表包含了流程定义和流程静态资源 (图片,规则,等等)。

ACT_RU:'RU’表示 runtime。这些运行时的表,包含流程实例,任务,变量,异步任务等运行中的数据。Activiti 只在流程实例执行过程中保存这些数据, 在流程结束时就会删除这些记录。这样运行时表可以一直很小速度很快。

ACT_HI:'HI’表示 history。这些表包含历史数据,比如历史流程实例, 变量,任务等等。

ACT_GE :GE 表示 general。通用数据, 用于不同场景下

6.2 数据表介绍

在这里插入图片描述

七. Activiti类关系图

在这里插入图片描述

7.1 Servcie服务接口

Service是工作流引擎提供用于进行工作流部署、执行、管理的服务接口,我们使用这些接口可以操作服务对应的数据表

7.2 常见接口以及作用

在这里插入图片描述

7.3 集成步骤

1. 引入依赖-activiti

<dependency>
	<groupId>org.activiti</groupId>
	<artifactId>activiti-engine</artifactId>
	<version>5.22.0</version>
	<type>jar</type>
</dependency>
<dependency>
	<groupId>org.activiti</groupId>
	<artifactId>activiti-spring</artifactId>
	<version>${activiti.version}</version>
	<type>jar</type>
</dependency>
<dependency>
	<groupId>org.activiti</groupId>
	<artifactId>activiti-process-validation</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-image-generator</artifactId>
	<version>${activiti.version}</version>
</dependency>

2. 配置Acitviti引擎

@Configuration
public class ActivitiServiceConfig implements ApplicationContextAware {
@Bean(name = "processEngineConfiguration")
    //1.创建流程引擎配置Bean----通过@Bean注解将SpringProcessEngineConfiguration实例声明为Spring Bean,使其可供其他组件注入和使用
    public ProcessEngineConfiguration getStandaloneProcessEngineConfiguration(@Qualifier("activitiIdGenerator") ActivitiIdGenerator activitiIdGenerator,
                                                                              @Qualifier("activitiDefCache") ActivitiDefCache activitiDefCache) {
        SpringProcessEngineConfiguration configuration = new SpringProcessEngineConfiguration();
        //  设置数据源,将注入的数据源设置到SpringProcessEngineConfiguration实例中
        configuration.setDataSource(dataSource);
        //  设置事务管理器将注入的事务管理器设置到SpringProcessEngineConfiguration实例中
        configuration.setTransactionManager(transactionManager);
        configuration.setHistory("none");
        configuration.setDbIdentityUsed(false);
        //  设置数据库模式更新策略 true表示在启动时自动创建或更新Activiti引擎所需的数据库表结构
        configuration.setDatabaseSchemaUpdate("true");
        configuration.setJobExecutorActivate(false);
        configuration.setIdGenerator(activitiIdGenerator);
        configuration.setProcessDefinitionCache(activitiDefCache);
        configuration.setLabelFontName("宋体");
        configuration.setActivityFontName("宋体");
        // 使用Spring的表达式管理器来实现juel表达式的解析
        configuration.setExpressionManager(new SpringExpressionManager(context, configuration.getBeans()));
        // 达梦数据库 设置activi的数据库为oracle
        if (DbType.DM.getDb().equals(databaseContext.getDbType())) {
            configuration.setDatabaseType("oracle");
        }
        return configuration;
    }

	 @Bean(name = "processEngine")
	 // 2.创建流程引擎
	 public ProcessEngine getProsessEngien(@Qualifier("processEngineConfiguration") ProcessEngineConfiguration pro) {
	        return pro.buildProcessEngine();
	    }
	    
	@Bean
    //  3.通过流程引擎创建流程下的各种服务
    public RepositoryService getRepositoryService(@Qualifier("processEngine") ProcessEngine ProcessEngine) {
        return ProcessEngine.getRepositoryService();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值