首页
统计
关于
Search
1
Sealos3.0离线部署K8s集群
1,164 阅读
2
类的加载
792 阅读
3
Spring Cloud OAuth2.0
749 阅读
4
SpringBoot自动装配原理
708 阅读
5
集合不安全问题
614 阅读
笔记
Java
多线程
注解和反射
JVM
JUC
设计模式
Mybatis
Spring
SpringMVC
SpringBoot
MyBatis-Plus
Elastic Search
微服务
Dubbo
Zookeeper
SpringCloud
Nacos
Sentinel
数据库
MySQL
Oracle
PostgreSQL
Redis
MongoDB
工作流
Activiti7
Camunda
消息队列
RabbitMQ
前端
HTML5
CSS
CSS3
JavaScript
jQuery
Vue2
Vue3
Canvas
Linux
容器
Docker
Containerd
Kubernetes
Python
FastApi
登录
Search
标签搜索
Java
CSS
mysql
RabbitMQ
JavaScript
Redis
JVM
Mybatis-Plus
Camunda
多线程
CSS3
Python
Spring Cloud
注解和反射
Activiti
工作流
SpringBoot
Mybatis
Spring
html5
蘇阿細
累计撰写
403
篇文章
累计收到
4
条评论
首页
栏目
笔记
Java
多线程
注解和反射
JVM
JUC
设计模式
Mybatis
Spring
SpringMVC
SpringBoot
MyBatis-Plus
Elastic Search
微服务
Dubbo
Zookeeper
SpringCloud
Nacos
Sentinel
数据库
MySQL
Oracle
PostgreSQL
Redis
MongoDB
工作流
Activiti7
Camunda
消息队列
RabbitMQ
前端
HTML5
CSS
CSS3
JavaScript
jQuery
Vue2
Vue3
Canvas
Linux
容器
Docker
Containerd
Kubernetes
Python
FastApi
页面
统计
关于
搜索到
403
篇与
的结果
2023-05-25
工作流 - 个人任务
1. 分配任务负责人(1)固定分配在进行业务流程设计的时候指定固定的任务负责人Properties ---> Main Config ---> Assignee(2)表达式分配在Activiti中支持使用UEL表达式,UEL表达式是Java EE6 规范的一部分, UEL(Unified Expression Language) 即统一表达式语言Activiti支持两种UEL表达式: UEL-value 和 UEL-methodUEL-value创建一个新的流程使用表达式之后的几个负责人以 ${assignee0}, ${assignee1}...的形式以此类推流程绘制完成之后,进行部署,在启动流程实例时给 UEL 表达式赋值@Test public void deploy() { //1.获取ProcessEngine对象 ProcessEngine engine = ProcessEngines.getDefaultProcessEngine(); //2.获取RepositoryService RepositoryService service = engine.getRepositoryService(); //3.部署 Deployment deploy = service.createDeployment() .addClasspathResource("bpmn/evection-uel.bpmn") .addClasspathResource("bpmn/evection-uel.png") .name("出差申请流程-UEL") .deploy(); System.out.println("id: " + deploy.getId()); System.out.println("name: " + deploy.getName()); } @Test public void createProcess() { ProcessEngine engine = ProcessEngines.getDefaultProcessEngine(); RuntimeService service = engine.getRuntimeService(); //设置assignee Map<String, Object> map = new HashMap<>(); map.put("assignee0", "zhangsan"); map.put("assignee1", "lisi"); map.put("assignee2", "wangwu"); map.put("assignee3", "xiaoming"); service.startProcessInstanceByKey("evection-uel", map); }启动成功后,可在 act_ru_variable 表中查找对应的赋值信息UEL-methodUserBean即注册到Spring容器中的Bean,表示调用该对象的 getUserId 方法UEL-method 与 UEL-value 结合如: ${userService.findManager(emp)}userService 是 Spring 容器的一个bean,findManager 是该 bean 的一个方法,emp 是 activiti 流程变量, emp作为参数传到 userService.findManager 方法中其他表达式支持解析基础类型、 bean、 list、 array 和 map,也可作为条件判断,如: ${order.price > 100 && order.price < 250}(3)分配监听器使用监听器来完成负责人的指定,在流程设计的时候就不需要指定assignee Event选项Event分类:create:任务创建后触发assignment:任务分配后触发Delete:任务完成后触发All:所有事件都触发自定义监听器package com.sw.listener; import org.activiti.engine.delegate.DelegateTask; public class TaskListener implements org.activiti.engine.delegate.TaskListener { @Override public void notify(DelegateTask delegateTask) { if ("创建请假单".equals(delegateTask.getName()) && "create".equals(delegateTask.getEventName())) { //指定任务负责人 delegateTask.setAssignee("zhangsan-listener"); } } }测试@Test public void deploy() { //1.获取ProcessEngine对象 ProcessEngine engine = ProcessEngines.getDefaultProcessEngine(); //2.获取RepositoryService RepositoryService service = engine.getRepositoryService(); //3.部署 Deployment deploy = service.createDeployment() .addClasspathResource("bpmn/evection-listener.bpmn") .addClasspathResource("bpmn/evection-listener.png") .name("出差申请流程-Listener") .deploy(); System.out.println("id: " + deploy.getId()); System.out.println("name: " + deploy.getName()); } @Test public void createProcess() { ProcessEngine engine = ProcessEngines.getDefaultProcessEngine(); RuntimeService service = engine.getRuntimeService(); service.startProcessInstanceByKey("evection-listener"); }(4)查询任务查询指定任务负责人的待办任务@Test public void queryPersonalTaskList() { //流程定义key String processDefinitionKey = "evection"; //任务负责人 String assignee = "lisi"; ProcessEngine engine = ProcessEngines.getDefaultProcessEngine(); TaskService taskService = engine.getTaskService(); List<Task> list = taskService.createTaskQuery().processDefinitionKey(processDefinitionKey).taskAssignee(assignee).list(); if (list != null && list.size() > 0) { for (Task task : list) { System.out.println("----------------------"); System.out.println("流程实例id:" + task.getProcessInstanceId()); System.out.println("任务id:" + task.getId()); System.out.println("任务负责人:" + task.getAssignee()); System.out.println("任务名称:" + task.getName()); } } }关联 businessKey在 activiti 实际应用中,查询待办任务的同时可能还要显示出和业务系统相关的数据,如:查询待审批出差任务列表需要将出差单的日期、 出差天数等信息显示出来,出差天数等信息在业务系统中存在,而并没有在 activiti 数据库中,所以无法通过 activiti 的 api 查询到出差相关的信息。 实现: 在查询待办任务时,通过 businessKey(业务标识 )关联查询业务系统的出差信息表单,从而查询出出差天数等信息@Test public void queryProcessInstance() { ProcessEngine engine = ProcessEngines.getDefaultProcessEngine(); //查询流程实例id TaskService taskService = engine.getTaskService(); Task task = taskService.createTaskQuery().processDefinitionKey("evection").taskAssignee("lisi").singleResult(); String instanceId = task.getProcessInstanceId(); //根据流程实例id查询businessKey RuntimeService runtimeService = engine.getRuntimeService(); ProcessInstance instance = runtimeService.createProcessInstanceQuery().processInstanceId(instanceId).singleResult(); System.out.println("businessKey: " + instance.getBusinessKey()); }(5)办理任务@Test public void completeTask() { //任务id String taskId = "10005"; //任务负责人 String assignee = "zhansan"; ProcessEngine engine = ProcessEngines.getDefaultProcessEngine(); TaskService taskService = engine.getTaskService(); Task task = taskService.createTaskQuery().taskId(taskId).taskAssignee(assignee).singleResult(); if (task == null) { throw new IllegalArgumentException(assignee + " 无权限处理该任务!"); } taskService.complete(taskId); System.out.println("任务:" + taskId + ",已完成"); }注:在实际应用中,完成任务前需要校验任务的负责人是否具有该任务的办理权限
2023年05月25日
58 阅读
0 评论
0 点赞
2023-05-25
工作流 - 流程实例
1. 概念流程实例(ProcessInstance)代表流程定义的执行实例一个流程实例包括了所有的运行节点,可以通过这个对象来了解当前流程实例的进度等信息,例如:用户或者程序按照流程定义的内容发起了一个流程,这就是一个流程实例2. 业务管理流程定义部署在Activiti后,我们就可以在系统中通过Activiti去管理流程的执行,但是如果要将流程实例和业务数据关联,这时需要使用到Activiti中预留的BusinessKey(业务标识)来关联@Test public void associateBusinessKey() { //1.获取ProcessEngine对象 ProcessEngine engine = ProcessEngines.getDefaultProcessEngine(); //2.RuntimeService RuntimeService service = engine.getRuntimeService(); //3.启动流程,添加businessKey ProcessInstance instance = service.startProcessInstanceByKey("evection", "10001"); System.out.println("businessKey: " + instance.getBusinessKey()); }3. 流程实例的挂起和激活在实际场景中可能由于流程变更需要将当前运行的流程暂停而不删除,流程暂停后将不能继续执行(1)全部流程挂起操作流程的定义为挂起状态,该流程定义下边所有的流程实例全部暂停。 流程定义为挂起状态时,该流程定义将不允许启动新的流程实例,同时该流程定义下的所有的流程实例都将全部挂起暂停执行@Test public void processSuspendOrActive() { //1.获取ProcessEngine对象 ProcessEngine engine = ProcessEngines.getDefaultProcessEngine(); //2.RepositoryService RepositoryService service = engine.getRepositoryService(); //3.查询流程定义的对象 ProcessDefinition definition = service.createProcessDefinitionQuery().processDefinitionKey("evection").singleResult(); //4.获取当前流程定义的状态 String id = definition.getId(); boolean suspended = definition.isSuspended(); //5.挂起 ---> 激活,否则反之 if (suspended) { service.activateProcessDefinitionById(id, true, null); System.out.println("流程:" + id + ",已激活"); } else { service.suspendProcessDefinitionById(id, true, null); System.out.println("流程:" + id + ",已挂起"); } }注:挂起流程定义后,实例对象中(数据库数据的状态)的状态会修改为 2,此时如果再去操作对应的流程实例会抛出异常,将挂起的流程转变为激活状态,则对应的状态值会从 2 更新为 1(即可以正常处理业务流程)4. 单个实例挂起针对单个流程执行挂起操作,则将指定的流程实例挂起,当前流程定义的其他流程实例不受干扰,此时如果去操作已挂起的该流程实例,同理会抛出异常@Test public void singleProcessSuspendOrActive() { //1.获取ProcessEngine对象 ProcessEngine engine = ProcessEngines.getDefaultProcessEngine(); //2.RuntimeService RuntimeService service = engine.getRuntimeService(); //3.查询流程实例对象 ProcessInstance instance = service.createProcessInstanceQuery().processInstanceId("2501").singleResult(); //4.获取相关状态 String id = instance.getId(); boolean suspended = instance.isSuspended(); //5.执行挂起/激活 if (suspended) { service.activateProcessInstanceById(id); System.out.println("流程实例:" + id + ",已激活"); } else { service.suspendProcessInstanceById(id); System.out.println("流程实例:" + id + ",已挂起"); } }
2023年05月25日
76 阅读
0 评论
0 点赞
2023-05-23
工作流 - 基本应用
1. 基本使用(1)创建Maven应用pom.xml<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.sw</groupId> <artifactId>ActivitiDemo</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <slf4j.version>1.6.6</slf4j.version> <log4j.version>1.2.12</log4j.version> <activiti.version>7.0.0.Beta1</activiti.version> <mysql.version>8.0.26</mysql.version> <mybatis.version>3.5.13</mybatis.version> <dbcp.version>1.4</dbcp.version> <junit.version>4.12</junit.version> <commons-io.version>2.6</commons-io.version> </properties> <dependencies> <!-- activiti core --> <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> <!-- bpmn 模型处理 --> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-bpmn-model</artifactId> <version>${activiti.version}</version> </dependency> <!-- bpmn 转换 --> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-bpmn-converter</artifactId> <version>${activiti.version}</version> </dependency> <!-- bpmn json转换 --> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-json-converter</artifactId> <version>${activiti.version}</version> </dependency> <!-- bpmn 布局 --> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-bpmn-layout</artifactId> <version>${activiti.version}</version> <exclusions> <exclusion> <groupId>com.github.jgraph</groupId> <artifactId>jgraphx</artifactId> </exclusion> </exclusions> </dependency> <!-- bpmn cloud --> <dependency> <groupId>org.activiti.cloud</groupId> <artifactId>activiti-cloud-services-api</artifactId> <version>${activiti.version}</version> </dependency> <!-- mysql --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <!-- mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>${mybatis.version}</version> </dependency> <!-- dbcp --> <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>${dbcp.version}</version> </dependency> <!-- junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> </dependency> <!-- log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>${log4j.version}</version> </dependency> <!-- sl4j --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>${slf4j.version}</version> </dependency> <!-- commons-io --> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>${commons-io.version}</version> </dependency> </dependencies> </project>(2)log4j# Set root category priority to INFO and its only appender to CONSOLE. #log4j.rootCategory=INFO, CONSOLE debug info warn error fatal log4j.rootCategory=debug, CONSOLE, LOGFILE # Set the enterprise logger category to FATAL and its only appender to CONSOLE. log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE # CONSOLE is set to be a ConsoleAppender using a PatternLayout. log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r[%15.15t] %-5p %30.30c %x - %m\n # LOGFILE is set to be a File appender using a PatternLayout. log4j.appender.LOGFILE=org.apache.log4j.FileAppender log4j.appender.LOGFILE.File=D:\logs\act\activiti.log log4j.appender.LOGFILE.Append=true log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r[%15.15t] %-5p %30.30c %x - %m\n(3)添加Activiti配置文件在resources下创建 activiti.cfg.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration" id="processEngineConfiguration"> <!--<property name="jdbcDriver" value="com.mysql.cj.jdbc.Driver"/>--> <!--<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/activiti?characterEncoding=utf-8&nullCatalogMeansCurrent=true&serverTimezone=UTC"/>--> <!--<property name="jdbcUsername" value="root"/>--> <!--<property name="jdbcPassword" value="123456"/>--> <property name="databaseSchemaUpdate" value="true"/> <property name="dataSource" ref="dataSource" /> </bean> <bean class="org.apache.commons.dbcp.BasicDataSource" id="dataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/activiti?characterEncoding=utf-8&nullCatalogMeansCurrent=true&serverTimezone=UTC"/> <property name="username" value="root"/> <property name="password" value="123456"/> <property name="maxActive" value="3"/> <property name="maxIdle" value="2"/> </bean> </beans>(4)单元测试自动生成表结构通过单元测试调用 Activiti API 来生成需要的表@Test public void test01() { //使用classpath下的activiti.cfg.xml中的配置来创建ProcessEngine ProcessEngine engine = ProcessEngines.getDefaultProcessEngine(); System.out.println(engine); }2. 表结构介绍(1)表的命名规则和作用ACT_RE :RE 表示 repository,这个前缀的表包含了流程定义和流程静态资源 (图片,规则等等)ACT_RU:RU 表示 runtime, 包含流程实例,任务,变量,异步任务,等运行中的 数据。Activiti 只在流程实例执行过程中保存这些数据,在流程结束时就会删除这些记录ACT_HI:HI 表示 history, 包含历史数据,比如历史流程实例, 变量,任务等等ACT_GE : GE 表示 general,通用数据,用于不同场景下(2)数据表介绍表分类表名解释一般数据ACT_GE_BYTEARRAY通用的流程定义和流程资源 ACT_GE_PROPERTY系统相关属性流程历史记录ACT_HI_ACTINST历史的流程实例 ACT_HI_ATTACHMENT历史的流程附件 ACT_HI_COMMENT历史的说明性信息 ACT_HI_DETAIL历史的流程运行中的细节信息 ACT_HI_IDENTITYLINK历史的流程运行过程中用户关系 ACT_HI_PROCINST历史的流程实例 ACT_HI_TASKINST历史的任务实例 ACT_HI_VARINST历史的流程运行中的变量信息流程定义表ACT_RE_DEPLOYMENT部署单元信息 ACT_RE_MODEL模型信息 ACT_RE_PROCDEF已部署的流程定义运行实例表ACT_RU_EVENT_SUBSCR运行时事件 ACT_RU_EXECUTION运行时流程执行实例 ACT_RU_IDENTITYLINK运行时用户关系信息,存储任务节点与参与者的相关信息 ACT_RU_JOB运行时作业 ACT_RU_TASK运行时任务 ACT_RU_VARIABLE运行时变量表3. ProcessEngine创建方式前面使用的是 getDefaultProcessEngine() 方法来加载 classpath 下的 activiti.cfg.xml 文件,还可以通过自定义的方式加载配置文件@Test public void test02() { //创建ProcessEngineConfiguration对象 ProcessEngineConfiguration configuration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml"); //创建ProcessEngine对象 ProcessEngine engine = configuration.buildProcessEngine(); System.out.println(engine); }4. Service服务接口Service是工作流引擎提供用于进行工作流部署、执行、管理的服务接口,使用这些接口就是操作服务对应的数据表(1)创建方式通过 ProcessEngine 创建 Service//RuntimeService RuntimeService runtimeService = processEngine.getRuntimeService(); //RepositoryService RepositoryService repositoryService = processEngine.getRepositoryService(); //TaskService TaskService taskService = processEngine.getTaskService();(2)Service总览名称作用RepositoryService资源管理RuntimeService流程运行管理TaskService任务管理HistoryService历史管理ManagementService引擎管理RepositoryService:是 activiti 的资源管理类,提供了管理和控制流程发布包和流程定义的操作。使用工作流建模工具设计的业务流程图需要使用此 service 将流程定义文件的内容进行部署。除了部署流程定义以外还可以:查询引擎中的发布包和流程定义暂停或激活发布包,对应全部和特定流程定义,暂停意味着它们不能再执行任何操作了,激活是对应的反向操作获得多种资源,像是包含在发布包里的文件,或引擎自动生成的流程图。获得流程定义的pojo版本,可以用来通过java解析流程,而不必通过xmlRuntimeService:获取很多关于流程执行相关的信息TaskService:获取任务的信息HistoryService:查询历史信息,执行流程时,引擎会根据配置保存很多数据,如:流程实例启动时间,任务的参与者,完成任务的时间,每个流程实例的执行路径等等ManagementService:提供了对 Activiti 流程引擎的管理和维护功能,这些功能主要用于 Activiti 系统的日常维护,而不在工作流驱动的应用程序中使用5. 流程绘制(1)绘制插件IDEA在2019版本之后没有再维护 Activiti 的流程设计工具,此处使用 Eclipse 来进行流程设计(2)绘制流程指定流程主键指定任务负责人(3)图标介绍流程符号 BPMN 2.0是业务流程建模符号2.0的缩写,它由Business Process Management Initiative这个非营利协会创建并不断发展。作为一种标识,BPMN 2.0是使用一些符号来明确业务流程设计流程图的一整套符号规范,它能增进业务建模时的沟通效率。 目前BPMN2.0是最新的版本,它用于在BPM上下文中进行布局和可视化的沟通。BPMN 2.0的基本符号主要包含:事件 Event活动 Activity网关 GateWay网关用来处理决策,常用网关有:排他网关:只有一条路径会被选择,流程执行到该网关时,按照输出流的顺序逐个计算,当条件的计算结果为 true 时,继续执行当前网关的输出流; 如果多条线路计算结果都是 true,则会执行第一个值为 true 的线路,如果所有网关计算结果没有 true,则引擎会抛出异常。 排他网关需要和条件顺序流结合使用,default 属性指定默认顺序流,当所有的条件不满足时会执行默认顺序流并行网关:所有路径会被同时选择:拆分:并行执行所有输出顺序流,为每一条顺序流创建一个并行执行线路合并:所有从并行网关拆分并执行完成的线路均在此等候,直到所有的线路都执行完成才继续向下执行包容网关:可以同时执行多条线路,也可以在网关上设置条件拆分:计算每条线路上的表达式,当表达式计算结果为 true 时,创建一个并行线路并继续执行合并:所有从包容网关拆分并执行完成的线路均在此等候,直到所有的线路都执行完成才继续向下 执行事件网关:专门为中间捕获事件设置的,允许设置多个输出流指向多个不同的中间捕获事件。当流程执行到事 件网关后,流程处于等待状态,需要等待抛出事件才能将等待状态转换为活动状态流向 Flow流是连接两个流程节点的连线,常见的流向有以下几种:Palette(画板)Connection 连接 Event事件Task 任务Gateway 网关Container 容器Boundary event 边界事件Intermediate event 中间事件6. 流程操作(1)流程部署流程部署即将流程设计器中定义的流程部署到 activiti 数据库中单文件部署@Test public void deploy() { //1.获取ProcessEngine对象 ProcessEngine engine = ProcessEngines.getDefaultProcessEngine(); //2.获取RepositoryService RepositoryService service = engine.getRepositoryService(); //3.部署 Deployment deploy = service.createDeployment() .addClasspathResource("bpmn/evection.bpmn") .addClasspathResource("bpmn/evection.png") .name("出差申请流程") .deploy(); System.out.println("id: " + deploy.getId()); System.out.println("name: " + deploy.getName()); }压缩包文件部署将bpmn和png文件打包为 .zip 文件,进行部署@Test public void test04(){ //定义zip文件的输入流 InputStream inputStream =this.getClass().getClassLoader().getResourceAsStream("bpmn/evection.zip"); //对inputStream做装饰 ZipInputStream zipInputStream = new ZipInputStream(inputStream); ProcessEngine engine = ProcessEngines.getDefaultProcessEngine(); RepositoryService repositoryService = engine.getRepositoryService(); Deployment deploy = repositoryService.createDeployment() .addZipInputStream(zipInputStream) .name("出差申请流程") .deploy(); // 4.输出流程部署的信息 System.out.println("流程部署的id:" + deploy.getId()); System.out.println("流程部署的名称:" + deploy.getName()); }流程定义部署后操作activiti中的三张表:act_re_deployment:流程定义部署表,每部署一次就增加一条记录act_re_procdef :流程定义表,部署每个新的流程定义都会在这张表中增加一条记录act_ge_bytearray :流程资源表,流程部署的bpmn文件和png图片会保存在该表中(2)启动流程实例启动一个流程表示发起一个新的出差申请单@Test public void start() { //1.获取ProcessEngine对象 ProcessEngine engine = ProcessEngines.getDefaultProcessEngine(); //2.RuntimeService RuntimeService service = engine.getRuntimeService(); //3.启动 ProcessInstance instance = service.startProcessInstanceByKey("evection"); System.out.println("流程定义id: " + instance.getProcessDefinitionId()); System.out.println("流程实例id: " + instance.getProcessInstanceId()); System.out.println("当前活动id: " + instance.getActivityId()); }启动流程实例涉及到的表:act_hi_actinst 流程实例执行历史act_hi_identitylink 流程的参与用户的历史信息act_hi_procinst 流程实例历史信息act_hi_taskinst 流程任务历史信息act_ru_execution 流程执行信息act_ru_identitylink 流程的参与用户信息act_ru_task 任务信息(3)任务查找流程启动后,对应的任务负责人就可以查询自己当前能够处理的任务,查询出来的任务都是当前用户的待办任务@Test public void queryTask() { //1.获取ProcessEngine对象 ProcessEngine engine = ProcessEngines.getDefaultProcessEngine(); //2.TaskService TaskService service = engine.getTaskService(); //3.根据流程key、任务负责人查询 List<Task> list = service.createTaskQuery().processDefinitionKey("evection").taskAssignee("zhansan").list(); for (Task task : list) { System.out.println("流程定义id: " + task.getProcessDefinitionId()); System.out.println("任务id: " + task.getId()); System.out.println("任务负责人: " + task.getAssignee()); System.out.println("任务名称: " + task.getName()); } }(4)流程任务处理任务负责人查询出来了待办的任务,选择任务进行处理,即完成任务@Test public void dealTask() { //1.获取ProcessEngine对象 ProcessEngine engine = ProcessEngines.getDefaultProcessEngine(); //2.TaskService TaskService service = engine.getTaskService(); //3.根据流程key、任务负责人查询 Task task = service.createTaskQuery().processDefinitionKey("evection").taskAssignee("孙笑川").singleResult(); //4.处理任务 service.complete(task.getId()); }孙笑川 处理完成之后,任务流转到药水哥处,接着由药水哥登录系统进行处理(5)查询流程定义查询流程相关的信息:流程的定义,流程的部署,流程定义的版本@Test public void queryProcessDefinition() { //1.获取ProcessEngine对象 ProcessEngine engine = ProcessEngines.getDefaultProcessEngine(); //2.RepositoryService RepositoryService service = engine.getRepositoryService(); List<ProcessDefinition> list = service.createProcessDefinitionQuery().processDefinitionKey("evection").orderByProcessDefinitionVersion().desc().list(); for (ProcessDefinition processDefinition : list) { System.out.println("流程定义id: " + processDefinition.getId()); System.out.println("流程定义name: " + processDefinition.getName()); System.out.println("流程定义key: " + processDefinition.getKey()); System.out.println("流程定义version: " + processDefinition.getVersion()); System.out.println("流程部署id: " + processDefinition.getDeploymentId()); } }(6)删除流程@Test public void deleteProcess() { //1.获取ProcessEngine对象 ProcessEngine engine = ProcessEngines.getDefaultProcessEngine(); //2.RepositoryService RepositoryService service = engine.getRepositoryService(); //如果该流程定义已经有启动了的流程实例,则无法删除 service.deleteDeployment("1"); //级联删除,设置为true时同时删除流程定义、运行中的流程实例 //service.deleteDeployment("1", true); }级联删除权限一般只开放给系统管理员(7)流程资源下载用户想要查看流程定义相关的信息时,可以从数据库中下载对应资源到本地(数据库直接查询blob类型数据或通过api下载资源文件,二选一即可)@Test public void readFileFromDatabase() throws IOException { //1.获取ProcessEngine对象 ProcessEngine engine = ProcessEngines.getDefaultProcessEngine(); //2.RepositoryService RepositoryService service = engine.getRepositoryService(); //3.查询器 ProcessDefinition definition = service.createProcessDefinitionQuery().processDefinitionKey("evection").singleResult(); //4.流程部署id String deploymentId = definition.getDeploymentId(); //png InputStream pngIs = service.getResourceAsStream(deploymentId, definition.getDiagramResourceName()); //bpmn InputStream bpmnIs = service.getResourceAsStream(deploymentId, definition.getResourceName()); //5.保存 File pngFile = new File("d:/evection.png"); File bpmnFile = new File("d:/evection.bpmn"); FileOutputStream pngOs = new FileOutputStream(pngFile); FileOutputStream bpmnOs = new FileOutputStream(bpmnFile); IOUtils.copy(pngIs, pngOs); IOUtils.copy(bpmnIs, bpmnOs); //6.关闭流 pngIs.close(); pngOs.close(); bpmnIs.close(); bpmnOs.close(); }(8)查看历史流程信息即使流程定义已经被删除了,流程执行的实例信息依然保存在Activiti的 act_hi_* 相关的表中@Test public void processHistoryInfo() { //1.获取ProcessEngine对象 ProcessEngine engine = ProcessEngines.getDefaultProcessEngine(); //2.HistoryService HistoryService service = engine.getHistoryService(); HistoricActivityInstanceQuery instanceQuery = service.createHistoricActivityInstanceQuery().processDefinitionId("evection:1:4").orderByHistoricActivityInstanceStartTime().desc(); List<HistoricActivityInstance> list = instanceQuery.list(); for (HistoricActivityInstance hi : list) { System.out.println(hi.getActivityId()); System.out.println(hi.getActivityName()); System.out.println(hi.getActivityType()); System.out.println(hi.getAssignee()); System.out.println(hi.getProcessDefinitionId()); System.out.println(hi.getProcessInstanceId()); System.out.println("--------------------------------"); } }
2023年05月23日
92 阅读
0 评论
0 点赞
2023-05-23
工作流简介
参考B站 波哥是个憨憨 Activiti教程Activiti是一个工作流引擎,业务系统访问(操作)activiti的接口,就可以方便的操作流程相关数据,这样就可以把工作流环境与业务系统的环境集成在一起。1. 流程定义使用activiti流程建模工具 activity-designer 定义业务流程 .bpmn 文件, .bpmn 文件就是业务流程定义文件,通过 xml 定义业务流程。2. 流程定义部署activiti部署业务流程定义 .bpmn 文件,使用 activiti 提供的api把流程定义内容存储起来,在Activiti执行过程中可以查询定义的内容。3. 流程实例流程实例(ProcessInstance):启动一个流程实例表示开始一次业务流程的运行。 在员工请假流程定义部署完成后,如果张三要请假就可以启动一个流程实例,如果李四要请假也启动一 个流程实例,两个流程的执行互相不影响。4. 待办任务待办任务(Task):因为现在系统的业务流程已经交给 activiti 管理,通过 activiti 就可以查询当前流程执行到哪了,当前用户需要办理什么任务了,这些 activiti 帮我们管理了,而不需要开发人员自己编写在sql语句查询。5. 用户办理任务用户查询待办任务后,就可以办理某个任务,如果这个任务办理完成还需要其它用户办理,比如采购单:创建后由部门经理审核,这个过程也是由 activiti 帮我们完成。6. 流程结束当任务办理完成没有下一个任务结点,表明这个流程实例完成。
2023年05月23日
64 阅读
0 评论
0 点赞
2022-11-16
All in boom 折腾笔记
2202年了还买10代u和b460的带冤种(b660带22110硬盘位的大板实在太贵了),买的全塔机箱老板分了两次寄,今天只收到了前面板2022-11-20 更新报纸机柜完成,Ubuntu 20.04 + 黑裙,后面再慢慢调整2023-04-16 更新折腾了半年,试了pve + pcie网卡,不得不感叹一句hp flr331这块网卡真是买了卖,卖了又买,不是太满意,最终停在Ubuntu22.04,花里胡哨的东西试了很多办法,最后发现用docker macvlan配合爱快端口分流还可以2023-11-26更新8月份买了块小海豚6536 3.2T AIC固态, 前几天又买了几个东西:兮克交换机,APC 390W 后备式UPS,金百达黑爵 16G x 2,加上原本的一共 16G x 4,软路由出掉了,又用回了硬路由(狗头)底层从 Ubuntu 22.04 换到了 pve 8.0.4,用了3个多月,这次坚持得比较久,去年 7.0 才用了1个月不到就又换了,哈哈2024-03-10 更新4202年,继续发扬买旧不买新的精神,J4125到了,这次是三进宫!2024-07-06 更新5月底的时候笔记本主力机烧了,联系了售后,配件停产没修好,把之前的all in boom改做了主力电脑,也把去年8月买的小海豚6536卖了6月陆陆续续的添了几样东西:美的 MR-251WTPE小天鹅 小乌梅 TG100RVIC雷鸟 鹤6Pro 75S585C Pro星粒3 15Bar手压咖啡机三星Watch5 Pro华硕 AS6604T 展示机镁光 5300 Pro 240G镁光 7300 Pro 480G2024-07-30 更新主力鸡升级了一下,12490f + b760m,还有个华硕圣旗DG1,如果只是为了av1硬解,12490f和DG1的价格好像能直接买12400/12500?2024-12-29 更新光猫更换为 中兴 7015tv3,内外网2.5G达成(外网联通千兆,给了大概20%的冗余,测速1180Mbps,1201Mbps左右)2025-05-25 更新给弱电箱加了个竹制的扩展柜(理线高手)2025-07-03 更新2025年,继续发扬买旧不买新的传统,全新的板U套装到了,干三小!等了几天,最后一个配件利民TL-B8 8025风扇也到了,小服务器暂定 PVE 8.42025-07-04 更新柜门上加了两个8010小风扇往外抽风,原本是想着如果usb 5v效果不好就从主板的pwm接口分两根线出来,结果5v半速往外抽的效果意外的不错
2022年11月16日
532 阅读
2 评论
1 点赞
2022-09-28
spring-context Test Demo
项目结构pom.xml <dependencies> <!-- <dependency>--> <!-- <groupId>org.springframework</groupId>--> <!-- <artifactId>spring-context</artifactId>--> <!-- <version>5.2.0.RELEASE</version>--> <!-- </dependency>--> <dependency> <groupId>com.spring</groupId> <artifactId>spring-demo</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>applicationContext.xml<?xml version="1.0" encoding="UTF-8"?> <beans> <bean id="userMapper" class="com.spring.mapper.impl.UserMapperImpl"> <property name="name" value="孙笑川"/> <property name="password" value="123456"/> </bean> <bean id="userService" class="com.spring.service.impl.UserServiceImpl"> <property name="userMapper" ref="userMapper"/> </bean> </beans>mapper 数据访问层UserMapperpublic interface UserMapper { /** * 添加 */ void add(); }UserMapperImplpublic class UserMapperImpl implements UserMapper { private String name; private String password; public UserMapperImpl() { System.out.println("UserMapper被创建了"); } public void setName(String name) { this.name = name; } public void setPassword(String password) { this.password = password; } @Override public void add() { System.out.println("UserMapper..." + "name: " + name + ",password: " + password); } }service 业务逻辑层UserServicepublic interface UserService { /** * 添加 */ void add(); }UserServiceImplpublic class UserServiceImpl implements UserService { private UserMapper userMapper; public UserServiceImpl() { System.out.println("UserService被创建了"); } public void setUserMapper(UserMapper userMapper) { this.userMapper = userMapper; } @Override public void add() { System.out.println("UserService..."); userMapper.add(); } }Controllerpublic class UserController { public static void main(String[] args) throws Exception { //创建spring容器 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); //BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml")); //从容器中获取userService对象 UserService userService = (UserService) applicationContext.getBean("userService"); //业务逻辑处理 userService.add(); /* UserMapper被创建了 UserService被创建了 UserService... UserMapper...name: 孙笑川,password: 123456 */ } }
2022年09月28日
78 阅读
0 评论
0 点赞
2022-09-28
自定义 spring-context Demo
applicationContext.xml<?xml version="1.0" encoding="UTF-8"?> <beans> <bean id="userMapper" class="com.spring.mapper.impl.UserMapperImpl"> <property name="name" value="孙笑川"/> <property name="password" value="123456"/> </bean> <bean id="userService" class="com.spring.service.impl.UserServiceImpl"> <property name="userMapper" ref="userMapper"/> </bean> </beans>1. pojo(1)PropertyValue类用于封装bean的属性public class PropertyValue { /** * name */ private String name; /** * ref */ private String ref; /** * value:给基本数据类型及String类型赋的值 */ private String value; public PropertyValue() { } public PropertyValue(String name, String ref, String value) { this.name = name; this.ref = ref; this.value = value; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getRef() { return ref; } public void setRef(String ref) { this.ref = ref; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } }(2)MutablePropertyValues类一个bean标签可以有多个 property 子标签,该类用于存储并管理多个 Propertyvalue 对象public class MutablePropertyValues implements Iterable<PropertyValue> { private final List<PropertyValue> propertyValueList; public MutablePropertyValues() { this.propertyValueList = new ArrayList<>(); } public MutablePropertyValues(List<PropertyValue> propertyValueList) { if (propertyValueList == null) { this.propertyValueList = new ArrayList<>(); } else { this.propertyValueList = propertyValueList; } } /** * 获取PropertyValue数组 * * @return */ public PropertyValue[] getPropertyValues() { return propertyValueList.toArray(new PropertyValue[0]); } /** * 根据名称获取PropertyValue对象 * * @param propertyName * @return */ public PropertyValue getPropertyValueByName(String propertyName) { for (PropertyValue propertyValue : propertyValueList) { if (propertyValue.getName().equals(propertyValue)) { return propertyValue; } } return null; } /** * 判断集合是否为空 * * @return */ public boolean isEmpty() { return propertyValueList.isEmpty(); } /** * 添加 * * @param propertyValue * @return */ public MutablePropertyValues addPropertyValue(PropertyValue propertyValue) { for (int i = 0; i < propertyValueList.size(); i++) { PropertyValue currentPropertyValue = this.propertyValueList.get(i); if (currentPropertyValue.getName().equals(propertyValue.getName())) { propertyValueList.set(i, new PropertyValue(propertyValue.getName(), propertyValue.getRef(), propertyValue.getValue())); return this; } } this.propertyValueList.add(propertyValue); return this; } /** * 判断是否包含指定名称的PropertyValue对象 * * @param propertyName * @return */ public boolean contains(String propertyName) { return this.getPropertyValueByName(propertyName) != null; } /** * 获取迭代器对象 * * @return */ @Override public Iterator<PropertyValue> iterator() { return propertyValueList.listIterator(); } } (3)BeanDefinitionBeanDefinition 用来封装 bean 的信息,主要包含id(bean对象的名称)、class(需交由 spring 管理的类的全路径类名)、子标签 property 数据public class BeanDefinition { private String id; private String className; private MutablePropertyValues propertyValues; public BeanDefinition() { propertyValues = new MutablePropertyValues(); } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getClassName() { return className; } public void setClassName(String className) { this.className = className; } public MutablePropertyValues getPropertyValues() { return propertyValues; } public void setPropertyValues(MutablePropertyValues propertyValues) { this.propertyValues = propertyValues; } }2. 注册表(1)BeanDefinitionRegistry接口注册 BeanDefinition 对象到注册表中从注册表中删除指定名称的对象根据名称获取指定对象根据名称判断是否包含指定对象获取已注册 bean 的个数获取已注册 bean 的名称数组 public interface BeanDefinitionRegistry { /** * 注册BeanDefinition对象到注册表中 * * @param beanName * @param beanDefinition */ void registerBeanDefinition(String beanName, BeanDefinition beanDefinition); /** * 从注册表中删除指定名称的对象 * * @param beanName */ void removeBeanDefinition(String beanName); /** * 根据名称获取指定对象 * * @param beanName * @return */ BeanDefinition getBeanDefinition(String beanName); /** * 根据名称判断是否包含指定对象 * * @param beanName * @return */ boolean containsBeanDefinition(String beanName); /** * 获取已注册bean的个数 * * @return */ int getBeanDefinitionCount(); /** * 获取已注册bean的名称数组 * * @return */ String[] getBeanDefinitionNames(); }(2)SimpleBeanDefinitionRegistry类该类实现了 BeanDefinitionRegistry 接口,并定义Map集合作为注册表容器public class SimpleBeanDefinitionRegistry implements BeanDefinitionRegistry { /** * BeanDefinition存储容器 */ private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>(); @Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) { beanDefinitionMap.put(beanName, beanDefinition); } @Override public void removeBeanDefinition(String beanName) { beanDefinitionMap.remove(beanName); } @Override public BeanDefinition getBeanDefinition(String beanName) { return beanDefinitionMap.get(beanName); } @Override public boolean containsBeanDefinition(String beanName) { return beanDefinitionMap.containsKey(beanName); } @Override public int getBeanDefinitionCount() { return beanDefinitionMap.size(); } @Override public String[] getBeanDefinitionNames() { return beanDefinitionMap.keySet().toArray(new String[0]); } }3. 解析器(1)BeanDefinitionReader接口用于解析配置文件并在注册表中注册bean的信息:获取注册表功能,让外界可以通过此对象获取注册表对象加载配置文件,并注册bean数据 public interface BeanDefinitionReader { /** * 获取注册表对象 * * @return */ BeanDefinitionRegistry getRegistry(); /** * 加载配置文件并在注册表中注册 * * @param configLocation */ void loadBeanDefinitions(String configLocation) throws DocumentException; }(2)XmlBeanDefinitionReader类用于解析xml配置文件,该类实现了 BeanDefinitionReader 接口public class XmlBeanDefinitionReader implements BeanDefinitionReader { /** * 声明注册表对象 */ private BeanDefinitionRegistry registry; public XmlBeanDefinitionReader() { registry = new SimpleBeanDefinitionRegistry(); } @Override public BeanDefinitionRegistry getRegistry() { return registry; } @Override public void loadBeanDefinitions(String configLocation) throws DocumentException { SAXReader reader = new SAXReader(); //获取类路径下的配置文件 InputStream is = XmlBeanDefinitionReader.class.getClassLoader().getResourceAsStream(configLocation); Document document = reader.read(is); //根标签 Element rootElement = document.getRootElement(); //根标签下的bean标签对象 List<Element> beanElementList = rootElement.elements(); for (Element beanElement : beanElementList) { //id String id = beanElement.attributeValue("id"); //class String className = beanElement.attributeValue("class"); MutablePropertyValues propertyValues = new MutablePropertyValues(); //property List<Element> propertyList = beanElement.elements("property"); for (Element propertyElement : propertyList) { String name = propertyElement.attributeValue("name"); String ref = propertyElement.attributeValue("ref"); String value = propertyElement.attributeValue("value"); propertyValues.addPropertyValue(new PropertyValue(name, ref, value)); } //封装 BeanDefinition beanDefinition = new BeanDefinition(); beanDefinition.setId(id); beanDefinition.setClassName(className); beanDefinition.setPropertyValues(propertyValues); //将beanDefinition注册到注册表中 registry.registerBeanDefinition(id, beanDefinition); } } }4. IOC容器(1)BeanFactory接口在该接口中定义IOC容器的统一规范(即获取 bean 对象)public interface BeanFactory { /** * 根据名称获取bean * * @param name * @return */ Object getBean(String name) throws Exception; /** * 根据名称、class类获取bean * * @param name * @param clazz * @param <T> * @return */ <T> T getBean(String name, Class<? extends T> clazz) throws Exception; }(2)ApplicationContext接口该接口的所有子实现类对 bean 对象的创建都是非延时的,所以在该接口中定义 refresh() 方法:加载配置文件根据注册表中的 BeanDefinition 对象封装的数据进行 bean 对象的创建public interface ApplicationContext extends BeanFactory { /** * 加载配置文件并创建对象 * * @throws Exception */ void refresh() throws Exception; }(3)AbstractApplicationContext类作为 ApplicationContext 接口的子类,该类也是非延时加载,所以在该类中定义Map集合作为 bean 对象的存储容器声明 BeanDefinitionReader 类型的变量,进行xml配置文件解析; BeanDefinitionReader 类型的对象的创建交由子类实现(因为只有子类明确创建 BeanDefinitionReader 哪个子实现类对象)public abstract class AbstractApplicationContext implements ApplicationContext { /** * 声明解析器 */ protected BeanDefinitionReader beanDefinitionReader; /** * 存储bean的容器 */ protected Map<String, Object> singleObjects = new HashMap<>(); /** * 配置文件路径 */ protected String configLocation; @Override public void refresh() throws Exception { //加载BeanDefinition beanDefinitionReader.loadBeanDefinitions(configLocation); //初始化bean this.finishBeanInitialization(); } /** * 初始化bean */ private void finishBeanInitialization() throws Exception { //获取注册表对象 BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry(); //获取BeanDefinition String[] beanNames = registry.getBeanDefinitionNames(); for (String beanName : beanNames) { //执行初始化 getBean(beanName); } } }注:finishBeanInitialization() 方法中的 getBean() 使用了模板方法(4)ClassPathXmlApplicationContext类该类主要功能是加载类路径下的配置文件,并创建 bean 对象:在构造方法中,创建 BeanDefinitionReader 对象在构造方法中,调用 refresh() 方法,用于加载配置文件、创建 bean 对象并存储到容器中重写父接口中的 getBean() 方法,并实现依赖注入public class ClassPathXmlApplicationContext extends AbstractApplicationContext { public ClassPathXmlApplicationContext(String configLocation) { this.configLocation = configLocation; //构建解析器 beanDefinitionReader = new XmlBeanDefinitionReader(); try { this.refresh(); } catch (Exception e) { } } @Override public Object getBean(String name) throws Exception { //判断对象容器中是否包含指定名称的容器对象,如果有则直接返回,反之进行创建 Object obj = singleObjects.get(name); if (obj != null) { return obj; } //获取BeanDefinition BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry(); BeanDefinition beanDefinition = registry.getBeanDefinition(name); //根据bean标签数据中的类名反射创建对象 Class<?> clazz = Class.forName(beanDefinition.getClassName()); Object beanObj = clazz.newInstance(); //执行依赖注入 for (PropertyValue propertyValue : beanDefinition.getPropertyValues()) { //name String propertyName = propertyValue.getName(); //value String value = propertyValue.getValue(); //ref String ref = propertyValue.getRef(); if (ref != null && !"".equals(ref)) { //获取依赖的bean对象 Object bean = getBean(ref); //拼接方法名 String methodName = StringUtils.getSetMethodNameByFieldName(propertyName); Method[] methods = clazz.getMethods(); for (Method method : methods) { if (method.getName().equals(methodName)) { method.invoke(beanObj, bean); } } } if (value != null && !"".equals(value)) { String methodName = StringUtils.getSetMethodNameByFieldName(propertyName); Method method = clazz.getMethod(methodName, String.class); method.invoke(beanObj, value); } } //在返回之前将该对象存储到bean容器中 singleObjects.put(name, beanObj); return beanObj; } @Override public <T> T getBean(String name, Class<? extends T> clazz) throws Exception { Object bean = getBean(name); if (bean == null) { return null; } return clazz.cast(bean); } }5. 补充pom.xml<!-- dom4j --> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency>项目结构:
2022年09月28日
142 阅读
0 评论
0 点赞
2022-09-25
行为型模式-解释器模式
(1)概述定义一个语言,定义它的文法表示,并定义一个解释器,根据文法规则来解释语言中的句子文法(语法)规则:用于描述语言的语法结构的形式规则# 例: expression ::= value | plus | minus # ::= 表示定义为 plus ::= expression '+' expression minus ::= expression '-' expression value ::= integer # 表达式可以是一个值,也可以是plus、minus运算, 而plus、minus又由表达式结合运算符构成,值的类型为整数抽象语法树在计算机科学中,抽象语法树(AbstractSyntaxTree,ATS)简称语法树(Syntax Tree),是源代码语法的一种抽象表示,它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。如:1 + 2 + 3 - 4(2)结构抽象表达式:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()终结符表达式:抽象表达式的子类,实现文法与终结符相关的操作,文法中的每一个终结符都有与之对应的具体终结表达式非终结符表达式:抽象表达式的子类,实现文法与非终结符相关的操作,文法中的每条规则都对应一个非终结符表达式环境角色:包含各个解释器需要的数据或公共功能,用来传递被所有解释器共享的数据,后面的解释器可以从该角色获取相应的数据客户端:将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,同时也可以通过环境角色间接访问解释器的解释方法(3)案例以加减运算为例抽象表达式public abstract class AbstractExpression { /** * 解释(解析) * * @param context 环境变量 * @return */ public abstract int interpret(Context context); }非终结符表达式public class Minus extends AbstractExpression { //减号左边的表达式 private AbstractExpression left; //减号右边的表达式 private AbstractExpression right; public Minus(AbstractExpression left, AbstractExpression right) { this.left = left; this.right = right; } @Override public int interpret(Context context) { return left.interpret(context) - right.interpret(context); } @Override public String toString() { return "(" + left.toString() + " - " + right.toString() + ")"; } } public class Plus extends AbstractExpression { //加号左边的表达式 private AbstractExpression left; //加号右边的表达式 private AbstractExpression right; public Plus(AbstractExpression left, AbstractExpression right) { this.left = left; this.right = right; } @Override public int interpret(Context context) { return left.interpret(context) + right.interpret(context); } @Override public String toString() { return "(" + left.toString() + " + " + right.toString() + ")"; } }终结符表达式public class Variable extends AbstractExpression { //变量名 private String name; public Variable(String name) { this.name = name; } @Override public int interpret(Context context) { //直接返回变量值 return context.getVariable(this); } @Override public String toString() { return name; } }环境角色public class Context { private Map<Variable, Integer> map = new HashMap<>(); /** * 添加变量 * * @param var key * @param value value */ public void assign(Variable var, Integer value) { map.put(var, value); } /** * 获取变量 * * @param var key * @return */ public int getVariable(Variable var) { return map.get(var); } }Clientpublic class Client { public static void main(String[] args) { //创建环境对象 Context context = new Context(); //创建多个变量对象 Variable a = new Variable("a"); Variable b = new Variable("b"); Variable c = new Variable("c"); Variable d = new Variable("d"); //存储变量 context.assign(a, 1); context.assign(b, 2); context.assign(c, 3); context.assign(d, 4); //获取抽象语法树 a + b - c + d AbstractExpression expression = new Plus(a, new Minus(b, new Plus(c, d))); //解释(解析) System.out.println(expression.interpret(context)); } }(4)优缺点易于改变和扩展文法:由于解释器模式中使用类来表示语言的文法规则,因此可以使用继承等机制来扩展或改变文法实现文法较为容易:在抽象语法树中,每一个表达式节点类的实现方式都是类似的,且不是特别复杂增加新的解释表达式较为方便:在需要扩展时只需增加一个对应的终结符/非终结符表达式类,符合开闭原则复杂文法难以维护:在该模式中,每一条规则至少需要定义一个类,类的个数会随着文法规则的增加而增加执行效率低:该模式使用了大量的循环和递归调用,在解释较为复杂的句子时存在性能问题(变量类型重写抽象表达式的解释(解析)方法,直接获取对应 key 的值,非终结符表达式先调用其他表达式父类的解释(解析)方法,然后才到自身这边)
2022年09月25日
71 阅读
0 评论
0 点赞
1
...
12
13
14
...
51