服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - springboot整合flowable框架入门步骤

springboot整合flowable框架入门步骤

2022-11-12 14:30Judy518 Java教程

最近工作中有用到工作流的开发,引入了flowable工作流框架,在此记录一下springboot整合flowable工作流框架的过程,感兴趣的朋友一起看看吧

最近工作中有用到工作流的开发,引入了flowable工作流框架,在此记录一下springboot整合flowable工作流框架的过程,以便后续再次使用到时可以做一些参考使用,如果项目中有涉及到流程审批的话,可以使用该框架帮我们实现流程图示化展示的功能,为了快速了解flowable工作流框架的一个使用过程,我们直接步入主题,springboot整合flowable工作流框架的步骤如下:

1、首先创建一个springboot工程,然后引入flowable pom依赖,代码如下:

?
1
2
3
4
5
<dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-spring-boot-starter</artifactId>
            <version>6.7.0</version>
        </dependency>

2、创建流程图定义文档

这里有一个使用flowable-ui可视化的工程来创建流程图定义文档,https://www.wandouip.com/t5i212543/,具体实施过程如下:

先从 https://github.com/flowable/flowable-engine/releases 上下载一个发布文档,这里选择Flowable 6.7.2 release;然后解压缩文件,将里面的wars文档下的两个jar包(flowable-rest.war、flowable-ui.war)部署到tomcat下,放到webapps文件加下,点击运行,运行存在一个解压缩文件的过程,会产生flowable-rest、flowable-ui文件夹,浏览器输入 http://localhost:8080/flowable-ui,如下图:

springboot整合flowable框架入门步骤

用户名密码输入admin/test,如下图:

springboot整合flowable框架入门步骤

点击建模器应用程序,点击右上角”创建流程“,填写相关信息,进去后就可以可视化地创建流程,如下图:

springboot整合flowable框架入门步骤

