欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

仿釘釘流程輕松實(shí)現(xiàn)JSON轉(zhuǎn)BPMN完整實(shí)現(xiàn)過程示例

 更新時(shí)間:2023年08月18日 09:24:39   作者:pq217  
這篇文章主要為大家介紹了仿釘釘流程輕松實(shí)現(xiàn)JSON轉(zhuǎn)BPMN完整實(shí)現(xiàn)過程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

寫過工作流都會(huì)遇到這樣的難題,希望流程的設(shè)計(jì)可以類似釘釘一樣簡單明了,而不是超級不有好的bpmn設(shè)計(jì)器,上網(wǎng)大概搜了一下實(shí)現(xiàn)方案,前端仿釘釘設(shè)計(jì)器一大堆,例如wflow,smart-flow-design,參照這些源碼前端設(shè)計(jì)器不成問題

問題在于這樣的設(shè)計(jì)器數(shù)據(jù)是json格式,不符合bpmn協(xié)議,就無法和activiti,flowable等工作流直接對接

如果自己開發(fā)工作流引擎,但開發(fā)成本肯定比較大,所以還是希望能實(shí)現(xiàn)自定義的json和xml可以轉(zhuǎn)換

方案

轉(zhuǎn)換這個(gè)活可以前端干,也可以后端干,如果前端干可以使用bpmn-moddle,bpmn.js就是使用它生成的xml,但大概看了一下發(fā)現(xiàn)文檔稀缺,使用很起來很難

最終決定使用java轉(zhuǎn)換,因?yàn)榘l(fā)現(xiàn)activiti包中的BpmnModel可以很輕松畫出xml,而且基本不用看文檔,.方法名基本就能和bpmn協(xié)議對上號

json協(xié)議

前后端使用json來表達(dá)流程設(shè)計(jì),那一定要訂一套自己的協(xié)議,大概按照smart-flow-design寫一個(gè)簡版的

smart-flow-design

{
  "id": "節(jié)點(diǎn)id",
  "name": "節(jié)點(diǎn)名稱",
  "type": "申請節(jié)點(diǎn)/審核節(jié)點(diǎn)/分支節(jié)點(diǎn)/抄送節(jié)點(diǎn)",
  "next": "下一節(jié)點(diǎn)",
  "exclusive": [// 排他條件
    {
      // 條件
      "condition": "條件表達(dá)式",
      //分支節(jié)點(diǎn)內(nèi)部流程
      "process": {}
    }
  ],
  // 委派人
  "assignee": {
    "users": [],
    "multiMode": "會(huì)簽/順序?qū)徟?
  },
  // 表單權(quán)限
  "formPerms": [
    {
      "key": "字段key",
      "perm": "權(quán)限類型 編輯/只讀/隱藏",
      "required": true
    }
  ]
}

節(jié)點(diǎn)類型簡單實(shí)現(xiàn)幾個(gè):申請節(jié)點(diǎn)/審核節(jié)點(diǎn)/分支節(jié)點(diǎn)/抄送節(jié)點(diǎn)

通過next指向下一節(jié)點(diǎn),實(shí)現(xiàn)一個(gè)鏈表結(jié)構(gòu)

如一個(gè)簡單的流程設(shè)計(jì)如下

簡單工作流

對應(yīng)的json數(shù)據(jù)如下

{
  "id": "1",
  "name": "申請節(jié)點(diǎn)",
  "type": "ROOT",
  "next": {
    "id": "2",
    "name": "審批節(jié)點(diǎn)",
    "type": "APPROVAL",
    "next": {
      "id": "3",
      "name": "抄送節(jié)點(diǎn)",
      "type": "CC"
    }
  }
}

帶分支的設(shè)計(jì)如下

分支工作流

對應(yīng)的json:

{
  "id": "1",
  "name": "申請節(jié)點(diǎn)",
  "type": "ROOT",
  "next": {
    "id": "2",
    "name": "條件節(jié)點(diǎn)",
    "type": "EXCLUSIVE",
    "exclusive": [
      {
        "condition": "amount>=100"
      },
      {
        "condition": "amount<100",
        "process": {
          "id": "4",
          "name": "審批人1",
          "type": "APPROVAL",
          "next": null
        }
      }
    ],
    "next": {
      "id": "3",
      "name": "審批人2",
      "type": "APPROVAL"
    }
  }
}

