Activiti工作流从入门到入土:完整Hello World大比拼(Activiti工作流 API结合实例讲解)

本文主要是介绍Activiti工作流从入门到入土:完整Hello World大比拼(Activiti工作流 API结合实例讲解),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章源码托管:https://github.com/OUYANGSIHAI/Activiti-learninig
欢迎 star !!!

本来想着闲来无事,前面在项目中刚刚用到了工作流 Activiti 框架,写写博客的,但是,事情总是纷纷杂杂,一直拖延到现在,这一节原本想要写一下关于 Activiti 的 API ,但是,想着太多这样的博客了,而且显得太生硬,难以理解,所以,这些 API 就在实际的 demo 中来讲解。

一、建立流程图

在开始做工作流之前,我们首先应该把具体的业务在工作流的部署流程图体现出来,并且都测试通过,这样就相当于成功了一半,后面的具体业务的开发就相对轻松一些了。

首先,我们先看一看在 idea 中有哪些控件,常用的控件进行了标注。

下面我们讲一下建立一个流程图的具体过程。

首先,我们需要拉入一个开始节点bpmn 文件中,这是图像化的界面,只需要拉入即可。

然后,我们从控件中拉入一个 UserTask 用户任务节点到 bpmn 文件中。

这样子就有了两个审批节点了,如果还需要其他的一些业务需求,我们还可以加入一些网关,这里就暂时不加了。

最后,我们只需要一个结束节点 EndEvent 就完成了这个工作流的部署图的绘制。

我们最后看一下完整的例子。

看似已经完成了整个流程图的绘制,但美中不足的是我们目前并没有设置导师审批辅导员审批到底由谁来审批,所以,我们还是需要来瞅一瞅怎么设置审批人员

首先,我们需要选中一个审批节点,例如,选中导师审批这个节点。

其次,我们就显而易见的可以在 idea 编辑器的左侧看到一个名为 BPMN editor 的属性框,里面包括一个用户任务节点的可以设置的所有属性

**注意:**候选用户、候选组、任务监听器,这三个属性这里暂时不讲,后面再补充。

由于,这一步我们需要设置审批人,所以,我们需要在 Assignee 这个属性中设置我们的审批人。

如上图,这里设置导师审批这个节点的审批人为 sihai 。设置审批人除了直接设置之外,还有两种方式设置,后面再补充。

另外一个审批节点也通过这种方式设置就可以完成审批人的设置了。

very good,这样就基本完成了一个流程图的创建。接下来,我们将通过实例来具体讲解Activiti 的 API 的讲解

二、实例讲解 API

在上面这个流程图的创建中,我们还没有生成 png 图片,所以,如果不知道如何生成的,可以参考之前的这篇文章:Activiti工作流从入门到入土:整合spring。

既然是讲解 API ,那么还是先看一下主要有哪些 API 吧,这样才有一个整体把握。

这些 API 具体怎么用,接下来一一道来。

2.1 流程定义

既然是流程定义,那肯定少不了如何部署流程定义了。

2.1.1 部署流程定义方法1
 @Autowiredprivate ProcessEngine processEngine;@Autowiredprivate TaskService taskService;@Autowiredprivate RuntimeService runtimeService;@Autowiredprivate HistoryService historyService;/*** 部署流程定义(从classpath)*/@Testpublic void deploymentProcessDefinition_classpath(){Deployment deployment = processEngine.getRepositoryService()//与流程定义和部署对象相关的Service.createDeployment()//创建一个部署对象.name("流程定义")//添加部署的名称.addClasspathResource("bpmn/hello.bpmn")//从classpath的资源中加载,一次只能加载一个文件.addClasspathResource("bpmn/hello.png")//从classpath的资源中加载,一次只能加载一个文件.deploy();//完成部署System.out.println("部署ID:"+deployment.getId());System.out.println("部署名称:"+deployment.getName());}

注意:这里用的是整合 spring 之后的 junit 测试环境,如何整合 spring 请看这篇文章:Activiti工作流从入门到入土:整合spring。

输出结果:

这样,我们就部署了这个流程。那么具体是怎么操作的呢,我们再来看看整个过程。

  • 获取流程引擎对象:这个跟 spring 整合了。

  • 通过流程引擎获取了一个 RepositoryService 对象(仓库对象)

  • 由仓库的服务对象产生一个部署对象配置对象,用来封装部署操作的相关配置。

  • 这是一个链式编程,在部署配置对象中设置显示名,上传流程定义规则文件

  • 向数据库表中存放流程定义的规则信息。

其实,这一步操作,用到了 Activiti 数据库中的三张表,分别是:act_re_deployment(部署对象表),act_re_procdef(流程定义表),act_ge_bytearray(资源文件表)。

我们看看这三张表的变化:
1)act_re_deployment