上面初步展示了使用可视化制作工具制作流程图的过程,然后导出BPMN2文件,文件内容大概如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
<?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" exporter="Flowable Open Source Modeler" exporterVersion="6.7.2">
  <process id="test" name="test" isExecutable="true">
    <startEvent id="startEvent1" flowable:formFieldValidation="true"></startEvent>
    <userTask id="sid-EE908BC6-94A5-4C9A-B53A-68890D1241BB" name="初级管理员" flowable:formFieldValidation="true"></userTask>
    <sequenceFlow id="sid-BD25A451-66F1-4AF1-B954-5EE5BC5A5782" sourceRef="startEvent1" targetRef="sid-EE908BC6-94A5-4C9A-B53A-68890D1241BB"></sequenceFlow>
    <exclusiveGateway id="sid-57C003F8-C0F3-4499-8B1D-0B29223DB541"></exclusiveGateway>
    <sequenceFlow id="sid-D9571C82-9071-41EA-9858-D10FF50B4396" sourceRef="sid-EE908BC6-94A5-4C9A-B53A-68890D1241BB" targetRef="sid-57C003F8-C0F3-4499-8B1D-0B29223DB541"></sequenceFlow>
    <userTask id="sid-1EF0B969-8092-4EC5-9899-16D066122EC8" name="高级管理员" flowable:formFieldValidation="true"></userTask>
    <sequenceFlow id="sid-7E40EF52-4ED7-4785-954B-3530C3400D81" sourceRef="sid-57C003F8-C0F3-4499-8B1D-0B29223DB541" targetRef="sid-1EF0B969-8092-4EC5-9899-16D066122EC8"></sequenceFlow>
    <intermediateThrowEvent id="sid-13E2B97C-78C9-4A4B-BAD8-A47038668B5F"></intermediateThrowEvent>
    <sequenceFlow id="sid-72B217BF-4F36-446E-B48F-741E3F38B66F" sourceRef="sid-57C003F8-C0F3-4499-8B1D-0B29223DB541" targetRef="sid-13E2B97C-78C9-4A4B-BAD8-A47038668B5F"></sequenceFlow>
    <exclusiveGateway id="sid-DB335453-27CB-4992-824A-3C060312F59F"></exclusiveGateway>
    <sequenceFlow id="sid-60B0EF34-8DB4-421C-A265-59E8A5B7C35F" sourceRef="sid-1EF0B969-8092-4EC5-9899-16D066122EC8" targetRef="sid-DB335453-27CB-4992-824A-3C060312F59F"></sequenceFlow>
    <intermediateThrowEvent id="sid-D6512D07-3215-48FC-A568-227A718EC174"></intermediateThrowEvent>
    <sequenceFlow id="sid-0ABE9D33-F08C-4787-A827-27639909C63A" sourceRef="sid-DB335453-27CB-4992-824A-3C060312F59F" targetRef="sid-D6512D07-3215-48FC-A568-227A718EC174"></sequenceFlow>
    <intermediateThrowEvent id="sid-3F43BAB4-6D7A-4705-A527-D78CF919EEC2"></intermediateThrowEvent>
    <sequenceFlow id="sid-50C31AEE-6B3A-48A2-92D3-7D640E5FF60E" sourceRef="sid-DB335453-27CB-4992-824A-3C060312F59F" targetRef="sid-3F43BAB4-6D7A-4705-A527-D78CF919EEC2"></sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_test">
    <bpmndi:BPMNPlane bpmnElement="test" id="BPMNPlane_test">
      <bpmndi:BPMNShape bpmnElement="startEvent1" id="BPMNShape_startEvent1">
        <omgdc:Bounds height="30.0" width="30.0" x="90.0" y="166.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="sid-EE908BC6-94A5-4C9A-B53A-68890D1241BB" id="BPMNShape_sid-EE908BC6-94A5-4C9A-B53A-68890D1241BB">
        <omgdc:Bounds height="80.0" width="100.0" x="240.0" y="141.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="sid-57C003F8-C0F3-4499-8B1D-0B29223DB541" id="BPMNShape_sid-57C003F8-C0F3-4499-8B1D-0B29223DB541">
        <omgdc:Bounds height="40.0" width="40.0" x="385.0" y="161.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="sid-1EF0B969-8092-4EC5-9899-16D066122EC8" id="BPMNShape_sid-1EF0B969-8092-4EC5-9899-16D066122EC8">
        <omgdc:Bounds height="80.0" width="100.0" x="470.0" y="141.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="sid-13E2B97C-78C9-4A4B-BAD8-A47038668B5F" id="BPMNShape_sid-13E2B97C-78C9-4A4B-BAD8-A47038668B5F">
        <omgdc:Bounds height="30.0" width="30.0" x="390.0" y="270.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="sid-DB335453-27CB-4992-824A-3C060312F59F" id="BPMNShape_sid-DB335453-27CB-4992-824A-3C060312F59F">
        <omgdc:Bounds height="40.0" width="40.0" x="615.0" y="161.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="sid-D6512D07-3215-48FC-A568-227A718EC174" id="BPMNShape_sid-D6512D07-3215-48FC-A568-227A718EC174">
        <omgdc:Bounds height="30.0" width="30.0" x="700.0" y="166.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="sid-3F43BAB4-6D7A-4705-A527-D78CF919EEC2" id="BPMNShape_sid-3F43BAB4-6D7A-4705-A527-D78CF919EEC2">
        <omgdc:Bounds height="30.0" width="30.0" x="620.0" y="270.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="sid-0ABE9D33-F08C-4787-A827-27639909C63A" id="BPMNEdge_sid-0ABE9D33-F08C-4787-A827-27639909C63A" flowable:sourceDockerX="20.5" flowable:sourceDockerY="20.5" flowable:targetDockerX="15.0" flowable:targetDockerY="15.0">
        <omgdi:waypoint x="654.557806573957" y="181.379746835443"></omgdi:waypoint>
        <omgdi:waypoint x="700.0002881553987" y="181.0940234142237"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-72B217BF-4F36-446E-B48F-741E3F38B66F" id="BPMNEdge_sid-72B217BF-4F36-446E-B48F-741E3F38B66F" flowable:sourceDockerX="20.5" flowable:sourceDockerY="20.5" flowable:targetDockerX="15.5" flowable:targetDockerY="3.0">
        <omgdi:waypoint x="405.5" y="200.43965611353715"></omgdi:waypoint>
        <omgdi:waypoint x="405.5" y="270.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-BD25A451-66F1-4AF1-B954-5EE5BC5A5782" id="BPMNEdge_sid-BD25A451-66F1-4AF1-B954-5EE5BC5A5782" flowable:sourceDockerX="15.0" flowable:sourceDockerY="15.0" flowable:targetDockerX="50.0" flowable:targetDockerY="40.0">
        <omgdi:waypoint x="119.94999946593475" y="181.0"></omgdi:waypoint>
        <omgdi:waypoint x="239.9999999999298" y="181.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-D9571C82-9071-41EA-9858-D10FF50B4396" id="BPMNEdge_sid-D9571C82-9071-41EA-9858-D10FF50B4396" flowable:sourceDockerX="50.0" flowable:sourceDockerY="40.0" flowable:targetDockerX="20.5" flowable:targetDockerY="20.5">
        <omgdi:waypoint x="339.9499999999977" y="181.21623376623376"></omgdi:waypoint>
        <omgdi:waypoint x="385.4130434782609" y="181.41304347826087"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-7E40EF52-4ED7-4785-954B-3530C3400D81" id="BPMNEdge_sid-7E40EF52-4ED7-4785-954B-3530C3400D81" flowable:sourceDockerX="20.5" flowable:sourceDockerY="20.5" flowable:targetDockerX="50.0" flowable:targetDockerY="40.0">
        <omgdi:waypoint x="424.52473707274277" y="181.41666666666666"></omgdi:waypoint>
        <omgdi:waypoint x="469.99999999999386" y="181.21812227074238"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-60B0EF34-8DB4-421C-A265-59E8A5B7C35F" id="BPMNEdge_sid-60B0EF34-8DB4-421C-A265-59E8A5B7C35F" flowable:sourceDockerX="50.0" flowable:sourceDockerY="40.0" flowable:targetDockerX="20.5" flowable:targetDockerY="20.5">
        <omgdi:waypoint x="569.9499999999981" y="181.21623376623378"></omgdi:waypoint>
        <omgdi:waypoint x="615.4130434782609" y="181.41304347826087"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-50C31AEE-6B3A-48A2-92D3-7D640E5FF60E" id="BPMNEdge_sid-50C31AEE-6B3A-48A2-92D3-7D640E5FF60E" flowable:sourceDockerX="20.5" flowable:sourceDockerY="20.5" flowable:targetDockerX="15.0" flowable:targetDockerY="15.0">
        <omgdi:waypoint x="635.4077669902913" y="200.53271096023283"></omgdi:waypoint>
        <omgdi:waypoint x="635.072221397189" y="270.00017140256364"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

