最新最全的Activiti开源工作流引擎面试题
一、简介
Activiti是一个开源的BPM(Business Process Management)框架,可以帮助企业实现业务流程的电子化和自动化。以下是Activiti的设计原理及架构解析:
- 架构
Activiti架构主要分为三部分:引擎、API和公共库。其中,引擎负责执行具体任务,API提供了对外的接口与应用整合,公共库则是在大量重复代码的基础上进行其他模块的扩展。
- 引擎(Engine):Activiti核心是一个基于Java语言的流程引擎,其负责管理和执行整个流程的过程,并支持将业务数据和任务流程相结合。引擎包含两个核心组件:任务引擎和执行引擎。
- API(Application Programming Interface):提供REST或Java的方式,通过封装引擎的基础功能,让外部系统可以通过API的方式来使用Activiti工作流功能。
- 公共库(Common Libraries):主要分为三个方面:工具类库、数据存储接口和其他公共功能。这些共用的库提高了开发效率并保证了Activiti程序的稳定性。
- 设计原理
Activiti从设计层面遵循以下原则:
- 可移植性(Portability):Activiti设计时候充分考虑到平台的移植性,尽可能地避免了与底层系统相关的代码。
- 扩展性(Extensibility):Activiti是一个高可扩展的框架,支持用户自定义任务节点和加入其他第三方组件。
- 易于使用(Ease-of-use):Activiti通过提供基于BPMN 2.0标准和Web设计器的流程定义、直观的流程图表达、Hibernate/JPA、Spring等框架的支持等,使得用户可以更容易使用工作流引擎。
总体来说,Activiti是一个基于流程引擎和BPMN 2.0标准的开源BPM框架。其核心设计原则是可移植性、扩展性和易用性这三个方面,Activiti引擎可以作为独立的流程引擎使用,也可以集成进各种Java应用程序中使用。以上是Activiti的设计原理及架构的概述,更详细的内容可以参考其官方文档。
二、常见面试题汇总
1. 你有使用过Activiti吗?如果有,请谈谈你的经验。
作为一种流程引擎和业务流程管理(BPM)系统,Activiti是一个非常强大的工具。我之前使用过Activiti,并在项目中实现了许多不同的应用场景。
在我的经验中,Activiti可以帮助我们快速地定义、执行和监控复杂的业务流程。通过使用Activiti提供的可视化设计器,我们可以轻松地创建流程模型,并将其部署到Activiti引擎中。
Activiti还提供了许多有用的API和工具,例如任务服务、历史记录、事件监听器等等。这些工具可以帮助我们更好地管理和监控流程实例,并使我们更容易地处理流程中的异常情况。
总的来说,我认为Activiti是一个非常优秀的BPM系统,它可以帮助我们更好地理解和管理业务流程。无论是在企业内部还是外部,Activiti都是一个非常有用的工具。
2. 请简要介绍Activiti的工作流引擎实现原理。
Activiti工作流引擎实现原理主要包括以下几个方面:
- 流程定义:Activiti使用BPMN(Business Process Model and Notation)作为流程定义的标准。BPMN是一种图形化表示业务流程的语言,它可以以流程图的形式来展示业务流程的各个环节和步骤。在Activiti中,我们可以通过可视化的方式来创建和编辑BPMN模型。
- 工作流引擎:Activiti引擎是Activiti核心的部分,它负责管理和执行业务流程。Activiti引擎通常会有一个单独的进程或服务来运行,它会监听待办任务并负责处理流程实例。在Activiti引擎中,每个流程实例都对应着一个执行过程,该执行过程由多个节点组成。
- 节点类型:Activiti支持多种不同类型的节点,例如开始节点、结束节点、用户任务、服务任务等等。每个节点都有自己的特定功能,例如开始节点用于触发流程实例,结束节点代表着流程实例的结束,而用户任务则代表着需要人工干预的步骤。
- 执行过程:在Activiti中,每个流程实例都会有一个对应的执行过程。执行过程由多个任务节点组成,每个任务节点都需要完成一定的工作才能进入下一个节点。任务节点的完成通常需要人工干预或触发相应的业务处理逻辑。
- 历史记录:在Activiti中,我们可以通过历史记录来查看流程实例的详细信息、执行过程以及每个任务节点的状态。历史记录是Activiti引擎对于流程实例的一种重要记录,它可以帮助我们跟踪流程实例的执行情况以及发现问题。
总的来说,Activiti的工作流引擎实现原理非常复杂,需要涉及到多个方面和技术。但是通过上述几个方面的了解,我们可以对Activiti的工作流引擎实现原理有一个初步的认识。
3. 你有使用过Activiti的哪些组件?请谈谈你对它们的理解。
Activiti是一个完整的BPMN 2.0实现,包含多个组件。以下是Activiti中的一些重要组件及其作用:
Activiti Modeler:可视化工具,用于创建和编辑业务流程定义。它基于Web技术,可以通过浏览器访问。
Activiti Explorer:Web应用程序,提供对已部署的流程定义、运行时数据和历史数据的访问和管理。
Activiti Engine:Activiti核心引擎,用于解析和执行BPMN 2.0流程定义。它支持多种数据库,可以嵌入到任何Java应用程序中。
Activiti REST:用于与Activiti Engine交互的RESTful API,用于查询和修改流程运行时数据。它支持JSON和XML格式。
Activiti Cycle:将Activiti集成到Eclipse开发环境中,提供了可视化建模工具和集成测试的支持。
Activiti Designer:Eclipse插件,用于创建业务流程定义和表单。
Activiti Probe:JMX控制台,用于监控和管理Activiti Engine的运行状况。
Activiti Identity:Activiti的安全框架,提供身份验证和授权功能。
总的来说,Activiti是一个非常丰富、完整的BPMN 2.0实现,具备多个组件,每个组件都有自己独特的功能和作用,并可以根据实际需求进行组合和配置。这些组件为Activiti的使用和扩展提供了很大的便利性。
4. 请说明Activiti中的任务分配方式有哪些,以及它们的区别。
Activiti中的任务分配方式常见有以下几种:
-
静态分配:将任务指派给特定的用户或组。这样做可以确保固定的人员来处理该任务,适用于一些核心人员在整个流程中要负责某个环节的情况。
-
动态分配:将任务分配给匹配某些条件的用户。比如根据用户的技能、用户在组织结构中的位置等信息来自动分配任务。这种分配方式可以提高任务处理效率,减少管理成本,但需要在系统中设置相应的规则和条件。
-
委派:将任务交由其他人员代理处理。这种方式可以在原始负责人无法处理任务时,快速且灵活地转移任务处理权限。
-
抢占式:允许多个用户同时接受任务,并在其中一个完成后撤销其他所有用户对该任务的访问。这种分配方式可以加速任务完成的速度,并便于集中资源解决紧急问题。
以上四种分配方式各有优缺点,具体使用需根据业务需求选择。
5. 请谈谈Activiti中的任务监听器和执行监听器的作用和区别。
在Activiti中,任务监听器和执行监听器是两种不同的监听器类型。
任务监听器用于在任务执行过程中对任务的生命周期进行监听和控制。任务生命周期包括创建、分配、完成等阶段,任务监听器可针对每个阶段进行监听和干预。比如,可以在任务被分配给特定用户时触发一个任务监听器,提醒用户处理任务。其作用是使开发人员能够编写自定义的Java类来监听任务的生命周期事件并在这些事件发生时执行自定义的逻辑。
执行监听器则是指在流程引擎执行某个具体操作(如节点进入、离开、取消等)时,监听这些操作并根据需要执行相应的业务逻辑。与任务监听器不同的是,执行监听器不仅能监听任务的生命周期,还能监听整个流程的执行过程。它通常被用于实现不同意、加签、抄送等复杂流程的控制。
区别:
监听的对象不同:任务监听器是对任务的生命周期进行监听,执行监听器则是对流程引擎执行具体操作时进行监听。
作用范围的不同:任务监听器只对任务相关事件进行监听,而执行监听器对整个流程执行过程进行监听。
执行逻辑不同:任务监听器通常被用于实现任务的分派、提醒、处理等,执行监听器一般用于实现复杂流程的控制和维护。
总之,在Activiti中,任务监听器和执行监听器可以帮助开发人员实现业务逻辑和流程控制。开发人员可以根据具体需求选择相应的监听器来实现自动化业务流程控制,减少业务操作中的手工填写和错误。
6. 你如何在Activiti中实现子流程?请举例说明。
在Activiti中,可以通过引入子流程来实现业务流程的重复利用和模块化设计。具体实现步骤如下:
- 创建一个子流程,在子流程中设计需要执行的任务和节点。
- 在父流程中添加一个Call Activity(调用活动)节点,并设置该节点的“Called Element”属性为子流程的ID,即子流程的BPMN文件名称(不需要包含后缀名)。
- 运行父流程时,当遇到Call Activity节点时,会跳转到子流程中执行相关任务和节点。
- 子流程执行完毕后,返回到父流程中继续执行未完成的任务。
下面是一个简单的示例:
假设有一个请假流程,其中包括申请、审批和提交三个节点。为了实现子流程,可以在审批节点中添加一个Call Activity节点,调用一个名为“审核子流程”的子流程。
子流程中包括两个任务:审核和通知。当流程执行到审批节点时,会自动跳转到子流程中执行审核任务和通知任务。等子流程执行完毕后,再回到父流程继续执行提交任务。
使用子流程可以有效地提高业务流程的可维护性和可重复利用性。因为子流程的设计和实现是与具体业务场景无关的,可以被多个业务流程所调用,降低了业务流程的复杂度和维护成本。
7. 请说明Activiti中的事件机制是如何实现的。
Activiti中的事件机制是一种钩子机制,用于在流程引擎运行过程中触发相关的事件以执行特定的操作。事件可以分为三种类型:全局事件、流程实例级别的事件和任务级别的事件。
全局事件是指整个流程引擎范围内的事件,比如流程引擎启动和关闭时的事件。全局事件可以通过注册全局监听器来实现。示例代码如下:
```
public class GlobalEventListener implements ActivitiEventListener {
@Override
public void onEvent(ActivitiEvent event) {
// 相关操作
}
@Override
public boolean isFailOnException() {
return false;
}
}
```
流程实例级别的事件是指与某个特定的流程实例相关的事件,比如流程实例创建、结束、暂停和恢复等事件。流程实例级别的事件可以通过在流程定义文件中配置事件监听器来实现。示例代码如下:
```
<bpmn:eventListeners>
<bpmn:executionListener event="start" class="com.example.MyProcessStartListener"/>
</bpmn:eventListeners>
```
任务级别的事件是指与某个特定的任务相关的事件,比如任务创建、完成、删除等事件。任务级别的事件可以通过在任务节点上配置事件监听器来实现。示例代码如下:
```
<userTask id="task1" name="Task 1">
<extensionElements>
<activiti:taskListener event="create" class="com.example.TaskCreateListener"/>
<activiti:taskListener event="complete" class="com.example.TaskCompleteListener"/>
</extensionElements>
</userTask>
```
事件机制的实现主要是通过ActivitiEventListeners类来管理全局事件、流程实例级别的事件和任务级别的事件。当有相关事件发生时,触发相应的事件监听器,并执行预定义的操作。
总之,事件机制的实现使得Activiti引擎具有更大的灵活性和可扩展性,在各个业务场景中可以灵活应用,从而提高了工作效率和系统的可维护性。
8. 你如何在Activiti中实现定时任务?请举例说明。
在Activiti中,可以通过定时器来实现调度任务。具体实现步骤如下:
在流程定义文件中设置定时器:在需要执行定时任务的节点上添加定时器,指定定时器触发的时间点或者时间段。例如,下面的代码设置了一个任务节点,在10分钟后触发该节点。
```
<userTask id="task1" name="Review">
<extensionElements>
<activiti:timerEventDefinition>
<activiti:timeDuration>P10M</activiti:timeDuration>
</activiti:timerEventDefinition>
</extensionElements>
</userTask>
```
创建定时任务监听器:创建一个自定义的定时任务监听器类,用于在定时器触发时执行相关操作。例如,下面的代码定义了一个定时任务监听器类:
```
public class MyTimerTask implements JavaDelegate {
@Override
public void execute(DelegateExecution execution) {
System.out.println("定时任务已触发");
// 执行相关操作
}
}
```
绑定定时任务监听器:将定时任务监听器绑定到任务节点上。例如,下面的代码将上一步定义的定时任务监听器绑定到之前设置的任务节点上:
```
<userTask id="task1" name="Review">
<extensionElements>
<activiti:taskListener event="complete" class="com.example.MyTimerTask"/>
</extensionElements>
</userTask>
```
启动流程引擎:启动流程引擎,并开始执行流程。等到定时器触发时,定时任务监听器类中定义的操作就会被执行。
通过以上步骤,就可以在Activiti中实现定时任务了。这种方法比较简单,同时也非常灵活,可以根据具体业务场景进行适应性调整。
9. 请谈谈Activiti中的表单引擎的作用和实现方式。
在Activiti中,表单引擎是用于设计和管理表单的一项功能。Activiti提供了一个内置的表单设计器,可以帮助用户快速地创建、管理和部署表单。表单引擎的主要作用有以下几点:
支持与业务流程的集成:在Activiti中,表单引擎可以和业务流程进行无缝集成,使得表单和流程之间的交互更加便捷。
提高工作效率:通过表单引擎,用户可以快速地创建和修改表单,使得表单的设计变得简单易行,从而提高工作效率。
丰富表单的展现形式:通过表单引擎,用户可以更加灵活地定义表单的展现形式,如表格、列表、矩阵等,满足不同场景下的需求。
表单引擎的实现方式主要基于Activiti Modeler这个可视化建模工具。用户可以使用该工具来创建和编辑包含表单的BPMN流程图。其中,表单可以分为两种类型:任务表单和开始表单。任务表单用于处理某个特定任务所需的数据,而开始表单则是在启动流程时需要填写的一些必要信息。
在表单设计过程中,用户可以根据需求选择不同的表单字段类型,并设置相应的属性。Activiti提供了多种表单字段类型,包括文本框、下拉框、日期控件、复选框等。此外,用户还可以通过JUEL表达式来动态地设置某些表单的属性,比如可见性和只读性。
在完成表单设计后,用户需要将表单部署到Activiti引擎中才能进行使用。部署方式有多种,可以将表单文件打包成war包或者jar包直接部署,也可以将表单以XML配置的形式插入到数据库中。
总之,Activiti中的表单引擎是非常重要的一个功能模块,它极大地丰富了工作流程的表单设计能力,使开发人员能够更加方便地定制和管理表单。
10. 请简要介绍Activiti在Spring Boot中的集成方式和使用方法。
在Spring Boot中集成Activiti非常简单,只需按照以下步骤进行操作即可:
在pom.xml文件中添加依赖项:在项目的pom.xml文件中添加Activiti的相关依赖,如下所示:
```
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-basic</artifactId>
<version>${activiti.version}</version>
</dependency>
```
配置数据库连接信息:根据需要,添加数据库连接池和相关配置,如下所示:
```
spring.datasource.url=jdbc:mysql://localhost:3306/activiti?serverTimezone=UTC&useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
```
创建流程定义文件:在src/main/resources目录下创建BPMN 2.0格式的流程定义文件,如下所示:
```
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd"
id="Definition"
targetNamespace="http://www.activiti.org/test">
...
</definitions>
```
添加业务逻辑代码:在Java代码中添加Activiti相关的业务逻辑,如下所示:
```
@Autowired
private RuntimeService runtimeService;
public void startProcess() {
runtimeService.startProcessInstanceByKey("myProcess");
}
```
运行程序:在项目根目录下执行`mvn spring-boot:run`命令,即可启动Spring Boot应用,并自动启动Activiti流程引擎。
通过以上5个步骤,就可以在Spring Boot中集成和使用Activiti了。值得注意的是,集成时需要按照具体的版本来进行配置和使用,以确保正常运行。此外,在使用过程中,还应注意Activiti的版本和Spring Boot的版本之间的兼容性问题。
21. 请说明Activiti中的流程定义文件是什么,它的结构和作用是什么?
在Activiti中,流程定义文件是指BPMN 2.0(Business Process Model and Notation)格式的XML文件,它描述了流程定义、节点、任务、连线等元素,以及它们之间的关系和属性。流程定义文件也被称为BPMN文件或流程图。
流程定义文件的结构包括三个主要部分:流程解析头部、流程图形定义和实体定义。具体结构如下:
流程解析头部:用于定义命名空间和引入相关xsd文件,如下所示:
```
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd"
id="Definition"
targetNamespace="http://www.activiti.org/test">
...
</definitions>
```
流程图形定义:用于定义流程图形,包括节点、任务、网关、事件、序列流等元素,如下所示:
```
<process id="myProcess" name="My process" isExecutable="true">
<startEvent id="start" name="Start"></startEvent>
<userTask id="task1" name="Task 1"></userTask>
<endEvent id="end" name="End"></endEvent>
<sequenceFlow id="flow1" sourceRef="start" targetRef="task1"></sequenceFlow>
<sequenceFlow id="flow2" sourceRef="task1" targetRef="end"></sequenceFlow>
</process>
```
实体定义:用于定义流程中使用到的Java类、监听器、表单等实体,如下所示:
```
<extensionElements>
<activiti:executionListener event="start" class="com.example.MyExecutionListener"/>
<activiti:taskListener event="create" class="com.example.MyTaskListener"/>
<activiti:formProperty id="name" name="Name" type="string" required="true"/>
<activiti:formProperty id="age" name="Age" type="integer" required="false"/>
</extensionElements>
```
流程定义文件的作用非常重要。一方面,在Activiti引擎运行时,需要根据流程定义文件来解析和生成流程执行的相关元素和逻辑;另一方面,在BPMN建模环境中,流程定义文件也是表达业务流程设计的主要载体,可以通过流程图形和实体定义等元素来描述业务流程的整体架构和细节规则。
总之,流程定义文件是Activiti中至关重要的一部分,它为业务流程奠定了坚实的基础,可以帮助企业快速地完成业务流程建模、部署和管理等工作。
22. 你如何实现Activiti中的多实例任务?
在Activiti中可以使用多种方式实现多实例任务,以下是两种常用的方法:
1. 循环多实例:这种类型的多实例任务会为指定的元素集合中每一个元素创建一个任务实例,并按照循环方式一次执行所有任务。要在BPMN图中表示循环多实例,需要在服务任务或用户任务上添加"multiInstanceLoopCharacteristics"属性;并设置"isSequential"为false表示非顺序执行,"collection"属性指向元素集合, "elementVariable"属性为循环时变量名。
例如,在下面的BPMN代码片段中,流程将为列表中的每个项依次创建一个服务任务:
```
<serviceTask id="serviceTask"
activiti:expression="${myList}"
activiti:collection="item"
activiti:isSequential="false"
activiti:elementVariable="item">
</serviceTask>
```
2. 并行多实例:这种类型的多实例任务会在同一时间内启动多个任务实例。当所有实例都完成后,流程才会继续执行。同样,要在BPMN图中表示并行多实例,需要在服务任务或用户任务上添加"multiInstanceLoopCharacteristics"属性,并将"isSequential"设置为true;其它属性与循环多实例不同。
例如,以下代码片段为用户任务创建5个并行的任务实例:
```
<userTask id="multiTask"
activiti:isSequential="false">
<multiInstanceLoopCharacteristics
activiti:parallel="true"
activiti:collection="${assigneeList}"
activiti:elementVariable="assignee">
<completionCondition>${nrOfCompletedInstances == 5}</completionCondition>
</multiInstanceLoopCharacteristics>
</userTask>
```
以上是Activiti中实现多实例任务的两种常用方式,应根据具体业务场景选择使用。
23. 请说明Activiti中的网关有哪些类型,它们的作用和区别。
Activiti中的网关是用于控制流程走向的关键元素,可以根据不同条件选择分支路径或汇聚多个分支。Activiti中的网关主要有以下几种类型:
1. 排他网关(Exclusive Gateway)
排他网关会根据指定的条件评估为true还是false,进而决定流程选择哪条路径。在BPMN图中,排他网关表示为菱形,上面带有一个“X”,如下所示:
```
+--- TRUE ---> [Task A]
Start |
+--- FALSE --> [Task B]
```
该网关只有一条入口和两条或多条出口。当流程到达排他网关时,会根据网关规则选择其中一条路径前进。
2. 并行网关(Parallel Gateway)
并行网关可以同时执行多个任务,并将所有任务的输出都聚合为一个结果。在BPMN图中,它被表示为+号,如下所示:
```
+--> [Task A] ---+
| |
Start+ +-->[Task C]---> End
| |
+--> [Task B] ---+
```
并行网关有一个入口和两个或多个出口,当流程到达并行网关时,会在多条路径之间同时进行流转。
3. 包容网关(Inclusive Gateway)
包容网关会根据条件来决定让哪些后续任务执行。在BPMN图中,它被表示为一个星形,如下所示:
```
+--> [Task A] ---+
| |
Start+ +--------> End
| |
+--> [Task B] ---+
|
v
[Task C]
```
包容网关有一个入口和两个或多个出口。当流程到达包容网关时,会根据指定的条件决定选择哪些路径前进。
4. 事件网关(Event Based Gateway)
事件网关在接收到特定事件时将决策进行分支。在BPMN图中,它被表示为一个钟表图标,如下所示:
```
Start
|
+--> [Timer] --> [Task A] --> End
|
+--> [Message] --> [Task B] --> End
```
事件网关有一个入口和两个或多个出口。当流程到达事件网关时,会根据事件类型来决定选择哪些路径前进。
5. 复杂网关(Complex Gateway)
复杂网关可以处理更加复杂的多路流转情况。在BPMN图中,它被表示为一个黑色棱形,如下所示:
```
+--- TRUE --+
| |
| Task A |
| |
+-----+-----+
|
+-----+-----+
| |
| Task B |
| |
+-----+-----+
|
+---- X >-+
| |
| Task C |
| |
+----+-----+
```
复杂网关有一个入口和两个或多个出口。进入网关时,需要执行一组规则并根据规则的结果来决定选择哪些路径前进。
以上是Activiti中常用的五种网关类型及其区别,应根据不同的业务需求来选择合适的网关类型。
24. 请谈谈Activiti中的历史机制,包括历史数据的存储和查询方式。
Activiti的历史机制用于记录流程引擎执行过程中产生的事件和数据,包括流程启动、任务完成、流程变量设置等。可以通过历史查询功能查询这些数据,以便更好地监控和优化流程的执行情况。
Activiti中的历史数据存储在数据库中,表名为"ACT_HI_"开头,包括以下几个表:
1. ACT_HI_PROCINST:该表存储流程实例的所有历史信息,包括流程定义ID、流程实例ID、流程开始时间、结束时间、业务关键字等。
2. ACT_HI_TASKINST:该表存储任务实例的所有历史信息,包括任务ID、任务名称、任务开始时间、结束时间、处理人、任务状态等。
3. ACT_HI_ACTINST:该表存储流程中所有活动节点的历史信息,包括活动节点ID、类型、开始时间、结束时间、持续时间等。
4. ACT_HI_VARINST:该表存储流程变量的历史信息,包括变量名称、变量类型、变量值等。
5. ACT_HI_COMMENT:该表存储所有评论相关的历史数据,包括任务评论和流程实例评论等。
Activiti提供了一系列API用于查询历史数据,例如HistoricProcessInstanceQuery、HistoricTaskInstanceQuery等,也可以使用历史管理服务HistoryService进行查询。常见的历史查询方式有以下几种:
1. 查询流程实例历史数据:通过HistoricProcessInstanceQuery查询流程实例的历史信息,包括实例ID、开始时间、结束时间、处理人等。
2. 查询任务历史数据:通过HistoricTaskInstanceQuery查询任务实例的历史信息,包括任务ID、名称、所属流程实例、处理人等。
3. 查询活动历史数据:通过HistoricActivityInstanceQuery查询流程中所有活动节点的历史信息,包括活动节点名称、类型、持续时间等。
4. 查询变量历史数据:通过HistoricVariableInstanceQuery查询流程变量的历史信息,包括变量名称、值、所属流程实例等。
以上是Activiti中的历史机制和常见的历史数据存储方式和查询方式。借助历史机制,可以对流程执行过程进行监控和分析,帮助优化流程效率和质量。
25. 你如何在Activiti中实现任务的优先级和超时处理?
在Activiti中实现任务的优先级和超时处理可以通过以下两种方式:
1. 任务优先级
在Activiti中对于任务的优先级并没有直接的支持,但是可以通过设置任务的“dueDate”属性来模拟实现任务的优先级。通过设置不同的截止时间,“dueDate”较早的任务会先被执行。
例如,在BPMN图中,可以使用表达式`${now()}`来动态设置任务的截止时间,如下所示:
```
<userTask id="task"
activiti:assignee="${user}"
activiti:dueDate="${now()}">
</userTask>
```
2. 超时处理
Activiti提供了监听器和定时任务服务来处理任务的超时事件。可以通过添加过期时间或超时监听器来启动这些机制。
例如,在BPMN中可以为用户任务添加如下代码片段,表示在超时事件发生时触发自定义监听器:
```
<userTask id="task" activiti:candidateUsers="kermit"
activiti:formKey="approveLoanForm"
activiti:taskListeners="timeoutListener">
<extensionElements>
<activiti:executionListener event="start">
<activiti:field name="duedate">
<activiti:expression>${dateCalculator.calculate()}</activiti:expression>
</activiti:field>
</activiti:executionListener>
</extensionElements>
</userTask>
```
当任务“task”的超时时间到达时,扩展元素中定义的监听器“timeoutListener”将被触发。
另外,当任务超时后还需要执行一些操作时,可以使用Activiti提供的定时任务服务。例如,在BPMN中添加一个定时器,并设置其duration为PT2H(两小时),如下所示:
```
<userTask id="task" activiti:candidateGroups="accounting">
<extensionElements>
<activiti:timerEventDefinition>
<activiti:timeDuration>PT2H</activiti:timeDuration>
</activiti:timerEventDefinition>
</extensionElements>
</userTask>
```
这将在任务创建两小时后自动触发定时器事件并执行相应的操作。
以上是Activiti中实现任务优先级和超时处理的两种方式,应根据实际业务需求选择合适的方法。
26. 请说明Activiti中的事务管理机制是什么,如何保证数据的一致性和可靠性?
在Activiti中,事务管理机制是用来保证流程引擎操作的原子性、一致性、隔离性和持久性的。Activiti使用JDBC事务管理器来控制数据库事务,确保在流程执行期间发生异常时能够回滚已完成的流程处理并防止数据错误。
Activiti使用Spring框架提供的事务抽象来管理事务。具体来说,它包括一个PlatformTransactionManager接口和对应的实现类(如DataSourceTransactionManager),用于开启、提交和回滚事务。默认地,Activiti使用Spring提供的DataSourceTransactionManager作为其事务管理器。
Activiti在处理流程时,会根据事务管理机制实现以下功能:
1. 保证原子性:流程引擎的每个操作都被视为一个事务,并且必须全部成功或全部失败。如果其中任何一个操作失败,整个事务将被回滚到前一个稳定状态,以确保数据不会出现混乱。
2. 保证一致性:Activiti的流程引擎通过执行事务来保证数据库更新的一致性。在流程执行过程中所有相关表的修改操作都在同一个事务中进行,因此在某些情况下可能需要在多个事务之间进行合并。
3. 保证隔离性:Activiti使用默认的事务隔离级别REPEATABLE READ,以确保在多个并发流程中不会出现数据混乱的情况。
4. 保证持久性:Activiti使用Hibernate框架来管理数据库访问,所有数据操作都被存储到数据库中,以便在系统重启后恢复状态。
为了保证Activiti中的事务管理机制能够正常工作,需要遵循一些最佳实践,例如,使用Spring框架管理事务、尽量将所有的业务操作放在单独的流程或任务中来减少事务范围等。此外,还可以通过配置流程引擎的失败处理程序来设置异常处理策略,以便发生异常时进行及时处理并保证数据的一致性和可靠性。
综上所述,Activiti中的事务管理机制通过使用JDBC事务管理器确保流程执行期间操作的原子性、一致性、隔离性和持久性,从而保证数据的一致性和可靠性。
27. 请谈谈Activiti中的并发执行机制,包括并发任务和多实例任务的执行顺序和并发控制方式。
Activiti中的并发执行机制支持两种类型的任务:并发任务和多实例任务。在这两种任务中,都允许多个流程实例或任务同时执行。以下是Activiti中的并发执行机制的详细说明:
1. 并发任务
并发任务允许多个任务实例同时执行,每个任务实例都有自己的独立变量。并发任务中的任务实例可以按照任意顺序运行,也可以使用任务监听器来控制执行顺序。
例如,下面的BPMN图中定义了一个并发任务“task”,分别分配给kermit和fozzie。当流程引擎执行此任务时,它会创建两个独立的任务,并在kermit和fozzie之间切换,直到所有任务都完成为止。
```
<parallelGateway id="fork" />
<userTask id="task" name="Review 2">
<extensionElements>
<activiti:candidateUsers>
<activiti:candidateUser>kermit</activiti:candidateUser>
<activiti:candidateUser>fozzie</activiti:candidateUser>
</activiti:candidateUsers>
</extensionElements>
</userTask>
<parallelGateway id="join" />
<sequenceFlow sourceRef="fork" targetRef="task" />
<sequenceFlow sourceRef="task" targetRef="join" />
```
2. 多实例任务
多实例任务允许通过迭代控制多个流程实例或任务。多实例任务中包含一个主体任务,该任务被重复运行多次。
例如,下面的BPMN图中定义了一个多实例任务“task”,它被分配给kermit和fozzie。当流程引擎执行此任务时,它会创建两个独立的任务实例,并按照顺序执行每个实例,直到所有实例都完成为止。
```
<userTask id="task" name="Review 2">
<multiInstanceLoopCharacteristics isSequential="true"
activiti:collection="${assignees}" activiti:elementVariable="reviewer">
<completionCondition>${nrOfCompletedInstances/nrOfInstances >= 0.6 }</completionCondition>
</multiInstanceLoopCharacteristics>
<extensionElements>
<activiti:assignment>
<activiti:expression>${reviewer}</activiti:expression>
</activiti:assignment>
</extensionElements>
</userTask>
```
在多实例任务中,可以使用“isSequential”属性来指定迭代方式是按顺序还是并发进行的。
除了上述迭代方式之外,Activiti还提供了一些其他的并发控制机制,例如并发网关(Parallel Gateway)和互斥网关(Exclusive Gateway),可以根据需要选择适合的并发控制方式。
综上所述,Activiti中的并发执行机制通过支持并发任务和多实例任务,以及提供不同的并发控制机制来满足不同的业务需求。使用这些机制可以有效地管理流程中的并发性和控制执行顺序。
28. 请说明Activiti中的错误处理机制,包括业务错误和系统错误的处理方式。
在Activiti中,错误处理机制可分为两种类型:业务错误和系统错误。业务错误通常是由于流程执行过程中的业务逻辑或数据导致的,而系统错误则通常是由于系统内部错误或外部环境问题导致的。Activiti提供了多种方式来处理这两种类型的错误。
1. 业务错误处理
业务错误通常指与流程相关的业务逻辑或数据验证错误。Activiti支持使用异常事件和边界捕获事件来处理业务错误。异常事件可以在BPMN图中定义预期的异常情况,并通过事件监听器来捕获异常并做相应的处理。例如:
```
<!-- 定义异常事件 -->
<boundaryEvent id="errorEvent" attachedToRef="task" >
<errorEventDefinition errorRef="inputError"/>
</boundaryEvent>
<!-- 在流程中添加事件监听器 -->
<serviceTask id="handleErrorTask" activiti:expression="${errorHandlerService}" >
<extensionElements>
<activiti:eventListener event="ACTIVITY-ERROR">
<activiti:expression>${errorHandlerService}</activiti:expression>
</activiti:eventListener>
</extensionElements>
</serviceTask>
```
当输入的数据格式错误时,将触发“inputError”异常事件,并执行与之关联的边界捕获事件,在此示例中为“errorEvent”。在此时可能会调用“handleErrorTask”中定义的服务来处理异常情况。实际上,我们可以根据具体业务需要使用不同的方式来处理这些异常。
2. 系统错误处理
系统错误通常指由于系统内部错误或外部环境问题导致的错误。Activiti可以使用异常事件和Java异常捕获机制来处理系统错误。例如,可以在流程中定义一个异常事件用来捕获未知的异常情况:
```
<boundaryEvent id="errorBoundary" attachedToRef="task" >
<timerEventDefinition>
<timeDuration>PT10S</timeDuration>
</timerEventDefinition>
<extensionElements>
<activiti:field name="errorCode">
<activiti:expression>${exceptionHandler.resolveErrorCode()}</activiti:expression>
</activiti:field>
<activiti:eventListener event="ACTIVITY-FAILED">
<activiti:expression>${exceptionHandler.handleException(execution)}</activiti:expression>
</activiti:eventListener>
</extensionElements>
<errorEventDefinition errorRef="unknownError"/>
</boundaryEvent>
```
在此示例中,我们为“task”任务添加了一个边界捕获事件,“errorBoundary”,并设置时间间隔为10秒钟来捕获未知异常。当发生未知异常时,会触发“unknownError”异常事件,并执行与之相关联的边界捕获事件。同时,可以调用“exceptionHandler”服务中的方法来处理异常情况。
综上所述,Activiti提供了多种处理业务错误和系统错误的方法。开发人员应根据具体需求选择合适的错误处理机制,在流程执行过程中做好错误处理工作,以提高流程执行的可靠性和稳定性。
29. 你如何在Activiti中实现多租户架构?
多租户架构是指在一个软件系统中,多个用户或租户可以共享一个系统实例,每个用户或租户都有自己独立的数据和配置。在Activiti中,实现多租户架构需要考虑以下几个方面:
1. 身份验证和授权
在多租户架构中,对不同的用户或租户进行身份验证和授权是必不可少的。Activiti支持使用Spring Security等第三方安全框架来实现身份验证和授权,同时还可以使用Activiti提供的基于GraphQL的API,根据用户或租户进行访问控制。
2. 数据隔离
在多租户架构中,为了确保不同的用户或租户之间的数据彼此独立,需要进行数据隔离。Activiti可以通过创建单独的数据库实例或使用不同的表前缀等方式,为不同的用户或租户提供独立的数据空间。
3. 流程定义管理
由于多个用户或租户可能会使用相同的流程定义,因此需要在流程定义管理方面实现多租户支持。Activiti可以通过将流程定义与部署相关联,并为每个租户创建独立的流程定义实例,以实现多租户管理。
4. 任务分配
在多租户架构中,不同的用户或租户可能会被分配到相同的任务。因此,需要考虑如何为不同的用户或租户分配任务,并保障任务执行过程中的数据独立性。Activiti可以通过为每个租户创建单独的任务实例和变量来解决这个问题。
最佳实践:在设计多租户架构时,需要根据具体业务需求结合上述方面进行实现,并进行充分的测试和验证。例如,在数据隔离方面,需要使用合适的数据库实例隔离策略来确保数据不会混淆。同时,在流程定义管理和任务分配方面,需要考虑多租户下的并发控制和错误处理机制。
综上所述,Activiti可以通过身份验证和授权、数据隔离、流程定义管理和任务分配等方式来实现多租户支持。在实现多租户架构时,需要充分考虑各种因素,确保系统能够更好地满足不同用户或租户的需求。
30. 请谈谈Activiti在企业级应用中的使用场景和最佳实践。
Activiti是一个基于Java平台的流程引擎,可以使用它来实现企业级应用中的各种流程自动化需求。下面是Activiti在企业级应用中的常见使用场景和最佳实践:
1. 工作流管理
Activiti被广泛应用于工作流管理系统中,例如流程审批、报销申请、人事招聘等。使用Activiti可以将复杂的工作流程图形化地呈现给用户,并提供高度定制化的流程、任务和表单等元素,以易于使用和操作。
最佳实践:根据具体业务需求,合理设计流程、任务和表单,优化并发控制机制,提高流程执行效率和可靠性。
2. 业务流程分析和优化
使用Activiti可以对企业内部的业务流程进行分析和优化。通过将业务流程转换为BPMN格式的流程定义,可以直观地展示业务流程的瓶颈、缺陷和改进空间,从而帮助企业更好地优化业务流程,提高效率和效益。
最佳实践:结合BPMN规范,在流程定义文件中详细描述业务流程,使用Activiti Modeler或其他工具可视化流程,然后通过流程监控和数据分析工具进行业务流程的分析和优化。
3. 任务调度和定时任务
使用Activiti可以设置定时任务,例如通过配置任务边界事件或使用Job Executor来触发任务。企业应用中对于定期处理任务的场景非常常见,比如数据备份、报表生成和邮件发送等。
最佳实践:结合Quartz框架等第三方调度器实现复杂的定时任务调度,同时根据业务需求合理设置任务的执行时间和频率。
4. 与其他系统集成
Activiti提供了多种方式与其他系统进行集成,例如通过Java API、REST API或消息队列等方式,实现流程引擎与其他系统之间的数据交互和业务逻辑的协同处理。
最佳实践:使用REST API或消息队列等方式将Activiti与其他系统无缝集成,并在业务逻辑处理过程中根据业务需要做好异常处理和数据保护工作。
5. 流程监控和统计分析
使用Activiti可以实现流程运行状态的实时监控和统计分析,了解流程的执行情况,及时发现问题并做出相应的调整和优化。
最佳实践:基于Activiti提供的流程管理控制台,实现对流程执行情况的实时监控和统计分析,以帮助企业更好地了解业务流程的执行情况,优化业务流程并提高效率。
综上所述,Activiti被广泛应用于企业级应用的各个方面,可以在工作流管理、业务流程分析和优化、任务调度和定时任务、与其他系统集成、流程监控和统计分析等方面提供强大的支持。使用Activiti需要根据具体业务需求选择合适的功能和组件,并结合最佳实践进行集成和开发,以实现高效自动化流程管理。
31. 请谈谈Activiti中的流程实例和执行实例的区别和联系。
Activiti是一个基于Java的开源工作流引擎,其主要功能是支持业务流程管理(BPM)。在Activiti中,流程定义、流程实例和执行实例是三个重要的概念。
流程实例(Process Instance)是指从一个流程定义中创建的一个执行实体,它代表了一个正在运行的流程。流程实例可以看作是一条流程的具体实现,其状态会随着业务流程的运转而不断变化。
执行实例(Execution)是指流程的一次具体执行过程,也可以理解为一个流程实例中的某个具体步骤。
流程实例与执行实例之间的联系可以通过以下几点来区分:
1. 流程实例是一个全局唯一的标识符,用于跟踪整个流程的生命周期,而执行实例则是针对一个流程节点的执行操作。
2. 流程实例是流程的最高层级,包含了所有的执行实例,也就是说一个流程实例可以包含多个执行实例。
3. 执行实例是流程实例中的一个子集,通过执行操作推进流程实例的状态。多个执行实例可以在同一时刻并行执行,也可以顺序执行。
4. 流程实例与执行实例之间的关系是一对多的,即一个流程实例对应多个执行实例。
因此,在Activiti中,流程实例和执行实例是密切相关的,通过对它们的处理来保证业务流程执行的正确性和完整性。
32. 你如何在Activiti中实现动态流程变量的设置和获取?
在Activiti中,可以通过JavaDelegate和execution对象的方法来实现动态流程变量的设置和获取。具体步骤如下:
1. 设置动态流程变量:在JavaDelegate的execute方法中,可以使用execution.setVariable方法设置流程变量,示例如下:
```
public class MyDelegate implements JavaDelegate {
public void execute(DelegateExecution execution) throws Exception {
// 设置变量
execution.setVariable("varName", "varValue");
}
}
```
上述代码中,使用setVariable方法设置了名为"varName"、值为"varValue"的流程变量。
2. 获取动态流程变量:在JavaDelegate的execute方法中,可以使用execution.getVariable方法获取流程变量,示例如下:
```
public class MyDelegate implements JavaDelegate {
public void execute(DelegateExecution execution) throws Exception {
// 获取变量
String varValue = (String) execution.getVariable("varName");
}
}
```
上述代码中,通过getVariable方法获取了名为"varName"的流程变量,并将其强制转换成String类型。
需要注意的是,如果要获取一个不存在的流程变量,会返回null值。
除了使用JavaDelegate和execution对象的方法外,还可以在表达式中使用动态流程变量,示例如下:
1. 设置动态流程变量:
```
<serviceTask id="servicetask" name="Service Task">
<extensionElements>
<activiti:field name="variableName">
<activiti:expression><![CDATA[varName]]></activiti:expression>
</activiti:field>
<activiti:field name="variableValue">
<activiti:expression><![CDATA[varValue]]></activiti:expression>
</activiti:field>
</extensionElements>
</serviceTask>
```
上述代码中,使用表达式将名为"varName"、值为"varValue"的流程变量设置到Service Task节点。
2. 获取动态流程变量:
```
<userTask id="usertask1" name="User Task 1">
<extensionElements>
<activiti:assignment>
<activiti:expression><![CDATA[${execution.getVariable('varName')}]]></activiti:expression>
</activiti:assignment>
</extensionElements>
</userTask>
```
上述代码中,使用表达式获取了名为"varName"的流程变量。
总之,在Activiti中实现动态流程变量的设置和获取,并不难理解,只需要掌握JavaDelegate和execution对象的方法以及表达式的使用即可
33. 请说明Activiti中的任务候选人和任务组的作用和区别。
在Activiti中,任务候选人和任务组都是用于指定任务的参与者或负责人。它们的作用和区别如下:
1. 任务候选人:任务的候选人是指能够执行该任务的人员列表。在流程设计时可以为任务指定一个或多个候选人,在任务完成前,任何一个候选人都可以申领并处理该任务。通常情况下,任务候选人由流程变量或通过表单设定。
2. 任务组(Candidate Group):任务组是一组用户的集合,这些用户可以共同作为一个任务的候选人。类似于任务候选人,任务组也可以在任务定义时进行指定。对于指定了任务组的任务,任务的所有成员都有资格认领任务,并且只要其中任意一个成员完成该任务,该任务就会被标记为完成。
因此,任务候选人和任务组之间的区别主要在于任务的执行人数。任务候选人是任务的具体执行人,而任务组则是由多个用户组成的集合,任务可以被组内的任何一个成员认领和完成。使用候选人时需要指定一个具体的人员来执行任务,而使用任务组时可以将任务分配给组中的某一成员来处理。
总的来说,任务候选人和任务组的作用是相似的,都是为了确保任务能够及时得到处理。在使用时需要根据业务需求和实际情况,选择合适的方法来指定任务的执行人或者候选人列表。
34. 请谈谈Activiti中的流程部署方式和流程版本管理机制。
Activiti是一个基于Java的开源工作流引擎,它支持多种流程部署方式和流程版本管理机制。下面分别进行详细说明:
一、流程部署方式
1. 手动部署:手动将bpmn文件或zip包上传至Activiti引擎,然后启动部署操作即可。
2. 自动部署:使用Spring集成Activiti时,可以通过设置classpath下的自动扫描路径,将指定目录下的BPMN 2.0格式的流程定义自动部署到Activiti数据库中。
3. 在线部署:在Activiti Modeler上在线创建流程定义,并直接部署到Activiti Engine中。
二、流程版本管理机制
流程版本管理是为了管理流程定义的不同版本,以便在流程实例运行过程中可以随时切换到指定的版本。在Activiti中,通过流程key和版本号来唯一标识一个流程定义的不同版本。具体包括以下两个方面:
1. 流程定义ID:每个流程定义都有一个唯一的ID,是由Activiti Engine生成的UUID。当创建新版本的流程定义时,会产生一个新的ID。
2. 流程版本号:每个流程定义都有一个版本号,是一个带数字和点的字符串,例如"1.0","1.1"等等。如果流程定义升级了,版本号增加。流程定义的版本号和初始版本在流程设计阶段定义,可以手动指定或者在部署时由Activiti Engine自动生成。
当同一个业务的不同流程定义版本被部署后,可以根据流程key来查询最新版本的流程定义。如果需要启动旧版本的流程实例,则可以通过传递旧版本的流程定义ID来启动指定版本的流程实例。
总之,在Activiti中,有多种流程部署方式和流程版本管理机制可供选择。我们可以通过选择合适的方式,灵活部署和管理流程定义的不同版本,以更好地支持业务流程的管理和优化。
35. 你如何在Activiti中实现任务的动态路由和动态分配?
在Activiti中,可以通过实现任务监听器和JavaDelegate来实现任务的动态路由和动态分配。具体步骤如下:
1. 实现任务监听器:
任务监听器(Task Listener)是一个回调接口,它提供了在执行任务过程中的各种事件回调。通过实现任务监听器的方法,可以实现任务在运行时的动态路由和分配。例如,在任务分配前,可以动态决定该任务应该分配给哪个人或候选人组。
2. 实现JavaDelegate:
JavaDelegate代表一个流程步骤执行时的任务,实现这个接口可以自定义流程执行逻辑。在流程定义定义一个Service Task节点之后,使用class属性指定实现了JavaDelegate接口的类,然后在execute方法中实现自定义的流程逻辑。通过实现JavaDelegate接口的类,可以实现任务在执行过程中的动态路由和分配。
总之,在Activiti中,可以通过实现任务监听器和JavaDelegate来实现任务的动态路由和动态分配,具体实现方式需要根据业务需求和实际情况灵活运用。
36. 请说明Activiti中的事件网关的作用和实现方式。
37. 你如何在Activiti中实现流程的回退和撤销?
38. 请谈谈Activiti中的流程实例挂起和恢复的作用和实现方式。
39. 请说明Activiti中的定时器事件和消息事件的作用和区别。
40. 你如何在Activiti中实现流程的并行执行和串行执行?
41. 你是否研究过Activiti的源代码?如果有,请谈谈你的研究经验。
42. 请说明Activiti的核心模块有哪些,它们的作用和职责是什么?
43. 请谈谈Activiti中的持久化机制,包括数据表结构和数据访问方式。
44. 你如何在Activiti中实现自定义的任务处理逻辑?
45. 请说明Activiti中的事件监听机制是如何实现的。
46. 请谈谈Activiti中的引擎配置方式和自定义配置的实现方式。
47. 你如何在Activiti中实现自定义的流程引擎扩展?
48. 请说明Activiti中的事务管理机制是如何实现的。
49. 请谈谈Activiti中的流程引擎初始化过程和运行时架构。
50. 你如何在Activiti中实现自定义的流程部署和流程版本管理机制?
51. 你是否熟悉Activiti的REST API?如果有,请谈谈你的使用经验。
52. 请说明Activiti中的任务分派机制是如何实现的。
53. 请谈谈Activiti中的任务委托和任务代理的作用和区别。
54. 你如何在Activiti中实现任务的自动化处理?
55. 请说明Activiti中的历史数据存储方式和查询性能优化方式。
56. 你如何在Activiti中实现任务的优化分配和调度?
57. 请说明Activiti中的定时任务的实现方式和调度机制。
58. 你如何在Activiti中实现任务的自定义提醒和通知?
59. 请谈谈Activiti中的引擎扩展机制和插件开发方式。
60. 你如何在Activiti中实现任务的自动归档和删除?
61. 在Activiti源代码中的哪个类中可以找到流程图的解析逻辑?
62. 请说明Activiti中的引擎执行过程中涉及到哪些线程池?
63. 请说明Activiti中的流程实例状态和任务状态是如何存储的?
64. 请说明Activiti中的流程实例和任务实例的生命周期是如何管理的?
65. 请说明Activiti中的任务处理过程中涉及到哪些事件?
66. 请说明Activiti中的数据库事务是如何控制的?
67. 请说明Activiti中的多租户实现方式和实现原理。
68. 请说明Activiti中的流程图渲染机制和样式自定义方式。
69. 请说明Activiti中的动态表单实现方式和表单控件定制方式。
70. 请说明Activiti中的数据模型实现方式和数据模型管理机制。
71. 你是否研究过Activiti的插件机制?如果有,请谈谈你的研究经验。
72. 请说明Activiti中的事件机制是如何实现的,提供一个简单的示例。
73. 请说明Activiti中的数据库访问是如何实现的,提供一个简单的示例。
74. 你如何在Activiti中实现自定义的任务监听器?
75. 请说明Activiti中的任务处理过程中涉及到哪些Java类和方法。
76. 请说明Activiti中的动态表单实现方式和表单控件定制方式。
77. 请说明Activiti中的Spring集成方式和原理。
78. 请说明Activiti中的Web服务API是如何实现的,提供一个简单的示例。
79. 请说明Activiti中的流程图渲染机制和样式自定义方式。
80. 请说明Activiti中的数据模型实现方式和数据模型管理机制。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
2009-04-20 SortedMap接口介绍-Java快速入门教程