基本上這個(gè)json數(shù)據(jù)結(jié)構(gòu)就足夠標(biāo)識很多場景了,分支條件可以自己再寫復(fù)雜一點(diǎn),如果需要擴(kuò)展新增屬性即可

java

java 創(chuàng)建一些實(shí)體來接受json,很簡單就不詳細(xì)寫了,大概如下

@Data
public class ProcessNode {
    @ApiModelProperty(value = "節(jié)點(diǎn)ID")
    private String id;
    @ApiModelProperty(value = "節(jié)點(diǎn)名稱")
    private String name;
    @ApiModelProperty(value = "節(jié)點(diǎn)類型")
    private String type;
    @ApiModelProperty(value = "下一節(jié)點(diǎn)")
    private ProcessNode next;
    @ApiModelProperty(value = "分支")
    private List<ExclusiveBranch> exclusive;
    @ApiModelProperty(value = "委托人")
    private Assignee assignee;
    @ApiModelProperty(value = "表單權(quán)限")
    private List<FormPerm> formPerms;
}
@Data
public class ExclusiveBranch {
    @ApiModelProperty(value = "id")
    private String id;
    @ApiModelProperty(value = "分支條件")
    private String condition;
    @ApiModelProperty(value = "分支內(nèi)部流程")
    private ProcessNode process;
}
@Data
public class Assignee {
    @ApiModelProperty(value = "委托人列表")
    private List<String> users;
    @ApiModelProperty(value = "多人審批方式")
    private String multiMode;
}

在controller使用@RequestBody接受一下前端傳來的json即可

轉(zhuǎn)BPMN

接下來就把這個(gè)java實(shí)體轉(zhuǎn)成xml,引入今天的主角:BpmnModel

引入依賴

<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-bpmn-model</artifactId>
    <version>7.1.0.M1</version>
</dependency>

即可開始使用BpmnModel開始繪制bpmn協(xié)議的工作流

初始化

首先準(zhǔn)備工作流

BpmnModel model = new BpmnModel();
Process process = new Process();
model.addProcess(process);
process.setId("Process_"+UUID.randomUUID());
process.setExecutable(true);

其中process就相當(dāng)于我們的圖紙,后續(xù)工作就是往這個(gè)圖紙上畫節(jié)點(diǎn)和線

繪制開始結(jié)束

由于json協(xié)議中不包含開始結(jié)束節(jié)點(diǎn),所以首先要繪制出兩個(gè)節(jié)點(diǎn)

開始節(jié)點(diǎn)

// 新建開始節(jié)點(diǎn)
StartEvent startEvent = new StartEvent();
startEvent.setId("_start");
// 繪制到圖紙
process.addFlowElement(startEvent)

結(jié)束節(jié)點(diǎn)

// 新建結(jié)束節(jié)點(diǎn)
EndEvent endEvent = new EndEvent();
endEvent.setId("_end");
// 繪制到圖紙
process.addFlowElement(endEvent)

到此兩個(gè)節(jié)點(diǎn)就畫出來了,但是還沒有任何線

繪制bpmn

接下來就根據(jù)json的節(jié)點(diǎn)來繪制bpmn節(jié)點(diǎn),同時(shí)還要考慮線的繪制節(jié)點(diǎn)的連接線

json協(xié)議中是next指向下一節(jié)點(diǎn),所以繪制節(jié)點(diǎn)的方法一定是要使用遞歸的畫法,為了處理畫線問題,可以在繪制方法中添加兩個(gè)參數(shù)preId(上一節(jié)點(diǎn)ID)和endId(結(jié)束節(jié)點(diǎn)ID)

這樣邏輯為如下:

  • 繪制bpmn節(jié)點(diǎn)
  • 繪制上一節(jié)點(diǎn)與當(dāng)前節(jié)點(diǎn)的連線
  • 如果有next,遞歸繪制下一節(jié)點(diǎn)
  • 如果沒有next,繪制當(dāng)前節(jié)點(diǎn)與結(jié)束節(jié)點(diǎn)的連接線

考慮到上一根線可能有條件,所以再加入?yún)?shù)preExpression(上一根線的條件),最終方法如下

