SpringBoot整合flowable及其簡單的使用實(shí)例教程
1. 什么是flowable?
Flowable是一個(gè)使用Java編寫的開源輕量級業(yè)務(wù)流程引擎,基于Apache 2.0協(xié)議,支持BPMN 2.0規(guī)范。它提供了從流程建模、執(zhí)行到監(jiān)控的全生命周期管理能力,能夠處理流程流轉(zhuǎn)相關(guān)的業(yè)務(wù),如審批流、工作流等。Flowable由Activiti項(xiàng)目發(fā)展而來,在保持原有功能的基礎(chǔ)上進(jìn)行了優(yōu)化和擴(kuò)展。
核心功能
- 流程定義與部署:支持BPMN 2.0流程定義的部署,可創(chuàng)建流程實(shí)例,進(jìn)行查詢以及訪問運(yùn)行中或歷史的流程實(shí)例與相關(guān)數(shù)據(jù)。
- 任務(wù)管理:提供任務(wù)管理功能,允許用戶查詢、處理和完成任務(wù),任務(wù)可以是人工任務(wù)或系統(tǒng)任務(wù)。
- 事件處理:支持事件處理機(jī)制,允許在流程執(zhí)行過程中觸發(fā)和監(jiān)聽各種事件,如開始事件、結(jié)束事件等。
- 表單管理:可與表單設(shè)計(jì)器配合使用,在流程中定義動態(tài)表單,并掛載表單數(shù)據(jù)。
- 實(shí)時(shí)監(jiān)控與報(bào)告:提供豐富的監(jiān)控功能,允許用戶實(shí)時(shí)查看流程實(shí)例的執(zhí)行狀態(tài)、任務(wù)執(zhí)行情況等。
核心組件
- Flowable IDM:身份管理應(yīng)用,提供單點(diǎn)登錄認(rèn)證功能,管理用戶、組與權(quán)限。
- Flowable Modeler:用于創(chuàng)建流程模型、表單、選擇表與應(yīng)用定義。
- Flowable Task:運(yùn)行時(shí)任務(wù)應(yīng)用,提供啟動流程實(shí)例、編輯任務(wù)表單等功能。
- Flowable Admin:管理應(yīng)用,查詢BPMN、DMN等引擎,并提供修改流程實(shí)例等功能。
- API服務(wù):通過ProcessEngine與Flowable交互,提供工作流/BPM方法。
使用場景
- 審批流程:如請假申請、報(bào)銷審批等,可設(shè)計(jì)流程、部署定義、啟動實(shí)例、處理任務(wù)。
- 工作流管理:適用于復(fù)雜多變的業(yè)務(wù)場景,根據(jù)業(yè)務(wù)流程建模,處理任務(wù)。
- 企業(yè)數(shù)字化轉(zhuǎn)型:作為企業(yè)實(shí)現(xiàn)數(shù)字化轉(zhuǎn)型的重要工具,提高業(yè)務(wù)處理的效率和靈活性。
特點(diǎn)
- 輕量級:以JAR形式發(fā)布,可輕易加入任何Java環(huán)境。
- 高性能:專注于提供高性能的業(yè)務(wù)流程管理解決方案。
- 靈活性:能夠靈活地嵌入到各種應(yīng)用和服務(wù)中。
- 擴(kuò)展性:支持NoSQL、LDAP等,具有良好的擴(kuò)展性。
2. flowable的重要實(shí)現(xiàn)類
流程引擎相關(guān)
- ProcessEngine:Flowable的核心組件,負(fù)責(zé)解析BPMN 2.0流程定義文件,并執(zhí)行其中的流程實(shí)例。它提供了豐富的API,方便開發(fā)者對流程實(shí)例進(jìn)行各種操作,如啟動、掛起、恢復(fù)、終止等。
- ProcessEngineConfiguration:用于配置和初始化流程引擎,可以設(shè)置數(shù)據(jù)庫連接、事務(wù)管理器、監(jiān)聽器等信息。
- RuntimeService:提供運(yùn)行時(shí)流程實(shí)例和執(zhí)行的相關(guān)操作,如啟動流程實(shí)例、查詢運(yùn)行時(shí)流程實(shí)例、獲取流程變量等。
- TaskService:與任務(wù)相關(guān)的服務(wù)類,用于管理任務(wù)的創(chuàng)建、分配、完成等操作。
- RepositoryService:用于管理流程定義和部署,可以部署流程定義文件、查詢流程定義、獲取流程模型等。
事務(wù)管理相關(guān)
- CommandExecutorImpl:命令執(zhí)行器的實(shí)現(xiàn)類,負(fù)責(zé)執(zhí)行具體的命令。
- SpringTransactionInterceptor:Spring事務(wù)攔截器,用于處理事務(wù)的傳播行為,確保流程操作在正確的事務(wù)上下文中執(zhí)行。
- TransactionContextInterceptor:事務(wù)上下文攔截器,用于管理事務(wù)上下文中的命令執(zhí)行。
監(jiān)聽器相關(guān)
- TaskListener:任務(wù)監(jiān)聽器接口,用于在任務(wù)的生命周期中監(jiān)聽特定事件,如任務(wù)創(chuàng)建、分配、完成等。
- ExecutionListener:執(zhí)行監(jiān)聽器接口,用于在流程執(zhí)行的生命周期中監(jiān)聽特定事件。
流程圖生成相關(guān)
- ProcessDiagramGenerator:流程圖生成器,用于根據(jù)BPMN模型生成流程圖。
身份管理相關(guān)
- Flowable IDM:身份管理應(yīng)用,為所有Flowable UI應(yīng)用提供單點(diǎn)登錄認(rèn)證功能,并且為擁有IDM管理員權(quán)限的用戶提供了管理用戶、組與權(quán)限的功能。
建模與任務(wù)相關(guān)
- Flowable Modeler:基于Web的建模工具,可用于創(chuàng)建分別符合BPMN 2.0、CMMN和DMN規(guī)范的業(yè)務(wù)流程。
- Flowable Task:運(yùn)行時(shí)任務(wù)應(yīng)用,提供了啟動流程實(shí)例、編輯任務(wù)表單、完成任務(wù),以及查詢流程實(shí)例與任務(wù)的功能。
管理與監(jiān)控相關(guān)
- Flowable Admin:管理應(yīng)用,讓具有管理員權(quán)限的用戶可以查詢BPMN、DMN、Form及Content引擎,并提供了許多選項(xiàng)用于修改流程實(shí)例、任務(wù)、作業(yè)等。
3. SpringBoot整合flowable
maven導(dǎo)入flowable
maven中引入flowable-spring-boot-starter的相關(guān)依賴,我導(dǎo)入的是6.6.0版本,然后再導(dǎo)入數(shù)據(jù)庫相關(guān)依賴(flowable需要配合數(shù)據(jù)庫使用,且支持多種數(shù)據(jù)庫,我這里使用MySQL8.0為例)
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter</artifactId>
<version>6.6.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>制作流程圖
在idea中安裝bpmn流程圖的制作工具 Flowable BPMN visualizer
我這以IntelliJ IDEA 2024.3.1.1 (Ultimate Edition)為例
左上角四橫線圖片--File--Setting--Plugins--Installed,搜索 Flowable BPMN visualizer后點(diǎn)擊安裝