可以看到,部署ID和部署名称就存在这张表中。

2)act_re_procdef

这张表中,存放了部署的Deployment_ID部署流程的id、bpmn资源文件名称、png图片名称等信息。

3)act_ge_bytearray

存储流程定义相关的部署信息。即流程定义文档的存放地。每部署一次就会增加两条记录,一条是关于 bpmn 规则文件的,一条是图片的(如果部署时只指定了 bpmn 一个文件,activiti 会在部署时解析 bpmn 文件内容自动生成流程图)。两个文件不是很大,都是以二进制形式存储在数据库中。

2.1.2 部署流程定义方法2
 /*** 部署流程定义(从zip)*/@Testpublic void deploymentProcessDefinition_zip(){InputStream in = this.getClass().getClassLoader().getResourceAsStream("bpmn/hello.zip");ZipInputStream zipInputStream = new ZipInputStream(in);Deployment deployment = processEngine.getRepositoryService()//与流程定义和部署对象相关的Service.createDeployment()//创建一个部署对象.name("流程定义")//添加部署的名称.addZipInputStream(zipInputStream)//指定zip格式的文件完成部署.deploy();//完成部署System.out.println("部署ID:"+deployment.getId());//System.out.println("部署名称:"+deployment.getName());//}

项目结构如下:

输出结果:

如此看来,也是没有任何问题的,唯一的区别只是压缩成zip格式的文件,使用zip的输入流用作部署流程定义,其他使用并无区别。

部署了流程定义之后,我们应该想查看一下流程定义的一些信息。

2.1.3 查看流程定义
/*** 查询流程定义*/@Testpublic void findProcessDefinition(){List<ProcessDefinition> list = processEngine.getRepositoryService()//与流程定义和部署对象相关的Service.createProcessDefinitionQuery()//创建一个流程定义的查询/**指定查询条件,where条件*/
//						.deploymentId(deploymentId)//使用部署对象ID查询
//						.processDefinitionId(processDefinitionId)//使用流程定义ID查询
//						.processDefinitionKey(processDefinitionKey)//使用流程定义的key查询
//						.processDefinitionNameLike(processDefinitionNameLike)//使用流程定义的名称模糊查询/**排序*/.orderByProcessDefinitionVersion().asc()//按照版本的升序排列
//						.orderByProcessDefinitionName().desc()//按照流程定义的名称降序排列/**返回的结果集*/.list();//返回一个集合列表,封装流程定义
//						.singleResult();//返回惟一结果集
//						.count();//返回结果集数量
//						.listPage(firstResult, maxResults);//分页查询if(list!=null && list.size()>0){for(ProcessDefinition pd:list){System.out.println("流程定义ID:"+pd.getId());//流程定义的key+版本+随机生成数System.out.println("流程定义的名称:"+pd.getName());//对应hello.bpmn文件中的name属性值System.out.println("流程定义的key:"+pd.getKey());//对应hello.bpmn文件中的id属性值System.out.println("流程定义的版本:"+pd.getVersion());//当流程定义的key值相同的相同下,版本升级,默认1System.out.println("资源名称bpmn文件:"+pd.getResourceName());System.out.println("资源名称png文件:"+pd.getDiagramResourceName());System.out.println("部署对象ID:"+pd.getDeploymentId());System.out.println("*********************************************");}}}

输出结果:

查询流程定义小结:

  • 流程定义和部署对象相关的Service都是 RepositoryService ,后面会发现关于流程定义的都是 RepositoryService

  • 通过这个 createProcessDefinitionQuery() 方法来设置一些查询参数,比如通过条件、降序升序等。

2.1.4 删除流程定义

通过删除部署 ID 为2501的信息。

/*** 删除流程定义*/@Testpublic void deleteProcessDefinition(){//使用部署ID,完成删除,指定部署对象id为2501删除String deploymentId = "2501";/*** 不带级联的删除*    只能删除没有启动的流程,如果流程启动,就会抛出异常*/
//		processEngine.getRepositoryService()//
//						.deleteDeployment(deploymentId);/*** 级联删除* 	  不管流程是否启动,都能可以删除*/processEngine.getRepositoryService()//.deleteDeployment(deploymentId, true);System.out.println("删除成功!");}

输出结果:

到数据库查看,发现 act_re_deployment 中的数据已经不存在了。

  • 这里还是通过 getRepositoryService() 方法获取部署定义对象,然后指定 ID 删除信息。
2.1.5 获取流程定义文档的资源

这里的作用主要是查询图片,通过图片可以在后面做流程展示用的。我们看看具体怎么查看。

/*** 查看流程图** @throws IOException*/@Testpublic void viewPic() throws IOException {/**将生成图片放到文件夹下*/String deploymentId = "5001";//获取图片资源名称List<String> list = processEngine.getRepositoryService()//.getDeploymentResourceNames(deploymentId);//定义图片资源的名称String resourceName = "";if (list != null && list.size() > 0) {for (String name : list) {if (name.indexOf(".png") >= 0) {resourceName = name;}}}//获取图片的输入流InputStream in = processEngine.getRepositoryService()//.getResourceAsStream(deploymentId, resourceName);//将图片生成到F盘的目录下File file = new File("F:/" + resourceName);//将输入流的图片写到磁盘FileUtils.copyInputStreamToFile(in, file);}

在F盘下,可以找到图片。

2.1.6 查询最新版本的流程定义
 /*** 查询最新版本的流程定义*/@Testpublic void findLastVersionProcessDefinition() {List<ProcessDefinition> list = processEngine.getRepositoryService()//.createProcessDefinitionQuery()//.orderByProcessDefinitionVersion().asc()//使用流程定义的版本升序排列.list();/**map集合的特点:当map集合key值相同的情况下,后一次的值将替换前一次的值*/Map<String, ProcessDefinition> map = new LinkedHashMap<String, ProcessDefinition>();if (list != null && list.size() > 0) {for (ProcessDefinition pd : list) {map.put(pd.getKey(), pd);}}List<ProcessDefinition> pdList = new ArrayList<ProcessDefinition>(map.values());if (pdList != null && pdList.size() > 0) {for (ProcessDefinition pd : pdList) {System.out.println("流程定义ID:" + pd.getId());//流程定义的key+版本+随机生成数System.out.println("流程定义的名称:" + pd.getName());//对应hello.bpmn文件中的name属性值System.out.println("流程定义的key:" + pd.getKey());//对应hello.bpmn文件中的id属性值System.out.println("流程定义的版本:" + pd.getVersion());//当流程定义的key值相同的相同下,版本升级,默认1System.out.println("资源名称bpmn文件:" + pd.getResourceName());System.out.println("资源名称png文件:" + pd.getDiagramResourceName());System.out.println("部署对象ID:" + pd.getDeploymentId());System.out.println("*********************************************************************************");}}}

输出结果:

2.1.7 流程定义总结

1、部署流程定义用到了 Activiti 的下面的几张表。

  • act_re_deployment:部署对象表
  • act_re_procdef:流程定义表
  • act_ge_bytearray:资源文件表
  • act_ge_property:主键生成策略表

2、我们发现部署流程定义的操作都是在 RepositoryService 这个类下进行操作的,我们只需要通过 getRepositoryService() 拿到对象,通过链式规则就可以进行部署流程定义的所有操作。

2.2 工作流完整实例的使用

这一节,我们通过一个完整的例子,来总结一下前面讲过的一些基本的知识,这样能够更好的学习前面以及后面的知识点,这也算是一个过渡的章节。

回到第一节的建立流程图,我们已经将基本的 bpmn 图已经建立好了,但是,需要做一个完整的实例,我们还是需要补充一些内容的,这样才能够把这样的一个实例做好,我们先把第一节的那个 bpmn 图拿过来。

首先,我们需要明确:这个图到目前为止,我们只是简简单单的把流程给画出来了,比如,我们需要审核的时候,是需要具体到某一个具体的人员去审核的,所以,我们需要给每个节点设置审核的具体人员。

**注意:**设置节点的审核人员后面还会分一节细讲,这里只是做一个简单的实例,所以,只需要这里能够看懂,做好就ok了。

设置审核人员步骤

首先,我们需要选中一个节点,例如,下图中的“导师审批”节点。

接下来,在左边的工具栏,我们会看到好多选项,有一项为 Assignee ,我们需要在这个选项中设置我们这个节点需要设置的审批人。

**Assignee设置格式:**直接使用英文或者中文都可以,例如,sihai,更复杂的设置后面再讲。

下面的节点设置也是跟上面一模一样。

辅导员审批的审批人员是:欧阳思海。

perfect,这样流程图的任务就完成了,下面我们就可以进行这个实例的测试阶段了。

1)部署流程定义
部署流程定义,在前面的章节已经讲过了,有两种方式进行处理,一种是加载 bpmn 文件和 png 文件,还有一种是将这两个文件压缩成 zip 格式的压缩文件,然后加载。这里我们使用第一种方式进行处理。

/*** 部署流程定义(从classpath)*/@Testpublic void deploymentProcessDefinition_classpath() {Deployment deployment = processEngine.getRepositoryService()//与流程定义和部署对象相关的Service.createDeployment()//创建一个部署对象.name("hello")//添加部署的名称.addClasspathResource("bpmn/hello.bpmn")//从classpath的资源中加载,一次只能加载一个文件.addClasspathResource("bpmn/hello.png")//从classpath的资源中加载,一次只能加载一个文件.deploy();//完成部署log.info("部署ID:" + deployment.getId());log.info("部署名称:" + deployment.getName());}

现在流程定义已经有了,下面我们就需要启动这个流程实例。

关于关于这一步做了什么事情,可以在前面的章节查看。

2)启动流程实例

 /*** 启动流程实例*/@Testpublic void startProcessInstance(){//1、流程定义的key,通过这个key来启动流程实例String processDefinitionKey = "hello";//2、与正在执行的流程实例和执行对象相关的Service// startProcessInstanceByKey方法还可以设置其他的参数,比如流程变量。ProcessInstance pi = processEngine.getRuntimeService().startProcessInstanceByKey(processDefinitionKey);//使用流程定义的key启动流程实例,key对应helloworld.bpmn文件中id的属性值,使用key值启动,默认是按照最新版本的流程定义启动log.info("流程实例ID:"+pi.getId());//流程实例IDlog.info("流程定义ID:"+pi.getProcessDefinitionId());//流程定义ID}


注意: processDefinitionKey 是 bpmn 文件的名称。

步骤
1 获取到 runtimeService 实例。
2 通过 bpmn 文件的名称,也就是 processDefinitionKey 来启动流程实例。
3 启动流程后,流程的任务就走到了导师审批节点。

下面就是查询个人任务了,我们可以查询导师审批节点的任务。

3)查询个人任务

/*** 查询当前人的个人任务*/@Testpublic void findPersonalTask(){String assignee = "sihai";List<Task> list = processEngine.getTaskService()//与正在执行的任务管理相关的Service.createTaskQuery()//创建任务查询对象/**查询条件(where部分)*/.taskAssignee(assignee)//指定个人任务查询,指定办理人
//						.taskCandidateUser(candidateUser)//组任务的办理人查询
//						.processDefinitionId(processDefinitionId)//使用流程定义ID查询
//						.processInstanceId(processInstanceId)//使用流程实例ID查询
//						.executionId(executionId)//使用执行对象ID查询/**排序*/.orderByTaskCreateTime().asc()//使用创建时间的升序排列/**返回结果集*/
//						.singleResult()//返回惟一结果集
//						.count()//返回结果集的数量
//						.listPage(firstResult, maxResults);//分页查询.list();//返回列表if(list!=null && list.size()>0){for(Task task:list){log.info("任务ID:"+task.getId());log.info("任务名称:"+task.getName());log.info("任务的创建时间:"+task.getCreateTime());log.info("任务的办理人:"+task.getAssignee());log.info("流程实例ID:"+task.getProcessInstanceId());log.info("执行对象ID:"+task.getExecutionId());log.info("流程定义ID:"+task.getProcessDefinitionId());log.info("********************************************");}}}

通过 sihai 这个审批人,查询到了下面的信息。

分析步骤
1 首先通过 getTaskService 方法,获取到 TaskService 对象。
2 通过 createTaskQuery 方法创建查询对象。
3 通过 taskAssignee 方法设置审核人。
4 对于结果的返回,我们可以通过 orderByTaskCreateTime().asc() 设置排序等其他信息。

这里需要注意一点,查询到的一个重要的信息是:任务 id(taskId),下一步,我们需要通过这个任务 id ,来完成任务。

4)办理个人任务

/*** 完成我的任务*/@Testpublic void completePersonalTask() {//任务ID,上一步查询得到的。String taskId = "7504";processEngine.getTaskService()//与正在执行的任务管理相关的Service.complete(taskId);log.info("完成任务:任务ID:" + taskId);}

通过上一步的任务 id :7504,完成任务。

步骤
1 首先,通过 getTaskService 方法拿到 TaskService 对象。
2 调用 complete 方法,给定具体的任务 id 完成任务。

5)查询流程状态(判断流程走到哪一个节点)
这个接口还是十分需要的,当我们在具体的业务中,我们需要判断我们的流程的状态是什么状态,或者说我们的流程走到了哪一个节点的时候,这一个接口就让我们实现业务省了非常多的事情。

/*** 查询流程状态(判断流程走到哪一个节点)*/@Testpublic void isProcessActive() {String processInstanceId = "7501";ProcessInstance pi = processEngine.getRuntimeService()//表示正在执行的流程实例和执行对象.createProcessInstanceQuery()//创建流程实例查询.processInstanceId(processInstanceId)//使用流程实例ID查询.singleResult();if (pi == null) {log.info("流程已经结束");} else {log.info("流程没有结束");//获取任务状态log.info("节点id:" + pi.getActivityId());}}

步骤:
1 获取到流程实例 ProcessInstance 对象。
2 通过 getActivityId 方法获取到实例 Id(节点 id )。

那么拿到了节点 Id 有什么作用呢?
其实,有了这个 Id 之后,我们就可以判断流程走到哪一步了。例如,上面的输出的节点 id 是 _4,这个 _4 就是对应 辅导员审批节点的 id,所以,我们就可以判读流程其实是已经走到这个节点了,后期需要在页面显示流程状态的时候就发挥作用了。

6)查询流程执行的历史信息
通过查看 activiti 5 的官方 API 接口,发现查看历史信息有下面的查询接口。

下面我们通过上面的实例对下面的方法一一进行测试。

历史活动实例查询接口

/*** 历史活动查询接口*/@Testpublic void findHistoryActivity() {String processInstanceId = "7501";List<HistoricActivityInstance> hais = processEngine.getHistoryService()//.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).list();for (HistoricActivityInstance hai : hais) {log.info("活动id:" + hai.getActivityId()+ "   审批人:" + hai.getAssignee()+ "   任务id:" + hai.getTaskId());log.info("************************************");}}

通过这个接口不仅仅查到这些信息,还有其他的方法,可以获取更多的关于历史活动的其他信息。

历史流程实例查询接口

/*** 查询历史流程实例*/@Testpublic void findHistoryProcessInstance() {String processInstanceId = "7501";HistoricProcessInstance hpi = processEngine.getHistoryService()// 与历史数据(历史表)相关的Service.createHistoricProcessInstanceQuery()// 创建历史流程实例查询.processInstanceId(processInstanceId)// 使用流程实例ID查询.orderByProcessInstanceStartTime().asc().singleResult();log.info(hpi.getId() + "    " + hpi.getProcessDefinitionId() + "    " + hpi.getStartTime() + "    "+ hpi.getEndTime() + "     " + hpi.getDurationInMillis());}

这个接口可以查询到关于历史流程实例的所有信息。

历史任务实例查询接口

 /*** 查询历史任务*/@Testpublic void findHistoryTask() {String processInstanceId = "7501";List<HistoricTaskInstance> list = processEngine.getHistoryService()// 与历史数据(历史表)相关的Service.createHistoricTaskInstanceQuery()// 创建历史任务实例查询.processInstanceId(processInstanceId)//.orderByHistoricTaskInstanceStartTime().asc().list();if (list != null && list.size() > 0) {for (HistoricTaskInstance hti : list) {log.info("\n 任务Id:" + hti.getId() + "    任务名称:" + hti.getName() + "    流程实例Id:" + hti.getProcessInstanceId() + "\n 开始时间:"+ hti.getStartTime() + "   结束时间:" + hti.getEndTime() + "   持续时间:" + hti.getDurationInMillis());}}}

这个查询接口可以查询到历史任务信息

历史流程变量查询接口

/*** 查询历史流程变量*/@Testpublic void findHistoryProcessVariables() {String processInstanceId = "7501";List<HistoricVariableInstance> list = processEngine.getHistoryService()//.createHistoricVariableInstanceQuery()// 创建一个历史的流程变量查询对象.processInstanceId(processInstanceId)//.list();if (list != null && list.size() > 0) {for (HistoricVariableInstance hvi : list) {log.info("\n" + hvi.getId() + "   " + hvi.getProcessInstanceId() + "\n" + hvi.getVariableName()+ "   " + hvi.getVariableTypeName() + "    " + hvi.getValue());}}}

在这个实例中没有设置流程变量,所以,这里是查询不到任何历史信息的。

这个接口主要是关于历史流程变量的设置的一些信息。

历史本地接口查询接口

/*** 通过执行sql来查询历史数据,由于activiti底层就是数据库表。*/@Testpublic void findHistoryByNative() {HistoricProcessInstance hpi = processEngine.getHistoryService().createNativeHistoricProcessInstanceQuery().sql("查询底层数据库表的sql语句").singleResult();log.info("\n" + hpi.getId() + "    " + hpi.getProcessDefinitionId() + "    " + hpi.getStartTime()+ "\n" + hpi.getEndTime() + "     " + hpi.getDurationInMillis());}

这个接口是提供直接通过 sql 语句来查询历史信息的,我们只需要在 sql() 方法中写原生的 sql 语句就可以进行数据查询。

写到这里,我想应该通过这样的一个完整的实例将 Activiti 工作流的 API 都介绍的差不多了,这一节到这里也就要说拜拜了。再回看一下文章开头的 API 接口,这也算是这一节的总结。

这篇关于Activiti工作流从入门到入土:完整Hello World大比拼(Activiti工作流 API结合实例讲解)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/495758

相关文章

Python跨文件实例化、跨文件调用及导入库示例代码

《Python跨文件实例化、跨文件调用及导入库示例代码》在Python开发过程中,经常会遇到需要在一个工程中调用另一个工程的Python文件的情况,:本文主要介绍Python跨文件实例化、跨文件调... 目录1. 核心对比表格(完整汇总)1.1 自定义模块跨文件调用汇总表1.2 第三方库使用汇总表1.3 导

Python实现Word转PDF全攻略(从入门到实战)

《Python实现Word转PDF全攻略(从入门到实战)》在数字化办公场景中,Word文档的跨平台兼容性始终是个难题,而PDF格式凭借所见即所得的特性,已成为文档分发和归档的标准格式,下面小编就来和大... 目录一、为什么需要python处理Word转PDF?二、主流转换方案对比三、五套实战方案详解方案1:

SpringBoot结合Knife4j进行API分组授权管理配置详解

《SpringBoot结合Knife4j进行API分组授权管理配置详解》在现代的微服务架构中,API文档和授权管理是不可或缺的一部分,本文将介绍如何在SpringBoot应用中集成Knife4j,并进... 目录环境准备配置 Swagger配置 Swagger OpenAPI自定义 Swagger UI 底

setsid 命令工作原理和使用案例介绍

《setsid命令工作原理和使用案例介绍》setsid命令在Linux中创建独立会话,使进程脱离终端运行,适用于守护进程和后台任务,通过重定向输出和确保权限,可有效管理长时间运行的进程,本文给大家介... 目录setsid 命令介绍和使用案例基本介绍基本语法主要特点命令参数使用案例1. 在后台运行命令2.

使用Python的requests库调用API接口的详细步骤

《使用Python的requests库调用API接口的详细步骤》使用Python的requests库调用API接口是开发中最常用的方式之一,它简化了HTTP请求的处理流程,以下是详细步骤和实战示例,涵... 目录一、准备工作:安装 requests 库二、基本调用流程(以 RESTful API 为例)1.

Python调用LibreOffice处理自动化文档的完整指南

《Python调用LibreOffice处理自动化文档的完整指南》在数字化转型的浪潮中,文档处理自动化已成为提升效率的关键,LibreOffice作为开源办公软件的佼佼者,其命令行功能结合Python... 目录引言一、环境搭建:三步构建自动化基石1. 安装LibreOffice与python2. 验证安装

Spring WebClient从入门到精通

《SpringWebClient从入门到精通》本文详解SpringWebClient非阻塞响应式特性及优势,涵盖核心API、实战应用与性能优化,对比RestTemplate,为微服务通信提供高效解决... 目录一、WebClient 概述1.1 为什么选择 WebClient?1.2 WebClient 与

Python极速搭建局域网文件共享服务器完整指南

《Python极速搭建局域网文件共享服务器完整指南》在办公室或家庭局域网中快速共享文件时,许多人会选择第三方工具或云存储服务,但这些方案往往存在隐私泄露风险或需要复杂配置,下面我们就来看看如何使用Py... 目录一、android基础版:HTTP文件共享的魔法命令1. 一行代码启动HTTP服务器2. 关键参

SpringBoot监控API请求耗时的6中解决解决方案

《SpringBoot监控API请求耗时的6中解决解决方案》本文介绍SpringBoot中记录API请求耗时的6种方案,包括手动埋点、AOP切面、拦截器、Filter、事件监听、Micrometer+... 目录1. 简介2.实战案例2.1 手动记录2.2 自定义AOP记录2.3 拦截器技术2.4 使用Fi

RabbitMQ消费端单线程与多线程案例讲解

《RabbitMQ消费端单线程与多线程案例讲解》文章解析RabbitMQ消费端单线程与多线程处理机制,说明concurrency控制消费者数量,max-concurrency控制最大线程数,prefe... 目录 一、基础概念详细解释:举个例子:✅ 单消费者 + 单线程消费❌ 单消费者 + 多线程消费❌ 多