以上初步实现怎么使用可视化工具来创建流程图定义文件,里面更为具体的使用方法还有待探索,上面的步骤执行完毕后,我们就可以运行我们的springboot工程了,在配置文件中配置好数据库链接相关的信息后,然后配置flowable相关信息:

flowable:
  aysnc-executor-activate: false
  database-schema-update: true
  process-definition-location-prefix: classpath*:/processes/
  process-definition-location-suffixes: "**.bpmn20.xml, **.bpmn"

上面的信息配置完毕后,就可以运行我们的工程了,在运行工程之前,在工程的resources文件夹下创建一个processes文件夹,我们的流程定义文档就放在这个文件夹下,创建一个process-test.bpmn20.xml放到processes文件夹下,具体内容定义如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
<?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:activiti="http://activiti.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.activiti.org/test">
  <process id="processTest" name="Request Resource Approval " isExecutable="true">
    <startEvent id="starter" name="Starter"></startEvent>
    <serviceTask id="sendJuniorRejectEmail" name="发送初级审批拒绝邮件"
                 activiti:class="com.chinaums.web.controller.flowable2.delegate.SendJuniorRejectionMailDelegate">
    </serviceTask>
    <endEvent id="juniorRejectEnd" name="Junior Reject End"></endEvent>
    <sequenceFlow id="flow5" sourceRef="sendJuniorRejectEmail" targetRef="juniorRejectEnd"></sequenceFlow>
    <userTask id="seniorApproval" name="高级审批" activiti:assignee="${seniorAdmin}"></userTask>
    <userTask id="juniorApproval" name="初级审批" activiti:assignee="${juniorAdmin}"></userTask>
    <exclusiveGateway id="exclusivegateway1" name="Exclusive Gateway1"></exclusiveGateway>
    <sequenceFlow id="juniorSuccessFlow" name="同意" sourceRef="exclusivegateway1" targetRef="seniorApproval">
      <conditionExpression xsi:type="tFormalExpression">
        <![CDATA[${approved=='Y'}]]>
      </conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="juniorRejectFlow" name="拒绝" sourceRef="exclusivegateway1" targetRef="sendJuniorRejectEmail">
      <conditionExpression xsi:type="tFormalExpression">
        <![CDATA[${approved=='N'}]]>
      </conditionExpression>
    </sequenceFlow>
    <exclusiveGateway id="exclusivegateway2" name="Exclusive Gateway2"></exclusiveGateway>
    <sequenceFlow id="flow7" sourceRef="seniorApproval" targetRef="exclusivegateway2"></sequenceFlow>
    <endEvent id="approvalSuccessEnd" name="Approval Success End"></endEvent>
    <sequenceFlow id="seniorSuccessFlow" name="同意" sourceRef="exclusivegateway2" targetRef="sendApprovalSuccessEmail">
      <conditionExpression xsi:type="tFormalExpression">
        <![CDATA[${approved=='Y'}]]>
      </conditionExpression>
    </sequenceFlow>
    <serviceTask id="sendSeniorRejectEmail" name="发送高级审批拒绝邮件"
                 activiti:class="com.chinaums.web.controller.flowable2.delegate.SendSeniorRejectionMailDelegate">
    </serviceTask>
    <endEvent id="seniorRejectEnd" name="Senior Reject End"></endEvent>
    <sequenceFlow id="seniorRejectFlow" name="拒绝" sourceRef="exclusivegateway2" targetRef="sendSeniorRejectEmail">
      <conditionExpression xsi:type="tFormalExpression">
        <![CDATA[${approved=='N'}]]>
      </conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow9" sourceRef="sendSeniorRejectEmail" targetRef="seniorRejectEnd"></sequenceFlow>
    <sequenceFlow id="flow11" sourceRef="juniorApproval" targetRef="exclusivegateway1"></sequenceFlow>
    <sequenceFlow id="flow12" sourceRef="starter" targetRef="juniorApproval"></sequenceFlow>
    <serviceTask id="sendApprovalSuccessEmail" name="发送审批通过邮件"
                 activiti:class="com.chinaums.web.controller.flowable2.delegate.SendApprovalSuccessEmailDelegate"></serviceTask>
    <sequenceFlow id="flow13"
                  sourceRef="sendApprovalSuccessEmail"
                  targetRef="approvalSuccessEnd"></sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_requestResourceApprovalProcess">
    <bpmndi:BPMNPlane bpmnElement="requestResourceApprovalProcess" id="BPMNPlane_requestResourceApprovalProcess">
      <bpmndi:BPMNShape bpmnElement="starter" id="BPMNShape_starter">
        <omgdc:Bounds height="35.0" width="35.0" x="45.0" y="118.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="sendJuniorRejectEmail" id="BPMNShape_sendJuniorRejectEmail">
        <omgdc:Bounds height="71.0" width="171.0" x="340.0" y="230.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="juniorRejectEnd" id="BPMNShape_juniorRejectEnd">
        <omgdc:Bounds height="35.0" width="35.0" x="408.0" y="385.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="seniorApproval" id="BPMNShape_seniorApproval">
        <omgdc:Bounds height="78.0" width="121.0" x="575.0" y="95.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="juniorApproval" id="BPMNShape_juniorApproval">
        <omgdc:Bounds height="81.0" width="115.0" x="170.0" y="95.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="exclusivegateway1" id="BPMNShape_exclusivegateway1">
        <omgdc:Bounds height="40.0" width="40.0" x="405.0" y="113.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="exclusivegateway2" id="BPMNShape_exclusivegateway2">
        <omgdc:Bounds height="40.0" width="40.0" x="765.0" y="115.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="approvalSuccessEnd" id="BPMNShape_approvalSuccessEnd">
        <omgdc:Bounds height="35.0" width="35.0" x="1140.0" y="117.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="sendSeniorRejectEmail" id="BPMNShape_sendSeniorRejectEmail">
        <omgdc:Bounds height="71.0" width="192.0" x="690.0" y="230.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="seniorRejectEnd" id="BPMNShape_seniorRejectEnd">
        <omgdc:Bounds height="35.0" width="35.0" x="768.0" y="385.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="sendApprovalSuccessEmail" id="BPMNShape_sendApprovalSuccessEmail">
        <omgdc:Bounds height="75.0" width="141.0" x="920.0" y="96.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="flow5" id="BPMNEdge_flow5">
        <omgdi:waypoint x="425.0" y="301.0"></omgdi:waypoint>
        <omgdi:waypoint x="425.0" y="385.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="juniorSuccessFlow" id="BPMNEdge_juniorSuccessFlow">
        <omgdi:waypoint x="445.0" y="133.0"></omgdi:waypoint>
        <omgdi:waypoint x="575.0" y="134.0"></omgdi:waypoint>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="16.0" width="32.0" x="445.0" y="133.0"></omgdc:Bounds>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="juniorRejectFlow" id="BPMNEdge_juniorRejectFlow">
        <omgdi:waypoint x="425.0" y="153.0"></omgdi:waypoint>
        <omgdi:waypoint x="425.0" y="230.0"></omgdi:waypoint>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="16.0" width="32.0" x="425.0" y="153.0"></omgdc:Bounds>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow7" id="BPMNEdge_flow7">
        <omgdi:waypoint x="696.0" y="134.0"></omgdi:waypoint>
        <omgdi:waypoint x="765.0" y="135.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="seniorSuccessFlow" id="BPMNEdge_seniorSuccessFlow">
        <omgdi:waypoint x="805.0" y="135.0"></omgdi:waypoint>
        <omgdi:waypoint x="920.0" y="133.0"></omgdi:waypoint>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="16.0" width="32.0" x="805.0" y="135.0"></omgdc:Bounds>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="seniorRejectFlow" id="BPMNEdge_seniorRejectFlow">
        <omgdi:waypoint x="785.0" y="155.0"></omgdi:waypoint>
        <omgdi:waypoint x="786.0" y="230.0"></omgdi:waypoint>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="16.0" width="32.0" x="785.0" y="155.0"></omgdc:Bounds>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow9" id="BPMNEdge_flow9">
        <omgdi:waypoint x="786.0" y="301.0"></omgdi:waypoint>
        <omgdi:waypoint x="785.0" y="385.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow11" id="BPMNEdge_flow11">
        <omgdi:waypoint x="285.0" y="135.0"></omgdi:waypoint>
        <omgdi:waypoint x="405.0" y="133.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow12" id="BPMNEdge_flow12">
        <omgdi:waypoint x="80.0" y="135.0"></omgdi:waypoint>
        <omgdi:waypoint x="170.0" y="135.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow13" id="BPMNEdge_flow13">
        <omgdi:waypoint x="1061.0" y="133.0"></omgdi:waypoint>
        <omgdi:waypoint x="1140.0" y="134.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