在resource目錄下新建processes文件夾(flowable默認(rèn)讀這個(gè)文件夾下的流程圖),新建文件xxx.bpmn20.xml(流程圖文件名必須以.bpmn20.xml或者bpmn結(jié)尾),右鍵文件選擇View BPMN

打開BPMN編輯頁面就可以制作自己想要的流程圖。
為了測試flowable我這里提供了一個(gè)簡單的流程圖
<?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" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:flowable="http://flowable.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.flowable.org/processdef">
<!-- id為流程圖名稱要遵循xml的CNAME的起名規(guī)則 name為流程圖中文名稱 -->
<process id="leave_flow" name="請假流程" isExecutable="true">
<startEvent id="startEvent1"/>
<!-- flowable:assignee="${userTask}"表示的意思是從哪個(gè)字段中獲取申請人的名稱 -->
<userTask id="applyPerson" name="申請人" flowable:assignee="${userTask}">
<extensionElements>
<modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
</extensionElements>
</userTask>
<sequenceFlow id="sid-78321D10-B222-41F2-A655-2550B97688E0" sourceRef="startEvent1" targetRef="applyPerson"/>
<userTask id="manager" name="經(jīng)理">
<extensionElements>
<!-- 這是流程監(jiān)聽器所指定的審批人,class要指定監(jiān)聽 -->
<flowable:taskListener event="create" class="com.sd.taskListener.ManagerTaskListener"/>
</extensionElements>
</userTask>
<sequenceFlow id="sid-25A43AFD-67C2-41F7-87DF-25826AA220F8" sourceRef="applyPerson" targetRef="manager"/>
<userTask id="boss" name="老板">
<extensionElements>
<!-- 這是流程監(jiān)聽器所指定的審批人,class要指定監(jiān)聽 -->
<flowable:taskListener event="create" class="com.sd.taskListener.BossTaskListener"/>
</extensionElements>
</userTask>
<endEvent id="sid-27AE7469-EC65-4EF0-B85E-3A658BD85EE3"/>
<!-- 這里表示指定兩個(gè)流程監(jiān)聽器的處理動作,同意/駁回 -->
<sequenceFlow id="refuse" name="駁回" sourceRef="manager" targetRef="applyPerson">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${result == "駁回"}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="pass" name="通過" sourceRef="manager" targetRef="boss">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${result == "同意"}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="refuse2" name="駁回" sourceRef="boss" targetRef="applyPerson">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${result == "駁回"}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="pass2" name="通過" sourceRef="boss" targetRef="sid-27AE7469-EC65-4EF0-B85E-3A658BD85EE3">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${result == "同意"}]]></conditionExpression>
</sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_leave_flow">
<bpmndi:BPMNPlane bpmnElement="leave_flow" id="BPMNPlane_leave_flow">
<bpmndi:BPMNShape bpmnElement="startEvent1" id="BPMNShape_startEvent1">
<omgdc:Bounds height="30.0" width="30.0" x="100.0" y="163.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="applyPerson" id="BPMNShape_applyPerson">
<omgdc:Bounds height="80.0" width="100.0" x="175.0" y="138.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="manager" id="BPMNShape_manager">
<omgdc:Bounds height="80.0" width="100.0" x="345.0" y="138.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="boss" id="BPMNShape_boss">
<omgdc:Bounds height="80.0" width="100.0" x="510.0" y="138.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="sid-27AE7469-EC65-4EF0-B85E-3A658BD85EE3" id="BPMNShape_sid-27AE7469-EC65-4EF0-B85E-3A658BD85EE3">
<omgdc:Bounds height="28.0" width="28.0" x="735.0" y="164.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="refuse" id="BPMNEdge_refuse">
<omgdi:waypoint x="395.0" y="217.95000000000002"/>
<omgdi:waypoint x="395.0" y="291.0"/>
<omgdi:waypoint x="225.0" y="291.0"/>
<omgdi:waypoint x="225.0" y="217.95"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="pass" id="BPMNEdge_pass">
<omgdi:waypoint x="444.9499999998897" y="178.0"/>
<omgdi:waypoint x="509.99999999998465" y="178.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="pass2" id="BPMNEdge_pass2">
<omgdi:waypoint x="609.9499999999675" y="178.0"/>
<omgdi:waypoint x="735.0" y="178.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="refuse2" id="BPMNEdge_refuse2">
<omgdi:waypoint x="560.0" y="138.0"/>
<omgdi:waypoint x="560.0" y="76.0"/>
<omgdi:waypoint x="225.0" y="76.0"/>
<omgdi:waypoint x="225.0" y="138.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sid-78321D10-B222-41F2-A655-2550B97688E0" id="BPMNEdge_sid-78321D10-B222-41F2-A655-2550B97688E0">
<omgdi:waypoint x="129.9499984899576" y="178.0"/>
<omgdi:waypoint x="175.0" y="178.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sid-25A43AFD-67C2-41F7-87DF-25826AA220F8" id="BPMNEdge_sid-25A43AFD-67C2-41F7-87DF-25826AA220F8">
<omgdi:waypoint x="274.95" y="178.0"/>
<omgdi:waypoint x="344.99999999993565" y="178.0"/>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>查看流程圖