/**
 * 繪制節(jié)點(diǎn)
 * @param process bpmn process 圖紙
 * @param node    json的節(jié)點(diǎn)
 * @param preId   上一節(jié)點(diǎn)id
 * @param endId   結(jié)束節(jié)點(diǎn)
 * @param preExpression 上一節(jié)點(diǎn)表達(dá)式
 */
public void drawNode(Process process, ProcessNode node, String preId, String endId, String preExpression) {
    // 根據(jù)type繪制不同種類的節(jié)點(diǎn)
    Inout inout = drawNodeByType(process, node);
    // 繪制前一根線
    process.addFlowElement(createSequenceFlow(preId, inout.getIn(), preExpression));
    if (node.getNext() == null) {
        // 沒有下一步,繪制指向結(jié)束的線
        process.addFlowElement(createSequenceFlow(inout.getOut(), endId, null));
    } else {
        // 有下一步,遞歸繪制下一個(gè)節(jié)點(diǎn)
        drawNode(process, node.getNext(), inout.getOut(), endId, null);
    }
}

其中drawNodeByType(process, node)方法根據(jù)不同的種類畫不通過的節(jié)點(diǎn),反回是一個(gè)Inout

@Data
@AllArgsConstructor
public class Inout {
    private String in;
    private String out;
}

代表進(jìn)入節(jié)點(diǎn)的id和出節(jié)點(diǎn)的id,這是因?yàn)?strong>json的節(jié)點(diǎn)和bpmn的節(jié)點(diǎn)不是一一對應(yīng)的,普通的審核節(jié)點(diǎn),in和out都是審核節(jié)點(diǎn)id,而如果是分支節(jié)點(diǎn),in代表分支的開始網(wǎng)關(guān)id,out代表分支結(jié)束網(wǎng)關(guān)的id,接下來分別以兩種節(jié)點(diǎn)類型舉例來實(shí)現(xiàn)

/**
 * 繪制不同種類節(jié)點(diǎn)
 * @param process
 * @param node
 * @return
 */
private Inout drawNodeByType(Process process, ProcessNode node) {
    if (node.getType().equals("審核節(jié)點(diǎn)")) {
        return drawAuditNode(process, node);
    } else if (node.getType().equals("分支節(jié)點(diǎn)")) {
        return drawExclusiveNode(process, node);
    } else {
        throw new IllegalArgumentException();
    }
}

審核節(jié)點(diǎn)

/**
 * 繪制審核節(jié)點(diǎn)
 * @param process
 * @param node
 * @return
 */