上面文件中的process标签的id值是该流程定义的唯一标识,在创建流程实例时需要传入processId标识,上面的assignee变量后面定义了一个变量${seniorAdmin},这个是由接口调用时传入的,activiti:class后面的值是一个类,表示执行到这个步骤时会触发执行某个动作,比如id为sendJuniorRectEmail的serviceTask中的class定义如下:

?
1
2
3
4
5
6
7
8
9
10
@Slf4j
public class SendJuniorRejectionMailDelegate implements JavaDelegate {
    @Override
    public void execute(DelegateExecution execution) {
        String requestUser = (String) execution.getVariable("requestUser");
        String resourceId = (String) execution.getVariable("resourceId");
        System.out.println("SendJuniorRejectionMailDelegate");
        log.info("send approval success mail for user [" + requestUser + "] with apply resource [" + resourceId + "]");
    }
}

上面的类需要实现JavaDelegate这个接口,上面的内容定义完毕后,就可以定义我们的实现方法了,先创建一个接口,定义一些方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
public interface IProcess {
 
    /**
     * 创建一个流程实例,创建实例时会登记一些信息,这些信息可以通过调用
     * queryProcessVariables方法获取到,调用时需要传递processInstanceId
     * @param paramObj
     * @return
     */
    ProcessInstanceEntity startProcess(ParamObj paramObj);
 