設(shè)置SpringBoot相關(guān)配置
spring:
application:
name: springboot-flowable
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/flowable_test?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8&nullCatalogMeansCurrent=true
username: root
password: 123456
server:
port: 3333
flowable:
#關(guān)閉定時(shí)任務(wù)job
async-executor-activate: false
#對數(shù)據(jù)庫表進(jìn)行操作,如果表不存在則會新建
database-schema-update: true配置flowable相關(guān)配置
防止流程圖中文亂碼配置
@Configuration
public class FlowableConfig implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {
@Override
public void configure(SpringProcessEngineConfiguration springProcessEngineConfiguration) {
springProcessEngineConfiguration.setActivityFontName("宋體");
springProcessEngineConfiguration.setLabelFontName("宋體");
springProcessEngineConfiguration.setAnnotationFontName("宋體");
}
}配置流程監(jiān)聽器
經(jīng)理監(jiān)聽器
public class ManagerTaskListener implements TaskListener {
@Override
public void notify(DelegateTask delegateTask) {
delegateTask.setAssignee("經(jīng)理");
System.out.println("經(jīng)理收到了流程");
}
}配置老板監(jiān)聽器
public class BossTaskListener implements TaskListener {
@Override
public void notify(DelegateTask delegateTask) {
delegateTask.setAssignee("老板");
System.out.println("老板收到了流程");
}
}
編寫flowable相關(guān)操作
我這里演示發(fā)起流程,查看流程,獲取審批流程,審批同意,駁回以及查看流程圖
其他操作跟這些都大差不大,可自行擴(kuò)展
package com.sd.controller;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.*;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;
/**
* @author sd
* @create 2025-06-17-下午 4:06
*/
@RestController
@RequestMapping("/flowable")
public class FlowableController {
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Autowired
private RepositoryService repositoryService;
@Autowired
private ProcessEngine processEngine;
/**
* 發(fā)起請假
*
* @param userId 用戶Id
* @param days 請假天數(shù)
* @param desc 描述
*/
@GetMapping(value = "/add")
public String addExpense(String userId,Integer days,String desc) {
//設(shè)置流程參數(shù),用戶名等等
HashMap<String, Object> map = new HashMap<>();
map.put("userTask", userId);
map.put("days", days);
map.put("desc", desc);
//啟動一個(gè)流程
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("leave_flow", map);
//生成一個(gè)任務(wù)
Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
//第一步任務(wù)流程完成后,會按照流程圖自動分配給下一個(gè)流程
taskService.complete(task.getId());
return "提交成功\n流程Id為:" + processInstance.getId()
+ "\n任務(wù)id: " + task.getId()
+ "\n請假人為:" +userId
+ "\n請假天數(shù)為:" + days
+ "\n描述為:" + desc;
}
/**
* 查詢請假流程
*
* @param userId 查詢?nèi)?
*
* */
@GetMapping("findTask")
public String findTask(String userId) {
//從引擎中獲取任務(wù)
TaskService taskService = processEngine.getTaskService();
Task task = taskService.createTaskQuery().taskAssignee(userId).singleResult();
return userId + "的任務(wù)ID:"+task.getId();
}
/**
* 獲取審批管理列表
*
* @param userId 查詢?nèi)?
*
*/
@GetMapping(value = "/list")
public Map<String,String> list(String userId) {
Map<String,String> result = new HashMap<>();
List<Task> tasks = taskService.createTaskQuery().taskAssignee(userId).orderByTaskCreateTime().desc().list();
for (Task task : tasks) {
result.put(task.getName(),task.getId());
}
return result;
}
/**
* 批準(zhǔn)
*
* @param taskId 任務(wù)ID
*/
@GetMapping(value = "apply")
public String apply(String taskId) {
//獲得任務(wù)
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
if (task == null) {
throw new RuntimeException("流程不存在");
}
//通過審核
HashMap<String, Object> map = new HashMap<>();
map.put("result", "同意");
taskService.complete(taskId, map);
return "任務(wù)ID:" + taskId + "---批準(zhǔn)!";
}
/**
* 駁回
*/
@GetMapping(value = "reject")
public String reject(String taskId) {
//獲得任務(wù)
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
if (task == null) {
throw new RuntimeException("流程不存在");
}
HashMap<String, Object> map = new HashMap<>();
map.put("result", "駁回");
taskService.complete(taskId, map);
return "任務(wù)ID:" + taskId + "---駁回!";
}
/**
* 生成流程圖
*
* @param taskId 任務(wù)ID
*/
@GetMapping(value = "processDiagram")
public void genProcessDiagram(HttpServletResponse httpServletResponse, String taskId) throws Exception {
ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(taskId).singleResult();
//流程走完的不顯示圖
if (pi == null) {
return;
}
Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
//使用流程實(shí)例ID,查詢正在執(zhí)行的執(zhí)行對象表,返回流程實(shí)例對象
String InstanceId = task.getProcessInstanceId();
List<Execution> executions = runtimeService
.createExecutionQuery()
.processInstanceId(InstanceId)
.list();
//得到正在執(zhí)行的flowable的Id
List<String> flowableIds = new ArrayList<>();
List<String> flows = new ArrayList<>();
for (Execution exe : executions) {
List<String> ids = runtimeService.getActiveActivityIds(exe.getId());
flowableIds.addAll(ids);
}
//獲取流程圖
BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());
ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();
ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();
InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", flowableIds, Collections.emptyList(), engconf.getActivityFontName(), engconf.getLabelFontName(), engconf.getAnnotationFontName(), null, 1.0, false);
OutputStream out = null;
byte[] buf = new byte[10240];
int legth = 0;
try {
httpServletResponse.setContentType("image/png"); // 設(shè)置響應(yīng)類型為圖片
out = httpServletResponse.getOutputStream();
while ((legth = in.read(buf)) != -1) {
out.write(buf, 0, legth);
}
} catch (IOException e) {
System.out.println("生成流程圖失敗");
throw new RuntimeException(e);
} finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
}
}4. 測試flowable
如果在yml中配置了 database-schema-update: true 那么初次啟動項(xiàng)目會在控制臺打印的日志中看到很多建表語句,flowable會對不存在的表進(jìn)行創(chuàng)建
我沒有寫前端頁面就全部使用postman進(jìn)行測試,查看流程圖操作我會附著在發(fā)起,審批同意,駁回操作后面
發(fā)起流程