private Inout drawAuditNode(Process process, ProcessNode node) {
    // 繪制節(jié)點(diǎn)
    String id = "Node_"+UUID.randomUUID();
    UserTask userTask = new UserTask();
    userTask.setId(id);
    userTask.setName(node.getName());
    // 設(shè)置多實(shí)例
    userTask.setAssignee("${user}");
    MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = new MultiInstanceLoopCharacteristics();
    if (node.getAssignee().getMultiMode().equals("順序?qū)徟?)) {
        multiInstanceLoopCharacteristics.setSequential(true);
    }
    multiInstanceLoopCharacteristics.setElementVariable("user");
    // 完成條件
    multiInstanceLoopCharacteristics.setCompletionCondition("${nrOfInstances == nrOfCompletedInstances}");
    multiInstanceLoopCharacteristics.setInputDataItem("${users}");
    userTask.setLoopCharacteristics(multiInstanceLoopCharacteristics);
    // 保存json節(jié)點(diǎn)配置到擴(kuò)展屬性
    Map<String, Object> extensions = new HashMap<>();
    extensions.put("node", node);
    BpmnUtil.addExtensionProperty(userTask, extensions);
    // 只有一個(gè)節(jié)點(diǎn),in&out相同
    return new Inout(id, id);
}

分支節(jié)點(diǎn)

/**
 * 繪制分支節(jié)點(diǎn)
 * @param process
 * @param node
 * @return
 */
private Inout drawExclusiveNode(Process process, ProcessNode node) {
    // 開始網(wǎng)關(guān)
    String startId = "Exclusive_"+UUID.randomUUID();
    ExclusiveGateway startGateway = new ExclusiveGateway();
    startGateway.setId(startId);
    process.addFlowElement(startGateway);
    // 結(jié)束網(wǎng)關(guān)
    String endId = "Exclusive_"+UUID.randomUUID();
    ExclusiveGateway endGateway = new ExclusiveGateway();
    endGateway.setId(endId);
    process.addFlowElement(endGateway);
    // 繪制分支
    List&lt;ExclusiveBranch&gt; branches = node.getExclusive();
    for (ExclusiveBranch branch : branches) {
        String expression = branch.getCondition();
        if (branch.getProcess()==null) {
            // 沒有子流程,直接繪制結(jié)束線
            process.addFlowElement(createSequenceFlow(startId, endId, expression));
        } else {
            // 有子流程,遞歸繪制子流程
            drawNode(process, branch.getProcess(), startId, endId, expression);
        }
    }
    // int和out不一樣
    return new Inout(startId, endId);
}

注意:繪制分支時(shí)如果有子流程,又回調(diào)用了drawNode,這是preId為開始網(wǎng)關(guān)id,endId是結(jié)束網(wǎng)關(guān)id,并且攜帶了表達(dá)式

其他類型的節(jié)點(diǎn)都類似,很簡單,不寫了

bpmn繪制完了,如果使用activiti就可以直接部署B(yǎng)pmnModel對象了

Deployment deployment = repositoryService
        .createDeployment()
        .addBpmnModel("test", bpmnModel)
        .deploy();

自動(dòng)布局

如果要轉(zhuǎn)換xml,上面的bpmnModel只有節(jié)點(diǎn)和線,并沒有布局,可以使用第三方輕松布局

<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-bpmn-layout</artifactId>
    <version>7.1.0.M1</version>
    <scope>compile</scope>
</dependency>

代碼一行就夠了

// 四.自動(dòng)布局
new BpmnAutoLayout(bpmnModel).execute();

轉(zhuǎn)xml

如果想把BpmnModel轉(zhuǎn)換為xml,也很簡單,引入依賴

<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-bpmn-converter</artifactId>
    <version>7.1.0.M1</version>
</dependency>

轉(zhuǎn)換代碼

// 五.轉(zhuǎn)xml
BpmnXMLConverter bpmnXMLConverter=new BpmnXMLConverter();
byte[] convertToXML = bpmnXMLConverter.convertToXML(bpmnModel);
String xml=new String(convertToXML);
xml = xml.replaceAll("&lt;","<").replaceAll("&gt;",">");

最終

貼一下完整實(shí)例代碼(代碼只是簡版,只為提供思路

/**
 * @Author pq
 * @Date 2022/10/20 10:58
 * @Description
 */
@SuppressWarnings("ALL")
public class BpmnConvert {
    public String toBpmn(ProcessNode node) {
        // 一.準(zhǔn)備工作
        BpmnModel bpmnModel = new BpmnModel();
        Process process = new Process(); // 相當(dāng)于圖紙
        bpmnModel.addProcess(process);
        process.setId("Process_"+UUID.randomUUID());
        process.setExecutable(true);
        // 二.開始結(jié)束節(jié)點(diǎn)
        StartEvent startEvent = new StartEvent();// 新建開始節(jié)點(diǎn)
        startEvent.setId("_start");
        process.addFlowElement(startEvent);// 繪制到圖紙
        EndEvent endEvent = new EndEvent(); // 新建結(jié)束節(jié)點(diǎn)
        endEvent.setId("_end");// 繪制到圖紙
        process.addFlowElement(endEvent);
        // 三.遞歸繪制節(jié)點(diǎn)
        drawNode(process, node, "_start", "_end", null);
        // 四.自動(dòng)布局
        new BpmnAutoLayout(bpmnModel).execute();
        // 五.轉(zhuǎn)xml
        BpmnXMLConverter bpmnXMLConverter=new BpmnXMLConverter();
        byte[] convertToXML = bpmnXMLConverter.convertToXML(bpmnModel);
        String xml=new String(convertToXML);
        xml = xml.replaceAll("<","<").replaceAll(">",">");
        return xml;
    }
    /**
     * 繪制節(jié)點(diǎn)
     * @param process bpmn process 圖紙
     * @param node    json的節(jié)點(diǎn)
     * @param preId   上一節(jié)點(diǎn)id
     * @param endId   結(jié)束節(jié)點(diǎn)
     * @param preExpression 上一節(jié)點(diǎn)表達(dá)式
     */
    public void drawNode(Process process, ProcessNode node, String preId, String endId, String preExpression) {
        // 根據(jù)type繪制不同種類的節(jié)點(diǎn)
        Inout inout = drawNodeByType(process, node);
        // 繪制前一根線
        process.addFlowElement(createSequenceFlow(preId, inout.getIn(), preExpression));
        if (node.getNext() == null) {
            // 沒有下一步, 繪制指向結(jié)束的線
            process.addFlowElement(createSequenceFlow(inout.getOut(), endId, null));
        } else {
            // 有下一步, 遞歸繪制下一個(gè)節(jié)點(diǎn)
            drawNode(process, node.getNext(), inout.getOut(), endId, null);
        }
    }
    /**
     * 繪制不同種類節(jié)點(diǎn)
     * @param process
     * @param node
     * @return
     */
    private Inout drawNodeByType(Process process, ProcessNode node) {
        if (node.getType().equals("審核節(jié)點(diǎn)")) {
            return drawAuditNode(process, node);
        } else if (node.getType().equals("分支節(jié)點(diǎn)")) {
            return drawExclusiveNode(process, node);
        } else {
            throw new IllegalArgumentException();
        }
    }
    /**
     * 繪制審核節(jié)點(diǎn)
     * @param process
     * @param node
     * @return
     */
    private Inout drawAuditNode(Process process, ProcessNode node) {
        // 繪制節(jié)點(diǎn)
        String id = "Node_"+UUID.randomUUID();
        UserTask userTask = new UserTask();
        userTask.setId(id);
        userTask.setName(node.getName());
        // 設(shè)置多實(shí)例
        userTask.setAssignee("${user}");
        MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = new MultiInstanceLoopCharacteristics();
        if (node.getAssignee().getMultiMode().equals("順序?qū)徟?)) {
            multiInstanceLoopCharacteristics.setSequential(true);
        }
        multiInstanceLoopCharacteristics.setElementVariable("user");
        // 完成條件
        multiInstanceLoopCharacteristics.setCompletionCondition("${nrOfInstances == nrOfCompletedInstances}");
        multiInstanceLoopCharacteristics.setInputDataItem("${users}");
        userTask.setLoopCharacteristics(multiInstanceLoopCharacteristics);
        // 保存json節(jié)點(diǎn)配置到擴(kuò)展屬性
        Map<String, Object> extensions = new HashMap<>();
        extensions.put("node", node);
        BpmnUtil.addExtensionProperty(userTask, extensions);
        return new Inout(id, id);
    }
    /**
     * 繪制分支節(jié)點(diǎn)
     * @param process
     * @param node
     * @return
     */
    private Inout drawExclusiveNode(Process process, ProcessNode node) {
        // 開始網(wǎng)關(guān)
        String startId = "Exclusive_"+UUID.randomUUID();
        ExclusiveGateway startGateway = new ExclusiveGateway();
        startGateway.setId(startId);
        process.addFlowElement(startGateway);
        // 結(jié)束網(wǎng)關(guān)
        String endId = "Exclusive_"+UUID.randomUUID();
        ExclusiveGateway endGateway = new ExclusiveGateway();
        endGateway.setId(endId);
        process.addFlowElement(endGateway);
        // 繪制分支
        List<ExclusiveBranch> branches = node.getExclusive();
        for (ExclusiveBranch branch : branches) {
            String expression = branch.getCondition();
            if (branch.getProcess()==null) {
                // 沒有子流程,直接繪制結(jié)束線
                process.addFlowElement(createSequenceFlow(startId, endId, expression));
            } else {
                // 有子流程,遞歸繪制子流程
                drawNode(process, branch.getProcess(), startId, endId, expression);
            }
        }
        // int和out不一樣
        return new Inout(startId, endId);
    }
    /**
     * 創(chuàng)建連線
     * @param from
     * @param to
     * @return
     */
    public SequenceFlow createSequenceFlow(String from, String to, String conditionExpression) {
        SequenceFlow flow = new SequenceFlow();
        flow.setId(from + "-" + to);
        flow.setSourceRef(from);
        flow.setTargetRef(to);
        if (conditionExpression != null) {
            flow.setConditionExpression(conditionExpression);
        }
        return flow;
    }
}

核心代碼真的沒幾行,細(xì)節(jié)自己完善即可

我自己做了個(gè)相對復(fù)雜的json,轉(zhuǎn)換為xml最終在bpmn.js展示效果如下

功能都沒大問題,就是自動(dòng)布局的線有點(diǎn)扭曲

以上就是仿釘釘流程輕松實(shí)現(xiàn)JSON轉(zhuǎn)BPMN完整實(shí)現(xiàn)過程示例的詳細(xì)內(nèi)容,更多關(guān)于仿釘釘流程JSON轉(zhuǎn)BPMN的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 一文教會(huì)你使用Java中的枚舉Enmu

    一文教會(huì)你使用Java中的枚舉Enmu

    枚舉是 Java 中的一種特殊類型,它用于表示一組固定值,這篇文章就是來和大家講講枚舉的作用與具體使用,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-06-06
  • 解讀CompletableFuture的底層原理

    解讀CompletableFuture的底層原理

    本文探討了Java8中CompletableFuture的原理和應(yīng)用,詳解其異步編程能力、工作機(jī)制及實(shí)際使用方法,CompletableFuture通過鏈?zhǔn)秸{(diào)用和狀態(tài)管理優(yōu)化異步任務(wù),提高Java應(yīng)用的效率和性能
    2024-09-09
  • springboot之自動(dòng)裝配全過程

    springboot之自動(dòng)裝配全過程

    這篇文章主要介紹了springboot之自動(dòng)裝配全過程,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • Java使用Freemarker頁面靜態(tài)化生成的實(shí)現(xiàn)

    Java使用Freemarker頁面靜態(tài)化生成的實(shí)現(xiàn)

    這篇文章主要介紹了Java使用Freemarker頁面靜態(tài)化生成的實(shí)現(xiàn),頁面靜態(tài)化是將原來的動(dòng)態(tài)網(wǎng)頁改為通過靜態(tài)化技術(shù)生成的靜態(tài)網(wǎng)頁,FreeMarker?是一個(gè)用?Java?語言編寫的模板引擎,它基于模板來生成文本輸,更多相關(guān)內(nèi)容需要的小伙伴可以參考一下
    2022-06-06
  • Spring深入了解常用配置應(yīng)用

    Spring深入了解常用配置應(yīng)用

    這篇文章主要給大家介紹了關(guān)于Spring的常用配置,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用springboot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2022-07-07
  • Java基于Graphics2D實(shí)現(xiàn)海報(bào)制作

    Java基于Graphics2D實(shí)現(xiàn)海報(bào)制作

    這篇文章主要為大家詳細(xì)介紹了Java如何基于Graphics2D實(shí)現(xiàn)海報(bào)制作,并且支持自定義顏色,背景,logo,貼圖,感興趣的小伙伴可以了解一下
    2024-04-04
  • 淺談SpringBoot實(shí)現(xiàn)自動(dòng)裝配的方法原理

    淺談SpringBoot實(shí)現(xiàn)自動(dòng)裝配的方法原理

    SpringBoot的自動(dòng)裝配是它的一大特點(diǎn),可以大大提高開發(fā)效率,減少重復(fù)性代碼的編寫。本文將詳細(xì)講解SpringBoot如何實(shí)現(xiàn)自動(dòng)裝配,需要的朋友可以參考下
    2023-05-05
  • 深入jetty的使用詳解

    深入jetty的使用詳解

    本篇文章是對jetty的使用進(jìn)行了詳細(xì)的分析解釋。需要的朋友參考下
    2013-05-05
  • 解讀@SelectProvider的使用方法

    解讀@SelectProvider的使用方法

    這篇文章主要介紹了@SelectProvider的使用方法,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • 詳解Java中native方法的使用

    詳解Java中native方法的使用

    native是與C++聯(lián)合開發(fā)的時(shí)候用的!使用native關(guān)鍵字說明這個(gè)方法是原生函數(shù),也就是這個(gè)方法是用C/C++語言實(shí)現(xiàn)的,并且被編譯成了DLL,由java去調(diào)用。本文給大家介紹java 中native方法使用,感興趣的朋友一起看看吧
    2020-09-09

最新評論