    /**
     * 获取指定工作人的代办任务
     * @param assignee
     * @return
     */
    List<TaskInstanceEntity> taskInstance(String assignee);
 
    /**
     * 处理工作
     * @param paramObj
     */
    void handleTask(ParamObj paramObj);
 
    /**
     * 获取某个流程实体的状态,各个审批环节所处的状态信息
     * @param processInstanceId
     * @return
     */
    List<ProcessStatusEntity> queryProcessStatus(String processInstanceId);
 
    /**
     * 查看创建流程实例时登记的变量信息
     * @param processInstanceId
     * @return
     */
    Map<String,Object> queryProcessVariables(String processInstanceId);
 
    /**
     * 获取某人的历史审批数据
     * @param assignee
     * @return
     */
    List<HistanceInstanceEntity> queryHistoryProcess(String assignee);
 
    /**
     * 生成流程的图谱
     * @param httpServletResponse
     * @param processInstanceId
     */
    void genProcessDiagram(HttpServletResponse httpServletResponse, String processInstanceId) throws Exception;
 
    /**
     * 查询是否存在历史数据的流程实例
     * @param processInstanceId
     * @return
     */
    boolean isExistHistoricProcessInstance(String processInstanceId);
 
    /**
     * 查询指定的流程是否是运行中的流程
     * @param processInstanceId
     * @return
     */
    boolean isExistRunningProcessInstance(String processInstanceId);
 
    /**
     * 将指定的流程挂起
     * @param processInstanceId
     */
    void suspendProcessInstance(String processInstanceId);
 
    /**
     * 终止项目流程
     * @param paramObj
     */
    void terminateProcessInstance(ParamObj paramObj);
 
    /**
     * 将指定的流程激活
     * @param processInstanceId
     */
    void activateProcessInstance(String processInstanceId);
 
    /**
     * 删除流程实例
     * @param paramObj
     */
    void deleteProcessInstance(ParamObj paramObj);
 
    /**
     * 将任务返回到某一步骤
     * @param taskId
     * @param targetTaskKey 返回到的目标任务ID
     */
    void rollbackTask(String taskId, String targetTaskKey);
 
    boolean isProcessFinished(String processInstanceId);
}

