首页
统计
关于
Search
1
Sealos3.0离线部署K8s集群
1,472 阅读
2
类的加载
1,021 阅读
3
Spring Cloud OAuth2.0
1,020 阅读
4
SpringBoot自动装配原理
879 阅读
5
All in boom 折腾笔记
719 阅读
笔记
Java
多线程
注解和反射
JVM
JUC
设计模式
Mybatis
Spring
SpringMVC
SpringBoot
MyBatis-Plus
Elastic Search
Netty
微服务
Dubbo
Zookeeper
SpringCloud
Nacos
Sentinel
数据库
MySQL
Oracle
PostgreSQL
Redis
MongoDB
工作流
Activiti7
Camunda
消息队列
RabbitMQ
前端
HTML5
CSS
CSS3
JavaScript
jQuery
Vue2
Vue3
Canvas
React
Linux
容器
Docker
Containerd
Podman
Kubernetes
Python
FastApi
OpenCV
数据分析
牛牛生活
登录
Search
标签搜索
Java
CSS
mysql
RabbitMQ
JavaScript
React
Redis
OpenCV
Netty
JVM
Mybatis-Plus
Camunda
多线程
CSS3
Python
Canvas
Spring Cloud
注解和反射
Activiti
工作流
蘇阿細
累计撰写
486
篇文章
累计收到
4
条评论
首页
栏目
笔记
Java
多线程
注解和反射
JVM
JUC
设计模式
Mybatis
Spring
SpringMVC
SpringBoot
MyBatis-Plus
Elastic Search
Netty
微服务
Dubbo
Zookeeper
SpringCloud
Nacos
Sentinel
数据库
MySQL
Oracle
PostgreSQL
Redis
MongoDB
工作流
Activiti7
Camunda
消息队列
RabbitMQ
前端
HTML5
CSS
CSS3
JavaScript
jQuery
Vue2
Vue3
Canvas
React
Linux
容器
Docker
Containerd
Podman
Kubernetes
Python
FastApi
OpenCV
数据分析
牛牛生活
页面
统计
关于
搜索到
486
篇与
的结果
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日
262 阅读
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日
172 阅读
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日
135 阅读
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半速往外抽的效果意外的不错2025-11-07 更新5700G + B550M 已出闲鱼,pve宿主机只添加直通参数和手动安装 r8125 的 dkms 驱动,其他操作都在lxc或者虚拟机里,但总是不定时 Kernel Panic,道心已碎,阿门!下一台,估计是 AMD EPYC
2022年11月16日
719 阅读
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日
151 阅读
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日
182 阅读
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日
136 阅读
0 评论
0 点赞
2022-09-25
行为型模式-备忘录模式
(1)概述备忘录模式又叫快照模式,在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便需要时能将该对象恢复到之前保存的状态(2)结构发起人角色:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录的功能,它可以访问备忘录里的所有信息备忘录角色:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人管理者角色:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录没有读写权限备忘录有两个等效接口:窄接口:管理者对象(和其他除发起人对象之外的任何对象),看到的是窄接口,该接口只允许把备忘录对象传递给其他对象宽接口:发起人对象可以看到宽接口,该接口允许读取所有的数据,以便恢复对象之前的内部状态(3)案例以游戏存档为例:“白箱”备忘录备忘录角色对任何对象都提供宽接口(破坏了封装性)发起人角色public class GameRole { /** * 生命值 */ private int vit; /** * 攻击值 */ private int atk; /** * 防御值 */ private int def; public int getVit() { return vit; } public void setVit(int vit) { this.vit = vit; } public int getAtk() { return atk; } public void setAtk(int atk) { this.atk = atk; } public int getDef() { return def; } public void setDef(int def) { this.def = def; } /** * 初始化内部状态 */ public void initState() { this.vit = 100; this.atk = 100; this.def = 100; } /** * 战斗 */ public void fight() { this.vit = 0; this.atk = 0; this.def = 0; } /** * 保存游戏角色状态 * * @return */ public RoleStateMemento saveState() { return new RoleStateMemento(vit, atk, def); } /** * 恢复角色状态 * * @param roleStateMemento */ public void recoverState(RoleStateMemento roleStateMemento) { this.vit = roleStateMemento.getVit(); this.atk = roleStateMemento.getAtk(); this.def = roleStateMemento.getDef(); } /** * 展示角色状态 */ public void displayState() { System.out.println("生命值:" + vit); System.out.println("攻击值:" + vit); System.out.println("防御值:" + vit); } }备忘录角色public class RoleStateMemento { /** * 生命值 */ private int vit; /** * 攻击值 */ private int atk; /** * 防御值 */ private int def; public RoleStateMemento() { } public RoleStateMemento(int vit, int atk, int def) { this.vit = vit; this.atk = atk; this.def = def; } public int getVit() { return vit; } public void setVit(int vit) { this.vit = vit; } public int getAtk() { return atk; } public void setAtk(int atk) { this.atk = atk; } public int getDef() { return def; } public void setDef(int def) { this.def = def; } }备忘录管理者public class RoleStateCaretaker { private RoleStateMemento roleStateMemento; public RoleStateMemento getRoleStateMemento() { return roleStateMemento; } public void setRoleStateMemento(RoleStateMemento roleStateMemento) { this.roleStateMemento = roleStateMemento; } }Clientpublic class Client { public static void main(String[] args) { System.out.println("====战斗前===="); GameRole gameRole = new GameRole(); gameRole.initState(); gameRole.displayState(); //备份角色状态 RoleStateCaretaker roleStateCaretaker = new RoleStateCaretaker(); roleStateCaretaker.setRoleStateMemento(gameRole.saveState()); System.out.println("====战斗后===="); gameRole.fight(); gameRole.displayState(); System.out.println("====恢复状态===="); gameRole.recoverState(roleStateCaretaker.getRoleStateMemento()); gameRole.displayState(); } }“黑箱”备忘录备忘录角色对发起人提供宽接口,对其他对象提供窄接口(即将备忘录类设置为发起人的内部类)发起人角色public class GameRole { /** * 生命值 */ private int vit; /** * 攻击值 */ private int atk; /** * 防御值 */ private int def; public int getVit() { return vit; } public void setVit(int vit) { this.vit = vit; } public int getAtk() { return atk; } public void setAtk(int atk) { this.atk = atk; } public int getDef() { return def; } public void setDef(int def) { this.def = def; } /** * 初始化内部状态 */ public void initState() { this.vit = 100; this.atk = 100; this.def = 100; } /** * 战斗 */ public void fight() { this.vit = 0; this.atk = 0; this.def = 0; } /** * 保存游戏角色状态 * * @return */ public Memento saveState() { return new RoleStateMemento(vit, atk, def); } /** * 恢复角色状态 * * @param memento */ public void recoverState(Memento memento) { RoleStateMemento roleStateMemento = (RoleStateMemento) memento; this.vit = roleStateMemento.getVit(); this.atk = roleStateMemento.getAtk(); this.def = roleStateMemento.getDef(); } /** * 展示角色状态 */ public void displayState() { System.out.println("生命值:" + vit); System.out.println("攻击值:" + vit); System.out.println("防御值:" + vit); } private class RoleStateMemento implements Memento { /** * 生命值 */ private int vit; /** * 攻击值 */ private int atk; /** * 防御值 */ private int def; public RoleStateMemento() { } public RoleStateMemento(int vit, int atk, int def) { this.vit = vit; this.atk = atk; this.def = def; } public int getVit() { return vit; } public void setVit(int vit) { this.vit = vit; } public int getAtk() { return atk; } public void setAtk(int atk) { this.atk = atk; } public int getDef() { return def; } public void setDef(int def) { this.def = def; } } }备忘录接口(对外提供的窄接口)public interface Memento { }备忘录管理者角色public class RoleStateCaretaker { private Memento memento; public Memento getMemento() { return memento; } public void setMemento(Memento memento) { this.memento = memento; } }Clientpublic class Client { public static void main(String[] args) { System.out.println("====战斗前===="); GameRole gameRole = new GameRole(); gameRole.initState(); gameRole.displayState(); //备份角色状态 RoleStateCaretaker roleStateCaretaker = new RoleStateCaretaker(); roleStateCaretaker.setMemento(gameRole.saveState()); System.out.println("====战斗后===="); gameRole.fight(); gameRole.displayState(); System.out.println("====恢复状态===="); gameRole.recoverState(roleStateCaretaker.getMemento()); gameRole.displayState(); } }(4)优缺点提供了一种可恢复状态的机制,当用户需要事,可方便的恢复到某个历史状态实现了内部状态的封装,除发起人之外的角色对这些状态信息都没有读写权限简化了发起人类,内部状态由管理者进行管理,发起人无需关心,复合单一职责原则当需要存储的状态信息及频率过多过大时,需要消耗更多的资源(5)使用场景需要保存与恢复数据时需要提供可回滚操作时,如 ctrl + z 、数据库回滚等
2022年09月25日
122 阅读
0 评论
0 点赞
2022-09-25
行为型模式-访问者模式
(1)概述封装一些作用于某种数据结构中的各元素的操作,它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作(2)结构抽象访问者角色:定义对每一个元素访问的行为,它的参数就是可以访问的元素(它的方法个数理论上来说与元素个数相等)具体访问者角色:给出对每一个元素类访问时所产生的具体行为抽象元素角色:定义接受访问者的方法(即每一个元素都可以被访问者访问)具体元素角色:提供接受访问方法的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法对象结构角色:一个具有容器性质或复合对象特性的类,它含有一组元素,且可以迭代这些元素,供访问者访问(3)案例以宠物喂食为例访问者角色:给宠物喂食的人具体访问者角色:宠物主人、其他人等抽象元素角色:动物抽象类具体元素角色:宠物狗、宠物猫结构对象角色:主人家抽象访问者角色public interface Person { /** * 喂猫 * * @param cat 猫 */ void feed(Cat cat); /** * 喂狗 * * @param dog 狗 */ void feed(Dog dog); }具体访问者public class Owner implements Person { @Override public void feed(Cat cat) { System.out.println("主人喂猫"); } @Override public void feed(Dog dog) { System.out.println("主人喂狗"); } } public class Someone implements Person { @Override public void feed(Cat cat) { System.out.println("客人喂猫"); } @Override public void feed(Dog dog) { System.out.println("客人喂狗"); } }抽象元素角色public interface Animal { /** * 接受访问者访问 * * @param person 访问者 */ void accept(Person person); }具体元素角色public class Dog implements Animal { @Override public void accept(Person person) { person.feed(this); System.out.println("喂狗吃狗粮"); } } public class Cat implements Animal { @Override public void accept(Person person) { person.feed(this); System.out.println("喂猫吃猫粮"); } }结构对象角色public class Home { private List<Animal> nodeList = new ArrayList<>(); void add(Animal animal) { nodeList.add(animal); } void action(Person person) { //访问者访问每一个元素 for (Animal animal : nodeList) { animal.accept(person); } } }Clientpublic class Client { public static void main(String[] args) { //创建Home Home home = new Home(); //添加元素 home.add(new Dog()); home.add(new Cat()); //创建主人对象 Owner owner = new Owner(); //主人喂宠物 home.action(owner); } }(4)优缺点扩展性好:在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能复用性好:通过访问者来定义整个对象结构的通用的功能,从而提高代码复用分离无关行为:通过访问者来分离无关的行为,把相关的行为封装在一起,构成一个访问者,使得每一个访问者的功能都比较单一对象结构变化困难:每增加一个新的元素类,都要在每一个具体访问者中增加相应的具体操作,违背了开闭原则违反了依赖倒置原则:访问者依赖具体类,而不依赖抽象类(5)使用场景对象结构稳定,但其操作算法经常变更时对象结构中对象需要提供多种不同且不相关的操作,而且要避免让这些操作的变化影响对象的结构时(6)扩展访问者模式使用了双分派技术1. 分派变量被声明时的类型叫做变量的静态类型,变量所引用的对象的真实类型叫实际类型 Map map = new HashMap()静态分派:发生在编译时期,分派根据静态类型信息发生,如:方法重载动态分派:发生在运行时期,动态分派动态地置换掉某个方法,如:方法重写2. 动态分派public class Animal { public void execute() { System.out.println("Animal"); } } public class Dog extends Animal { @Override public void execute() { System.out.println("Dog"); } } public class Cat extends Animal { @Override public void execute() { System.out.println("Cat"); } } public class Client { public static void main(String[] args) { Animal a = new Dog(); a.execute(); Animal a1 = new Cat(); a1.execute(); } }Java编译器在编译时期并不总是知道哪些代码会被执行,因为编译器仅仅知道对象的静态类型,而方法的调用则是根据对象的真实类型3. 静态分派public class Animal { } public class Dog extends Animal { } public class Cat extends Animal { } public class Execute() { public void print(Animal a) { System.out.println("Animal"); } public void print(Dog d) { System.out.println("Dog"); } public void print(Cat c) { System.out.println("Cat"); } } public class Client { public static void main(String[] args) { Animal a = new Animal(); Animal d = new Dog(); Animal c = new Cat(); Execute execute = new Execute(); execute.print(a); //Animal execute.print(d); //Animal execute.print(c); //Animal } }重载是根据方法的静态类型进行的,该分派过程在编译期完成,所以打印结果都是 Animal4. 双分派双分派在选择一个方法的时候,不仅要根据消息接收者的运行时区别,还要根据参数的运行时区别public class Animal { public void accept(Execute execute) { execute.print(this); } } public class Dog extends Animal { @Override public void accept(Execute execute) { execute.print(this); } } public class Cat extends Animal { @Override public void accept(Execute execute) { execute.print(this); } } public class Execute() { public void print(Animal a) { System.out.println("Animal"); } public void print(Dog d) { System.out.println("Dog"); } public void print(Cat c) { System.out.println("Cat"); } } public class Client { public static void main(String[] args) { Animal a = new Animal(); Animal d = new Dog(); Animal c = new Cat(); Execute execute = new Execute(); a.accept(execute); //Animal d.accept(execute); //Dog c.accept(execute); //Cat } }客户端将 Execute 作为参数传递给 Animal 类型的变量调用的方法,这里通过方法重写完成了第一次分派(动态分派),同时也将自己 this 作为参数传递到 Execute.print() 方法中,这里通过方法重载完成了第二次分派(静态分派)双分派实现动态绑定的本质就是在重载委派之前加上继承体系中的重写
2022年09月25日
145 阅读
0 评论
0 点赞
2022-09-19
行为型模式-迭代器模式
(1)概述提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露内部对象的表示(2)结构抽象聚合角色:定义存储、添加、删除聚合元素以及创建迭代器对象的接口具体聚合角色:实现抽象聚合类,返回一个具体迭代器的实例抽象迭代器角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()、next() 等方法具体迭代器角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置(3)案例以遍历学生对象集合为例抽象聚合角色public interface StudentAggregate { /** * 添加元素 * * @param student */ void add(Student student); /** * 移除元素 * * @param student */ void remove(Student student); /** * 获取迭代器 * * @return */ StudentIterator getStudentIterator(); }具体聚合角色public class StudentAggregateImpl implements StudentAggregate { private List<Student> list = new ArrayList<>(); @Override public void add(Student student) { list.add(student); } @Override public void remove(Student student) { list.remove(student); } @Override public StudentIterator getStudentIterator() { return new StudentIteratorImpl(list); } }抽象迭代器角色public interface StudentIterator { /** * 判断是否还有元素 * * @return */ boolean hasNext(); /** * 获取下一个元素 * * @return */ Student next(); }具体迭代器角色public class StudentIteratorImpl implements StudentIterator { private List<Student> list; //记录遍历时的位置 private int position = 0; public StudentIteratorImpl(List<Student> list) { this.list = list; } @Override public boolean hasNext() { return position < list.size(); } @Override public Student next() { Student currentStudent = list.get(position); position++; return currentStudent; } }Clientpublic class Client { public static void main(String[] args) { //创建聚合对象 StudentAggregateImpl studentAggregate = new StudentAggregateImpl(); //添加元素 studentAggregate.add(new Student("孙笑川", "001")); studentAggregate.add(new Student("药水哥", "002")); studentAggregate.add(new Student("刘波", "003")); //获取迭代器对象 StudentIterator iterator = studentAggregate.getStudentIterator(); //遍历 while (iterator.hasNext()) { System.out.println(iterator.next()); } } }(4)优缺点支持以不同的方式遍历一个聚合对象,在同一个聚合对象上可以定义多种遍历方式,在迭代器模式中只需要用一个不同的迭代器来替换原有的迭代器即可改变遍历算法简化了聚合类,在原有的聚合类中不需要再自行提供遍历等方法由于引入了抽象层,新增新的聚合类和迭代器类都很方便,满足开闭原则(5)使用场景当需要为聚合对象提供多种遍历方式时当需要为遍历不同的聚合结构提供一个统一的接口时当需要访问一个聚合对象的内容而无需暴露其内部细节时
2022年09月19日
147 阅读
0 评论
0 点赞
1
...
18
19
20
...
49