查詢對應(yīng)流程
這里只返回了查詢?nèi)撕腿蝿?wù)ID,其他操作可自行進(jìn)行擴(kuò)展

獲得審批列表
這里只返回了審批人以及流程ID

審批通過
審批通過后會執(zhí)行到流程圖設(shè)置的下一步


駁回
駁回后會返回到流程圖設(shè)置的上一步
先獲取老板的任務(wù)ID
注意:為什么在經(jīng)理審批時(shí)不用回去經(jīng)理的流程ID?因?yàn)榻?jīng)理是提交申請后的第一步,提交申請我直接打印了任務(wù)ID,直接使用就行

進(jìn)行駁回

駁回后又返回到了第一步操作

到此這篇關(guān)于SpringBoot整合flowable及其簡單的使用實(shí)例教程的文章就介紹到這了,更多相關(guān)SpringBoot flowable使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot/Spring?AOP默認(rèn)動態(tài)代理方式實(shí)例詳解
這篇文章主要給大家介紹了關(guān)于SpringBoot/Spring?AOP默認(rèn)動態(tài)代理方式的相關(guān)資料,Spring AOP是一款基于Java的AOP框架,其中默認(rèn)采用動態(tài)代理方式實(shí)現(xiàn)AOP功能,本文將詳細(xì)介紹動態(tài)代理的實(shí)現(xiàn)原理和使用方法,需要的朋友可以參考下2023-03-03
Android開發(fā)Activity管理工具類的操作方法
這篇文章主要介紹了Android開發(fā)Activity管理工具類,下面是一個(gè)完整的Activity管理工具類實(shí)現(xiàn),可以幫助你管理應(yīng)用中的所有Activity,方便一鍵退出應(yīng)用、獲取當(dāng)前Activity等操作,需要的朋友可以參考下2021-02-02
Netty實(shí)現(xiàn)簡易版的RPC框架過程詳解
這篇文章主要為大家介紹了Netty實(shí)現(xiàn)簡易版的RPC框架過程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02
Spring?myBatis數(shù)據(jù)庫連接異常問題及解決
這篇文章主要介紹了Spring?myBatis數(shù)據(jù)庫連接異常問題及解決,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06