定义好接口后,再定义一个实现类:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
@Service
public class IProcessImpl implements IProcess {
    @Autowired
    private RepositoryService repositoryService;
    private RuntimeService runtimeService;
    private TaskService taskService;
    private HistoryService historyService;
    private ProcessEngine processEngine;
    ManagementService managementService;
    @Override
    public ProcessInstanceEntity startProcess(ParamObj paramObj) {
        Map<String, Object> variables = new HashMap<>();
        // 请求的资源ID
        variables.put("resourceId", paramObj.getResourceId());
        // 请求发起用户
        variables.put("requestUser", paramObj.getRequestUser());
        // 初级审批用户
        variables.put("juniorAdmin", paramObj.getJuniorAdmin());
        // 高级审批用户
        variables.put("seniorAdmin", paramObj.getSeniorAdmin());
        ProcessInstance processInstance=runtimeService.
                startProcessInstanceByKey(ConstantValues.FLOWABLE_PROCESS_TEST, variables);
        ProcessInstanceEntity entity=new ProcessInstanceEntity();
        entity.setProcessDeploymentId(processInstance.getDeploymentId());
        entity.setProcessInstanceId(processInstance.getProcessInstanceId());
        entity.setActivityId(processInstance.getActivityId());
        return entity;
    }
    public List<TaskInstanceEntity> taskInstance(String assignee) {
        List<TaskInstanceEntity> entities=new ArrayList<>();
        List<Task> tasks= taskService.createTaskQuery().taskAssignee(assignee).orderByTaskCreateTime().desc().list();
        if(!CollectionUtils.isEmpty(tasks)){
            tasks.stream().forEach(task -> {
                TaskInstanceEntity entity=new TaskInstanceEntity();
                String id=task.getId();
                entity.setCreateTime(task.getCreateTime());
                entity.setTaskName(task.getName());
                entity.setProcessInstanceId(task.getProcessInstanceId());
                entity.setTaskId(id);
                Map<String, Object> processVariables = taskService.getVariables(id);
                entity.setRequestUser(processVariables.get("requestUser").toString());
                entity.setResourceId(processVariables.get("resourceId").toString());
                entities.add(entity);
            });
        }
        return entities;
    public void handleTask(ParamObj paramObj) {
        Map<String, Object> taskVariables = new HashMap<>();
        String approved=paramObj.isApproved()?"Y":"N";
        taskVariables.put("approved", approved);
        //审核结果和审核意见都封装为JSON然后放在评论里,后续需要进行逆操作。
        ObjectMapper objectMapper = new ObjectMapper();
        Map<String, String> map= new HashMap<>();
        map.put("approved", approved);
        map.put("comment", paramObj.getComment());
        try {
            String json = objectMapper.writeValueAsString(map);
            taskService.addComment(paramObj.getTaskId(), null, json);
            taskService.complete(paramObj.getTaskId(), taskVariables);
        } catch (Exception e) {
            throw new RuntimeException(e);
    public List<ProcessStatusEntity> queryProcessStatus(String processInstanceId) {
        List<ProcessStatusEntity> result = new ArrayList<>();
        List<HistoricTaskInstance> historicTaskInstances = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId).list();
        if(CollectionUtils.isEmpty(historicTaskInstances)) {
            throw new RuntimeException("Process instance [" + processInstanceId + "] not exist");
        for (HistoricTaskInstance hti : historicTaskInstances) {
            String taskId = hti.getId();
            String taskName = hti.getName();
            String assignee = hti.getAssignee();
            Date createTime = hti.getCreateTime();
            String comment = null;
            String approved=null;
            List<Comment> comments = taskService.getTaskComments(taskId);
            if (!CollectionUtils.isEmpty(comments)) {
                comment = comments.get(0).getFullMessage();
                if(null!=comment) {
                    //这里进行评论的JSON数据的逆操作提取数据
                    ObjectMapper mapper = new ObjectMapper();
                    try {
                        Map<String,Object> data = mapper.readValue(comment, Map.class);
                        approved=data.get("approved").toString();
                        comment=data.get("comment").toString();
                    } catch (Exception e) {
                        System.out.println(e.toString());
                    }
                }
            }
            ProcessStatusEntity pd=new ProcessStatusEntity();
            pd.setTaskName(taskName);
            pd.setAssignee(assignee);
            pd.setCreateTime(createTime);
            pd.setApproved(approved);
            pd.setComment(comment);
            pd.setTaskId(hti.getId());
            pd.setProcessInstanceId(hti.getProcessInstanceId());
            result.add(pd);
        return result;
    public Map<String, Object> queryProcessVariables(String processInstanceId) {
        List<HistoricVariableInstance> historicVariableInstances =
                historyService.createHistoricVariableInstanceQuery()
        if (historicVariableInstances == null) {
        Map<String,Object> ret= new HashMap<>();
        for(HistoricVariableInstance var: historicVariableInstances) {
            ret.put(var.getVariableName(), var.getValue());
        return ret;
    public List<HistanceInstanceEntity> queryHistoryProcess(String assignee) {
        List<HistanceInstanceEntity> result=new ArrayList<>();
        List<HistoricActivityInstance> activities = historyService.createHistoricActivityInstanceQuery()
                .taskAssignee(assignee).finished().orderByHistoricActivityInstanceEndTime().desc().list();
        for(HistoricActivityInstance h : activities) {
            HistanceInstanceEntity d=new HistanceInstanceEntity();
            d.setProcessInstanceId(h.getProcessInstanceId());
            d.setTaskId(h.getTaskId());
            d.setStartTime(h.getStartTime());
            d.setEndTime(h.getEndTime());
            result.add(d);
    public void genProcessDiagram(HttpServletResponse httpServletResponse,
                                  String processInstanceId) throws Exception{
        ProcessInstance pi = runtimeService.createProcessInstanceQuery().
                processInstanceId(processInstanceId).singleResult();
        //流程走完的不显示图
        if (pi == null) {
//            System.out.println("不存在该流程或则流程已经走完");
            throw new RuntimeException("不存在该流程或则流程已经走完");
//            return;
        Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
        //使用流程实例ID,查询正在执行的执行对象表,返回流程实例对象
        String InstanceId = task.getProcessInstanceId();
        List<Execution> executions = runtimeService
                .createExecutionQuery()
                .processInstanceId(InstanceId)
                .list();
        //得到正在执行的Activity的Id
        List<String> activityIds = new ArrayList<>();
        List<String> flows = new ArrayList<>();
        for (Execution exe : executions) {
            List<String> ids = runtimeService.getActiveActivityIds(exe.getId());
            activityIds.addAll(ids);
        //获取流程图
        BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());
        ProcessEngineConfiguration engineConf = processEngine.getProcessEngineConfiguration();
        ProcessDiagramGenerator diagramGenerator = engineConf.getProcessDiagramGenerator();
        InputStream in = diagramGenerator.generateDiagram(bpmnModel,
                "png",
                activityIds,
                flows,
                engineConf.getActivityFontName(),
                engineConf.getLabelFontName(),
                engineConf.getAnnotationFontName(),
                engineConf.getClassLoader(),
                1.0,true);
        OutputStream out = null;
        byte[] buf = new byte[1024];
        int length = 0;
            out = httpServletResponse.getOutputStream();
            while ((length = in.read(buf)) != -1) {
                out.write(buf, 0, length);
        } finally {
            if (in != null) {
                in.close();
            if (out != null) {
                out.close();
    public boolean isExistHistoricProcessInstance(String processInstanceId) {
        HistoricProcessInstance historicProcessInstance =
                historyService.createHistoricProcessInstanceQuery().
                        processInstanceId(processInstanceId).singleResult();
        if (historicProcessInstance == null) {
            return false;
        return true;
    public boolean isExistRunningProcessInstance(String processInstanceId) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().
        if (processInstance == null) {
    public void suspendProcessInstance(String processInstanceId) {
        runtimeService.suspendProcessInstanceById(processInstanceId);
    public void terminateProcessInstance(ParamObj paramObj) {
        runtimeService.deleteProcessInstance(paramObj.getProcessInstanceId(),paramObj.getDeleteReason());
    public void activateProcessInstance(String processInstanceId) {
        runtimeService.activateProcessInstanceById(processInstanceId);
    public void deleteProcessInstance(ParamObj paramObj) {
        //查询是否操作
        long count = runtimeService.createExecutionQuery().processInstanceId(paramObj.getProcessInstanceId()).count();
        if(count>0){
            DeleteFlowableProcessInstanceCmd cmd=
                    new DeleteFlowableProcessInstanceCmd(paramObj.getProcessInstanceId(),
                            paramObj.getDeleteReason(),true);
            managementService.executeCommand(cmd);
            //runtimeService.deleteProcessInstance(processInstanceId,deleteReason);
        }else{
            //删除历史数据的流程实体
            historyService.deleteHistoricProcessInstance(paramObj.getProcessInstanceId());
    public void rollbackTask(String taskId, String targetTaskKey) {
        Task currentTask = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (currentTask == null) {
            return ;
        List<String> key = new ArrayList<>();
        key.add(currentTask.getTaskDefinitionKey());
        runtimeService.createChangeActivityStateBuilder()
                .processInstanceId(currentTask.getProcessInstanceId())
                .moveActivityIdsToSingleActivityId(key, targetTaskKey)
                .changeState();
    public boolean isProcessFinished(String processInstanceId) {
        return historyService.createHistoricProcessInstanceQuery().finished()
                .processInstanceId(processInstanceId).count()>0;
}

实现类中注入的变量都是flowable框架中的变量,实现类中的方法的作用在接口中都有向相关注释,其中deleteProcessInstance方法中会引用一个类来删除流程实例,DeleteFlowableProcessInstanceCmd类的定义如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@Data
public class DeleteProcessInstanceCmd implements Command<Void>, Serializable {
    String processInstanceId;
    String deleteReason;
    //是否删除历史
    boolean cascade=true;
    public DeleteProcessInstanceCmd(){
    }
    public DeleteProcessInstanceCmd(String processInstanceId,String deleteReason){
        this.deleteReason=deleteReason;
        this.processInstanceId=processInstanceId;
    }
    public DeleteProcessInstanceCmd(String processInstanceId,
                                    String deleteReason,
                                    boolean cascade){
        this.deleteReason=deleteReason;
        this.processInstanceId=processInstanceId;
        this.cascade=cascade;
    }
    @Override
    public Void execute(CommandContext commandContext) {
        ExecutionEntity entity= CommandContextUtil.getExecutionEntityManager(commandContext)
                .findById(processInstanceId);
        if(entity!=null){
            if(entity.isDeleted()){
                return null;
            }
            if(Flowable5Util.isFlowable5ProcessDefinitionId(commandContext,entity.getProcessDefinitionId())){
                Flowable5CompatibilityHandler handler=Flowable5Util.getFlowable5CompatibilityHandler();
                handler.deleteProcessInstance(processInstanceId,deleteReason);
            }else{
                CommandContextUtil.getExecutionEntityManager(commandContext).deleteProcessInstance(entity.getProcessInstanceId(),deleteReason,cascade);
            }
        }
        return null;
    }
}

上述功能了类定义完毕后,就可以创建我们的controller类了,我们的controller类的定义如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
@RestController
@RequestMapping("/flowableTest")
public class FlowableController {
    @Autowired
    IProcess process;
    @PostMapping("/startProcess")
    public ProcessInstanceEntity startProcess(@RequestBody ParamObj paramObj){
        return process.startProcess(paramObj);
    }
    @GetMapping("/getTaskInstance/{assignee}")
    public List<TaskInstanceEntity> taskInstance(@PathVariable("assignee") String assignee){
        return process.taskInstance(assignee);
    }
    @PutMapping("/handleTask")
    public String handleTask(@RequestBody ParamObj paramObj){
        process.handleTask(paramObj);
        return "success";
    }
    @GetMapping("/queryProcessStatus")
    public List<ProcessStatusEntity> queryProcessStatus(String processInstanceId){
        return process.queryProcessStatus(processInstanceId);
    }
    @GetMapping("/queryProcessVariables")
    public Map<String, Object> queryProcessVariables(String processInstanceId){
        return process.queryProcessVariables(processInstanceId);
    }
    @GetMapping("/queryHistoryProcess")
    public List<HistanceInstanceEntity> queryHistoryProcess(String assignee){
        return process.queryHistoryProcess(assignee);
    }
    @GetMapping("/genProcessDiagram")
    public void genProcessDiagram(HttpServletResponse httpServletResponse,
                                  String processInstanceId) throws Exception {
        process.genProcessDiagram(httpServletResponse,processInstanceId);
    }
    @GetMapping("/isExistHistoricProcessInstance")
    public boolean isExistHistoricProcessInstance(String processInstanceId){
        return process.isExistHistoricProcessInstance(processInstanceId);
    }
    @GetMapping("/isProcessFinished")
    public boolean isProcessFinished(String processInstanceId){
        return process.isProcessFinished(processInstanceId);
    }
    @GetMapping("/isExistRunningProcessInstance")
    public boolean isExistRunningProcessInstance(String processInstanceId){
        return process.isExistRunningProcessInstance(processInstanceId);
    }
    @PutMapping("/suspendProcessInstance")
    public String suspendProcessInstance(String processInstanceId){
        process.suspendProcessInstance(processInstanceId);
        return "流程 "+processInstanceId+" 已经挂起";
    }
    @PutMapping("/terminateProcessInstance")
    public String terminateProcessInstance(ParamObj paramObj){
        process.terminateProcessInstance(paramObj);
        return "流程 "+paramObj.getProcessInstanceId()+" 已经终止";
    }
    @PutMapping("/activateProcessInstance")
    public String activateProcessInstance(String processInstanceId) {
        process.activateProcessInstance(processInstanceId);
        return "流程 "+processInstanceId+" 已经激活";
    }
    @PutMapping("/deleteProcessInstance")
    public String deleteProcessInstance(ParamObj paramObj){
        process.deleteProcessInstance(paramObj);
        return "流程 "+paramObj.getProcessInstanceId()+" 已经删除";
    }
    @PutMapping("/rollback")
    public String rollbackTask(String taskId, String targetTaskKey){
        process.rollbackTask(taskId,targetTaskKey);
        return "流程回退成功";
    }
}

试运行工程,运行工程后,系统会自动生成一些表单,是一些act_和flw_开头的表单,如下,利用postman创建一个流程实例:

springboot整合flowable框架入门步骤

查看流程示例图:

springboot整合flowable框架入门步骤

调用查看任务接口,可以查看某个人有哪些任务需要处理:

springboot整合flowable框架入门步骤

调用任务处理接口:

springboot整合flowable框架入门步骤

查看处理后的结果:

springboot整合flowable框架入门步骤

以上是flowable流程框架的简单应用,更为详细的使用等待后续的挖掘......

到此这篇关于springboot整合flowable-初步入门的文章就介绍到这了,更多相关springboot整合flowable内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://www.cnblogs.com/codeMedita/p/15972476.html

延伸 · 阅读

精彩推荐
  • Java教程springmvc HttpServletRequest 如何获取c:forEach的值

    springmvc HttpServletRequest 如何获取c:forEach的值

    这篇文章主要介绍了springmvc HttpServletRequest 如何获取c:forEach的值方法,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝...

    小菜91111682021-11-21
  • Java教程Java实现邮件发送功能

    Java实现邮件发送功能

    这篇文章主要为大家详细介绍了Java实现邮件发送功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    Marvellous丶9952021-11-13
  • Java教程SpringBoot上传图片到指定位置并返回URL的实现

    SpringBoot上传图片到指定位置并返回URL的实现

    本文主要介绍了SpringBoot上传图片到指定位置并返回URL,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    七旅之言8672022-10-11
  • Java教程java将XML文档转换成json格式数据的示例

    java将XML文档转换成json格式数据的示例

    本篇文章主要介绍了java将XML文档转换成json格式数据的示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    zhchoutai16032021-02-28
  • Java教程浅谈自定义校验注解ConstraintValidator

    浅谈自定义校验注解ConstraintValidator

    鉴于通用性和普遍性,Spring框架提供了validator组件,通过一些校验器,可以对一些数据进行统一的完整性和有效性等校验,即简单又好用...

    陈皮的JavaLib5562021-09-18
  • Java教程JPA 加锁机制及@Version版本控制方式

    JPA 加锁机制及@Version版本控制方式

    这篇文章主要介绍了JPA 加锁机制及@Version版本控制方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...

    black-ant5332022-02-20
  • Java教程java中四种操作xml方式的比较

    java中四种操作xml方式的比较

    本文主要介绍了java中四种操作xml的方式并对它们进行比较分析。具有很好的参考价值。下面跟着小编一起来看下吧 ...

    互联网开发者3632020-08-30
  • Java教程java Thumbnails 图片处理的使用

    java Thumbnails 图片处理的使用

    这篇文章主要介绍了java Thumbnails 图片处理的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面...

    浮生夢11792021-08-19