精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

jBPM實現高級交互模式詳解

開發 后端
jBpm是一個非常好用的業務流程管理,其涉足領域包括業務流程管理、工作流、服務協作等。在這片文章中會針對jBpm高級交互模式的實現操作技巧進行一個詳細介紹,幫助大家理解。

JBPM是目前應用廣泛的Java工作流管理系統,在51CTO之前報道的J2EE工作流管理系統JBPM詳解中,我們曾詳細介紹過JBPM的工作原理和工作流應用方案。今天通過對JBPM中的四眼原則、任命和上報的實現來詳細講解如何使用JBPM實現高級交互模式。

#t#

許多通用業務流程都包含人類參與者。人類活動,從簡單場景(如人工批準)到復雜場景(涉及復雜的數據輸入),在流程實現中引入了新的方面,如人類交互模式。人類交互模式的一個典型集合包括:

1、四眼原則(The 4-eyes principle),通常又被稱為“職責分離”,它是決策由多人彼此獨立作出時的一個常見場景。在很多情況下,很容易就能得到另一個觀點/簽名。

2、任命(Nomination)是指上級根據團隊成員的任務安排、工作負荷或經驗人工地將任務分配給他的情形。

3、任務通常被建模來表達一種預期:它們將在確定時間段內完成。如果任務沒有按預期地進展,就需要一種上報(escalation)機制。兩種典型的上報實現是:重新分配任務,并常常附帶一個上報已經發生的通知;或任務未按時完成的通知(通常發給經理)。

4、鏈狀執行(Chained execution)是一個流程(片斷),其中的一系列步驟是由同一個人來完成。

在本文中,我將討論如何實現JBPM高級交互模式。

JBPM中的任務管理

JBPM的一個核心功能2是為人類管理任務和任務列表。JBPM允許將任務和任務節點作為整個流程設計的一部分使用。

任務一般在JBPM中定義成任務節點。單個任務節點可以包含一個或多個任務。包含任務節點的JBPM流程的一個公共行為就是等待任務節點中的全部任務完成,然后繼續執行。某個任務可被分配3 給個人、用戶組或泳道:

假如任務被分配給某個特定用戶,那么就只有這個使用者可以執行它。

假如任務被分配給某個用戶組,那么這個組內的任何參與者都能執行這個任務。JBPM使用的是參與者池(pooled actors)符號(它可以包含組名、組名列表和參與者個人列表等),而不是組ID。如果用戶開始執行在他們組任務列表中的任務,最終可能會引起沖突4——可能有多人開始執行相同的任務。為了避免這種情況,在開始執行任務之前,用戶應該將任務從組任務列表移動到他們自己的任務列表中。

泳道代表一個流程角色,它通常被分配給一個用戶組。它是一種指定流程中的多個任務要由同一參與者完成的機制5。因此,在第一個任務被分配給某個泳道之后,流程就會記住所有在相同泳道內的后續任務都將由同一參與者完成。

JBPM提供了兩種定義任務分配的基本方法:作為流程定義的一部分或通過編程實現。如果是作為流程定義的一部分,分配可以通過指定具體用戶、用戶組或泳道完成。此外,可以使用表達式根據流程變量動態確定某個具體用戶。完整的編程實現是基于分配處理器(assignment handler)的6,它允許任務根據任意的計算規則去查找用戶ID。那么如何才能很好的實現JBPM高級交互模式這一功能呢,讓我們繼續往下看。

流程定義描述流程實例的方式類似任務描述任務實例的方式。當流程執行時,一個流程實例——流程的運行時表示——就會被創建。類似,一個任務實例——任務的運行時表示——就會被創建。根據任務定義,任務實例被分配給一個參與者/參與者組。

任務實例的一個作用就是支持用戶交互——把數據顯示給用戶并從用戶那里收集數據。一個JBPM任務實例擁有訪問流程(令牌)變量7的全部權限,而且還可以有自己的變量。任務能夠擁有自己的變量對于以下場景非常有用:

在任務實例中創建流程變量的副本,這樣對任務實例變量的即時更新只有在該任務完成且這些副本被提交給流程變量時才會影響流程變量。

創建更好支持用戶活動的“派生(計算)”變量。

任務自己的變量在JBPM中是通過任務控制器處理器(task controller handler)支持的,它可以在任務實例創建時生成任務實例數據(從流程數據),并在任務實例完成時將任務實例數據提交給流程變量。

#p#

擴展Task類

JBPM高級交互模式的實現操作中任務的定義被包含在org.JBPM.taskmgmt.def.Task類中。為了支持四眼原則,我們需要給類增加以下的字段/方法(清單1):

  1. protected int numSignatures = 1;  
  2. public int getNumSignatures(){  
  3. return numSignatures;  
  4. }  
  5. public void setNumSignatures
    (int numSignatures){  
  6. this.numSignatures = 
    numSignatures;  

清單1 給Task類增加字段和方法

這個新的參數允許指定任務完成所需的JBPM高級交互模式任務處理人數量。缺省值為1,這意味著,只有1個用戶應該/可以處理這個任務。

JBPM使用Hibernate來向數據庫保存和讀取數據。為了讓我們新加的變量持久化,我們需要更新Task類的Hibernate配置文件(Task.hbm.xml),它在org.JBPM.taskmgmt.def文件夾中,增加代碼如下(清單2)

  1. <  property name="numSignatures" 
    column="NUMSIGNATURES_" /> 

清單2 在Task映射文件中指定新增域

為了讓我們新加的屬性能被流程定義和數據庫正確讀取,我們需要修改org.JBPM.jpdl.xml.JpdlXmlReader類以正確地讀取我們的新屬性(清單3)

  1. String numSignatureText = 
    taskElement.attributeValue
    ("numSignatures");  
  2. if (numSignatureText != null) {  
  3. try{  
  4. task.setNumSignatures
    (Integer.parseInt(num
    SignatureText));  
  5. }  
  6. catch(Exception e){}  

清單3 讀取numSignature屬性

最后,因為JpdlXmlReader根據模式來驗證XML,因此我們需要在jpdl-3.2.xsd中增加一個屬性定義(清單4):

  1. <  xs:element name="task"> 
  2. ………………….  
  3. <  xs:attribute name=
    "numSignatures" type=
    "xs:string" /> 

清單4 在jpdl-3.2.xsd中增加numSignatures屬性

當完成這些工作,JBPM高級交互模式的任務定義就被擴展可以使用numSignatures屬性(清單5):

  1. <  task name="task2" 
    numSignatures = "2">   
  2. <  assignment pooled-actors
    ="Peter, John"> 
  3. <  /assignment>   
  4. <  /task> 

清單5 給任務定義增加numSignatures屬性

#p#

擴展TaskInstance類

在JBPM高級交互模式實現操作到此處時,擴展完任務類后,我們還需要創建一個自定義的任務實例類來跟蹤分配給該任務實例的參與者,并確保所有被分配的參與者完成類執行(清單6)。

  1. package com.navteq.JBPM.extensions;  
  2. import java.util.Date;  
  3. import java.util.LinkedList;  
  4. import java.util.List;  
  5. import org.JBPM.JBPMException;  
  6. import org.JBPM.taskmgmt.exe.TaskInstance;  
  7. public class AssignableTaskInstance 
    extends TaskInstance {  
  8. private static final long 
    serialVersionUID = 1L;  
  9. private List< Assignee> assignees = 
    new LinkedList< Assignee>();  
  10. private String getAssigneeIDs(){  
  11. StringBuffer sb = new StringBuffer();  
  12. boolean first = true;  
  13. for(Assignee a : assignees){  
  14. if(!first)  
  15. sb.append(" ");  
  16. else   
  17. first = false;  
  18. sb.append(a.getUserID());  
  19. }  
  20. return sb.toString();  
  21. }  
  22. public List< Assignee> getAssignees() {  
  23. return assignees;  
  24. }  
  25. public void reserve(String userID) 
    throws 
    JBPMException{  
  26. if(task == null)  
  27. throw new JBPMException("can't 
    reserve instance with no task");  
  28. // Duplicate assignment is ok  
  29. for(Assignee a : assignees){  
  30. if(userID.equals(a.getUserID()))  
  31. return;  
  32. }  
  33. // Can we add one more guy?  
  34. if(task.getNumSignatures() > 
    assignees.size()){  
  35. assignees.add(new Assignee(userID));  
  36. return;  
  37. }  
  38. throw new JBPMException("task 
    is already reserved by " +  
  39. getAssigneeIDs());  
  40. }  
  41. public void unreserve(String userID){  
  42. for(Assignee a : assignees){  
  43. if(userID.equals(a.getUserID())){  
  44. assignees.remove(a);  
  45. return;  
  46. }  
  47. }  
  48. }  
  49. private void completeTask(Assignee 
    assignee, String transition){  
  50. assignee.setEndDate(new Date());  
  51. // Calculate completed assignments  
  52. int completed = 0;  
  53. for(Assignee a : assignees){  
  54. if(a.getEndDate() != null)  
  55. completed ++;  
  56. }  
  57. if(completed <  task.getNumSignatures())  
  58. return;  
  59. if(transition == null)  
  60. end();  
  61. else   
  62. end(transition);  
  63. }  
  64. public void complete(String userID, 
    String transition) throws 
    JBPMException{  
  65. if(task == null)  
  66. throw new JBPMException("can't 
    complete instance with no task");  
  67. // make sure it was reserved  
  68. for(Assignee a : assignees){  
  69. if(userID.equals(a.getUserID())){  
  70. completeTask(a, transition);  
  71. return;  
  72. }  
  73. }  
  74. throw new JBPMException("task 
    was not reserved by " + userID);  
  75. }  
  76. public boolean isCompleted(){  
  77. return (end != null);  
  78. }  

清單6 擴展TaskInstance類

這個JBPM高級交互模式的操作實現擴展了JBPM提供的TaskInstance類,并跟蹤完成該實例所需的參與者個數。它引入了幾個新方法,允許參與者預留(reserve)/退還(unreserve)任務實例,以及讓指定參與者完成任務執行。

#p#

JBPM高級交互模式的實現操作中,清單6的實現依賴一個支持類Assignee(清單7)

  1. package com.navteq.JBPM.extensions;  
  2. import java.io.Serializable;  
  3. import java.text.DateFormat;  
  4. import java.text.SimpleDateFormat;  
  5. import java.util.Date;  
  6. public class Assignee implements 
    Serializable{  
  7. private static final long 
    serialVersionUID = 1L;  
  8. private static final DateFormat 
    dateFormat = new   
  9. SimpleDateFormat("yyyy/MM/dd HH:mm:ss");  
  10. long id = 0;  
  11. protected String startDate = null;  
  12. protected String userID = null;  
  13. protected String endDate = null;  
  14. public Assignee(){}  
  15. public Assignee(String uID){  
  16. userID = uID;  
  17. startDate = dateFormat.format(new Date());  
  18. }  
  19. //////Setters and Getters //////  
  20. public long getId() {  
  21. return id;  
  22. }  
  23. public void setId(long id) {  
  24. this.id = id;  
  25. }  
  26. public String getStartDate() {  
  27. return startDate;  
  28. }  
  29. public void setStartDate(String startDate) {  
  30. this.startDate = startDate;  
  31. }  
  32. public String getUserID() {  
  33. return userID;  
  34. }  
  35. public void setUserID(String id) {  
  36. userID = id;  
  37. }  
  38. public String getEndDate() {  
  39. return endDate;  
  40. }  
  41. public void setEndDate(String endDate) {  
  42. this.endDate = endDate;  
  43. }  
  44. public void setEndDate(Date endDate) {  
  45. this.endDate = dateFormat.format(endDate);  
  46. }  
  47. public void setEndDate() {  
  48. this.endDate = dateFormat.format(new Date());  
  49. }  
  50. public String toString(){  
  51. StringBuffer bf = new StringBuffer();  
  52. bf.append(" Assigned to ");  
  53. bf.append(userID);  
  54. bf.append(" at ");  
  55. bf.append(startDate);  
  56. bf.append(" completed at ");  
  57. bf.append(endDate);  
  58. return bf.toString();  
  59. }  

清單7 Assignee類

#p#

JBPM高級交互模式自定義的TaskInstance類和Assignee類都必須保存到數據庫中。這意味著需要給這兩個類實現Hibernate映射14 (清單8,9):

  1. <  ?xml version="1.0"?>   
  2. <  !DOCTYPE hibernate-mapping PUBLIC "-
    //Hibernate/Hibernate Mapping DTD 3.0
    //EN" "http://hibernate.sourceforge.net
    /hibernate-mapping-3.0.dtd"
    >   
  3. <  hibernate-mapping auto-import="false" 
    default-access="field">   
  4. <  subclass namename="com.navteq.JBPM.
    extensions.AssignableTaskInstance"
     
    extends="org.JBPM.taskmgmt.exe.
    TaskInstance"
     discriminator-value="A">   
  5. <  list name="assignees" cascade="all" >   
  6. <  key column="TASKINSTANCE_" />   
  7. <  index column="TASKINSTANCEINDEX_"/>   
  8. <  one-to-many class="com.navteq.
    JBPM.extensions.Assignee"
     />   
  9. <  /list>   
  10. <  /subclass>   
  11. <  /hibernate-mapping> 

清單8 自定義任務實例的Hibernate映射文件

  1. <  ?xml version="1.0"?>   
  2. <  !DOCTYPE hibernate-mapping PUBLIC "-
    //Hibernate/Hibernate Mapping DTD 3.0
    //EN" "http://hibernate.sourceforge.net
    /hibernate-mapping-3.0.dtd"
    >   
  3. <  hibernate-mapping auto-import=
    "false" default-access="field">   
  4. <  class name="com.navteq.JBPM.
    extensions.Assignee"
     table=
    "JBPM_ASSIGNEE">   
  5. <  cache usage="nonstrict-read-write"/>   
  6. <  id name="id" column="ID_"> 
  7. <  generator class="native" /> 
  8. <  /id> <  !-- Content -->   
  9. <  property name="startDate" 
    column="STARTDATE_" />   
  10. <  property name="userID" 
    column="USERID_" />   
  11. <  property name="endDate" 
    column="ENDDATE_" />   
  12. <  /class>   
  13. <  /hibernate-mapping> 

清單9 Assignee類的Hibernate映射文件

要讓JBPM高級交互模式的實現操作能夠使用我們的自定義任務實例實現,我們還需要提供一個自定義的任務實例工廠(清單10)。

  1. package com.navteq.JBPM.extensions;  
  2. import org.JBPM.graph.exe.ExecutionContext;  
  3. import org.JBPM.taskmgmt.TaskInstanceFactory;  
  4. import org.JBPM.taskmgmt.exe.TaskInstance;  
  5. public class AssignableTaskInstanceFactory 
    implements TaskInstanceFactory {  
  6. private static final long serialVersionUID = 1L;  
  7. @Override  
  8. public TaskInstance createTaskInstance
    (ExecutionContext executionContext) {  
  9. return new AssignableTaskInstance();  
  10. }  

清單10 自定義的任務實例工廠

最后,為了讓JBPM運行時使用正確的任務實例工廠(清單10),還必須創建一個新的JBPM配置(清單11)。

  1. <  JBPM-configuration>   
  2. <  bean name="JBPM.task.instance.
    factory"
     class="com.navteq.JBPM.
    extensions.AssignableTask
    InstanceFactory"
     singleton="true" />   
  3. <  /JBPM-configuration> 

清單11 JBPM配置

完成所有這些變更之后(清單1-11),一個典型的任務處理顯示如下:

  1. List<  String> actorIds = 
    new LinkedList
    <  String>();  
  2. actorIds.add("Peter");  
  3. List<  TaskInstance> cTasks = 
    JBPMContext.getGroupTaskList(actorIds)  
  4. TaskInstance cTask = cTasks.get(0);  
  5. AssignableTaskInstance aTask = 
    (AssignableTaskInstance)cTask;  
  6. try{  
  7. aTask.reserve("Peter");  
  8. // Save  
  9. JBPMContext.close();  
  10. }  
  11. catch(Exception e){  
  12. System.out.println("Task " + 
    cTask.getName() + " is already reserved");  
  13. e.printStackTrace();  

清單12 處理可分配任務實例

這里,在得到某個用戶的任務實例并將其轉變成可分配任務實例之后,我們將試著預留它15。一旦預留成功,我們將關閉JBPM高級交互模式運行時以提交事務。

#p#

實現任命

JBoss JBPM可以非常輕易的實現手動將任務分配給特定用戶。根據JBPM提供的簡單API,可以完成將任務實例從一個任務列表移動到另一個任務列表,因此給某個用戶分配任務相當直接(清單13)

  1. List< String> actorIds = 
    new LinkedList< String>();  
  2. actorIds.add("admins");  
  3. String actorID = "admin";  
  4. List< TaskInstance> cTasks = 
    JBPMContext.getGroupTaskList(actorIds);  
  5. TaskInstance cTask = cTasks.get(0);  
  6. cTask.setPooledActors((Set)null);  
  7. cTask.setActorId(actorID); 

 

清單13 將任務重新分配給指定用戶

在JBPM高級交互模式的操作中,提供了2類不同的API來設置參與者池:一類接收字符串id數組,另一類則接收id集合。如果要清空一個池,就要使用那個接收集合的API(傳入一個null集合)。

實現上報

前面已經說過,上報一般被實現為任務的重新分配,并常常附帶一個上報已發生的通知;或是實現成一個任務未及時完成的通知。

實現為重新分配的上報

盡管JBPM不直接支持上報,但它提供了2個基本的機制:超時和重新分配(參見上節)。粗一看,實現上報只需將這二者結合即可,但是仔細一想還是存在一些困難:

JBPM實現中的關系并不總是雙向的。如,從一個任務節點我們可以找到所有這個節點定義的任務,但是從一個任務,并沒有API可以完成找到包含它的任務節點的工作16;由某個任務實例,你可以得到一個任務,但是沒有由某個任務得到所有實例的API,諸如此類。

超時不是發生在任務自身,而是發生在任務節點上。由于某個節點可以關聯多個任務,并且JBPM關系實現并不是雙向的(見上),因此要跟蹤當前任務實例就需要其他的支持手段。
以重新分配實現的上報的整個實現17涉及3個處理器:

負責給任務分配參與者的分配處理器。這個處理器跟蹤它是一個首次任務調用還是一個上報任務調用。清單14給出了一個分配處理器的例子。

  1. package com.sample.action;  
  2. import org.JBPM.graph.def.Node;  
  3. import org.JBPM.graph.exe.
    ExecutionContext;  
  4. import org.JBPM.taskmgmt.def.
    AssignmentHandler;  
  5. import org.JBPM.taskmgmt.exe.
    Assignable;  
  6. public class EscalationAssi
    gnmentHandler implements 
    AssignmentHandler {  
  7. private static final long 
    serialVersionUID = 1L;  
  8. @Override  
  9. public void assign(Assignable assignable, 
    ExecutionContext context)  
  10. throws Exception {  
  11. Node task = context.getToken().getNode();  
  12. if(task != null){  
  13. String tName = task.getName();  
  14. String vName = tName + "escLevel";  
  15. Long escLevel = (Long)context.
    getVariable(vName);  
  16. if(escLevel == null){  
  17. // First time through  
  18. assignable.setActorId("admin");  
  19. }  
  20. else{  
  21. // Escalate  
  22. assignable.setActorId("bob");  
  23. }  
  24. }  
  25. }  

清單14 分配處理器示例

在JBPM高級交互模式的實現操作中我們嘗試得到一個包含了給定任務上報次數的流程變量。如果變量未定義,則就分配“admin”為任務擁有者,否則任務就被分配給“bob”。在這個處理器中可以使用任何其他的分配策略。

任務實例創建動作處理器(清單15),它保存流程實例上下文的任務實例id

  1. package com.sample.action;  
  2. import org.JBPM.graph.def.ActionHandler;  
  3. import org.JBPM.graph.def.Node;  
  4. import org.JBPM.graph.exe.ExecutionContext;  
  5. import org.JBPM.taskmgmt.exe.TaskInstance;  
  6. public class TaskCreationActionHandler 
    implements ActionHandler {  
  7. private static final long 
    serialVersionUID = 1L;  
  8. @Override  
  9. public void execute(ExecutionContext 
    context) throws Exception {  
  10. Node task = context.getToken().getNode();  
  11. TaskInstance current = 
    context.getTaskInstance();  
  12. if((task == null) || (current == null))  
  13. return;  
  14. String tName = task.getName();  
  15. String iName = tName + "instance";  
  16. context.setVariable(iName, 
    new Long(current.getId()));  
  17. }  

清單15 任務實例創建處理器

JBPM高級交互模式中任務節點計時器觸發調用的超時處理器(清單16)。

  1. package com.sample.action;  
  2. import org.JBPM.graph.def.ActionHandler;  
  3. import org.JBPM.graph.def.GraphElement;  
  4. import org.JBPM.graph.exe.ExecutionContext;  
  5. import org.JBPM.taskmgmt.exe.TaskInstance;  
  6. public class EscalationActionHandler 
    implements ActionHandler {  
  7. private static final long 
    serialVersionUID = 1L;  
  8. private String escalation;  
  9. @Override  
  10. public void execute(ExecutionContext 
    context) throws Exception {  
  11. GraphElement task = context.getTimer().
    getGraphElement();  
  12. if(task == null)  
  13. return;  
  14. String tName = task.getName();  
  15. String vName = tName + "escLevel";  
  16. long escLevel = (long)context.
    getVariable(vName);  
  17. if(escLevel == null)  
  18. escLevel = new long(1);  
  19. else  
  20. escLevel += 1;  
  21. context.setVariable(vName, escLevel);  
  22. String iName = tName + "instance";  
  23. long taskInstanceId = (long)
    context.getVariable(iName);  
  24. TaskInstance current =   
  25. context.getJBPMContext().
    getTaskInstance(taskInstanceId);  
  26. if(current != null){  
  27. current.end(escalation);  
  28. }  
  29. }  

清單16 超時處理器

這個處理器首先記錄上報計數器,接著完成此節點關聯的任務實例。任務實例的完成伴隨有一個變遷(一般是回到任務節點)。

使用以上描述的處理器實現JBPM高級交互模式的上報的簡單流程例子顯示在清單17中。

  1. < ?xml version="1.0" encoding="UTF-8"?>   
  2. < process-definition xmlns="urn:JBPM.
    org:jpdl-3.2"
     name="escalationHumanTaskTest">   
  3. < start-state name="start">   
  4. < transition to="customTask"> 
  5. < /transition>   
  6. < /start-state>   
  7. < task-node name="customTask">   
  8. < task name="task2">   
  9. < assignment class="com.sample.action.
    EscalationAssignmentHandler"
    > 
  10. < /assignment>   
  11. < /task>   
  12. < event type="task-create">   
  13. < action name="Instance Tracking" 
    class="com.sample.action.
    TaskCreationActionHandler"
    > 
  14. < /action>   
  15. < /event>   
  16. < timer duedate="10 second" 
    name="Escalation timeout">   
  17. < action class="com.sample.action.
    EscalationActionHandler"
    > < escalation>   
  18. escalation   
  19. < /escalation>   
  20. < /action>   
  21. < /timer>   
  22. < transition to="end" name="to end"> 
  23. < /transition>   
  24. < transition to="customTask" 
    name="escalation"> 
  25. < /transition>   
  26. < /task-node>   
  27. < end-state name="end"> 
  28. < /end-state>   
  29. < /process-definition> 

清單17 簡單流程的上報

實現成通知的上報

JBPM為郵件傳遞提供了強大支持18,這使得實現成通知的上報變得極其簡單。郵件傳遞可由給節點附加定時器,然后觸發,它使用已經寫好的郵件動作來完成通知傳遞。

實現鏈狀執行

鏈狀執行直接由JBPM泳道支持,并不需要額外的開發。

總結

不管我們在自動化方面投入多少努力,面對復雜的業務流程,總免不了要有人工介入的可能。在這篇JBPM高級交互模式的操作介紹的文章中,我給出了一系列已建立的高級人工交互模式,并展示了用JBPM完成它是多么輕而易舉。

責任編輯:曹凱 來源: infoq.com
相關推薦

2025-08-28 06:25:00

2009-08-25 18:04:30

C#實現Singlet

2009-06-26 13:51:49

jBPM4高級圖形執行

2010-06-04 15:59:45

Hadoop完全分布模

2011-06-28 15:18:45

Qt 單例模式

2009-06-26 09:15:31

jBPM4基本活動

2009-06-26 09:32:35

jBPM4基本活動

2010-10-19 16:32:46

MySQL

2021-09-12 07:30:10

配置

2009-06-24 16:23:29

jBPM 4.0配置

2010-02-06 13:42:36

C++單件模式

2010-05-12 16:13:04

2021-06-29 08:54:23

設計模式代理模式遠程代理

2009-08-07 14:10:13

C# WebserviDelphi

2019-08-30 07:24:16

2010-06-13 09:15:16

WinForm窗體

2011-06-28 15:01:01

Qt PIMPL

2021-07-07 10:31:19

對象池模式解釋器模式設計模式

2009-06-25 17:13:51

jBPM與Spring

2009-06-11 13:53:35

jBPM用戶指南
點贊
收藏

51CTO技術棧公眾號

精品国产乱码久久久久久1区2区| 国产蜜臀av在线一区二区三区| 久久99久久久久久久噜噜| 日韩大尺度视频| 老色鬼在线视频| 中文一区一区三区高中清不卡| 成人美女av在线直播| 免费人成视频在线| 怕怕欧美视频免费大全| 欧美一区二区在线不卡| 欧美亚洲精品一区二区| av在线电影免费观看| 国产成人亚洲精品狼色在线 | 老司机在线视频二区| 成人精品鲁一区一区二区| 国产精品大陆在线观看| 青青草在线观看视频| 蜜桃精品wwwmitaows| 欧美一区二区福利视频| 国产日产欧美视频| 日本无删减在线| 国产精品你懂的| 久久99精品久久久久久三级| 国产福利视频导航| 日本怡春院一区二区| 国模精品系列视频| 中文字幕在线有码| 久久精品播放| 亚洲色图18p| yy1111111| 免费精品一区二区三区在线观看| 欧美视频精品在线| 国产一区二区三区精彩视频| 牛牛精品在线视频| 亚洲三级在线免费| 夜夜春亚洲嫩草影视日日摸夜夜添夜| 欧美一区二区三区激情| 国产福利91精品一区二区三区| 国产精品久久久久久亚洲影视| 国产成人免费看| 国产精品分类| 欧美高清激情视频| 精品人妻伦九区久久aaa片| 欧美久久综合网| 亚洲人成电影在线播放| 成人h动漫精品一区| 欧美日韩一本| 亚洲精品电影在线| a天堂视频在线观看| 97精品久久| 福利91精品一区二区三区| 国产精品久久久久久久久免费| 在线观看亚洲欧美| 日韩天堂av| 97免费在线视频| 可以在线观看av的网站| 在线国产日韩| 97人人模人人爽人人喊中文字| 国产主播在线观看| 亚洲性视频h| 国内久久久精品| 日本少妇在线观看| 亚洲精品欧美| 日本人成精品视频在线| 无码一区二区三区在线观看| 青青草97国产精品免费观看 | 成人性视频欧美一区二区三区| 中文字幕在线中文字幕在线中三区| 狠狠干狠狠久久| 黄色片久久久久| 电影亚洲一区| 欧美一区二区三区免费观看视频 | 你懂的视频欧美| 亚洲无线码在线一区观看| 免费黄在线观看| 一区二区在线| 97久久精品人人澡人人爽缅北| 欧美日韩精品区| 日韩av午夜在线观看| 国产日韩专区在线| 精品人妻一区二区三区日产乱码 | 91超碰在线免费| 日韩欧美福利视频| www.超碰97.com| 9999久久久久| 亚洲午夜国产成人av电影男同| 懂色av蜜桃av| 国内精品久久久久久久影视蜜臀 | 亚洲AV无码成人精品区东京热| 日韩不卡手机在线v区| 亚洲va久久久噜噜噜| 日日躁夜夜躁白天躁晚上躁91| 久久久精品综合| 男人j进女人j| 亚洲www免费| 欧美一区二区三区色| 精品国产人妻一区二区三区| jvid福利在线一区二区| 久久久久久久爱| 一区二区视频免费观看| 成人综合婷婷国产精品久久蜜臀| 日本亚洲欧洲精品| 午夜av在线播放| 日本精品视频一区二区| 折磨小男生性器羞耻的故事| 极品美女一区二区三区| 欧美大片在线看免费观看| aaa在线视频| 国产mv日韩mv欧美| 亚洲资源在线网| 亚洲人成在线网站| 精品国产一二三| 肉色超薄丝袜脚交69xx图片| 国产模特精品视频久久久久| 亚洲精品欧美日韩| 91在线高清| 一本到高清视频免费精品| 欧美熟妇另类久久久久久多毛| 精品国产一级毛片| 欧美在线一区二区三区四| www.黄色片| 中文字幕第一区综合| 少妇性饥渴无码a区免费| jizz性欧美23| 欧美日韩高清区| 夜夜躁很很躁日日躁麻豆| 久久综合精品国产一区二区三区 | 天堂一区二区在线| 国内精品国语自产拍在线观看| 国产视频在线播放| 欧美日韩国产片| 在线免费看视频| 日韩和欧美一区二区三区| 精品久久精品久久| 精精国产xxx在线视频app| 精品少妇一区二区三区视频免付费| fc2ppv在线播放| 久久精品二区亚洲w码| 日韩国产伦理| 成人国产一区| 中文亚洲视频在线| 最近中文字幕在线免费观看| 国产亚洲自拍一区| 99久久国产宗和精品1上映| 神马日本精品| 日本国产一区二区三区| 免费在线黄色电影| 色诱亚洲精品久久久久久| 性欧美成人播放77777| 国产欧美一级| 久久综合九色综合网站| 在线视频超级| 亚洲欧美日韩国产中文专区| 中文在线第一页| 国产亚洲一区字幕| 污版视频在线观看| 91精品综合| 99热最新在线| 美女视频在线免费| 亚洲精品在线观看www| 九九热最新视频| 国产精品美女久久久久av爽李琼| 中文字幕22页| 欧美精品国产一区二区| 成人在线观看网址| 欧美激情网站| 亚洲最新av在线| 自拍偷拍视频亚洲| 青青草97国产精品免费观看 | 国产精品久久国产精麻豆96堂| 美女国产一区二区| 成年人黄色在线观看| 久久免费福利| 97**国产露脸精品国产| 成年人在线视频| 91精品国产一区二区三区| 国产一级片免费看| 久久综合网色—综合色88| 天天操天天爽天天射| 亚洲自拍偷拍网| 国产一区二区精品免费| 欧亚一区二区| 欧美巨大黑人极品精男| 天堂av在线资源| 欧美日韩免费在线视频| 国产小视频在线看| 国产欧美日韩在线看| 中文字幕乱码在线人视频| 一区二区三区导航| 一区二区视频在线免费| 国产精品香蕉| 国产精品第1页| 五月婷婷视频在线观看| 国产亚洲精品va在线观看| 国产精品久久无码一三区| 精品久久久久久久久久久久| 美国美女黄色片| 99久久精品国产精品久久| www.久久91| 香蕉视频成人在线观看| 国产高清免费在线| 久久99国内| 成人区精品一区二区| 国产亚洲欧美日韩精品一区二区三区 | 日韩不卡免费视频| 免费一级特黄毛片| 亚洲最新av| 日韩中文字幕一区| 国产色噜噜噜91在线精品 | 欧美激情欧美激情| 男人在线资源站| 亚洲男人7777| 十八禁一区二区三区| 7777精品伊人久久久大香线蕉超级流畅 | 国产精品一区在线观看| 欧美一级做a| 国产成人精品电影| 黑森林国产精品av| 久久99国产精品久久久久久久久| 成人欧美亚洲| 日韩成人久久久| 性中国古装videossex| 欧美四级电影网| 精人妻无码一区二区三区| 精品国产91乱高清在线观看| 国产亚洲精品久久久久久打不开| 中文字幕一区在线观看| 日韩中文字幕有码| 久久久久国产精品麻豆ai换脸| 亚洲国产精品狼友在线观看| 国产一区二区在线视频| 男人的天堂最新网址| 免费不卡在线观看| 国产裸体免费无遮挡| 久久久噜噜噜久久狠狠50岁| 一区二区传媒有限公司| 亚洲日本久久| 日韩小视频在线播放| 激情久久久久| 欧美一级免费播放| 一本久久综合| 国内性生活视频| 国产欧美日韩亚洲一区二区三区| 丁香花在线影院观看在线播放| 欧美福利一区| 蜜臀av性久久久久蜜臀av| 一区二区国产在线| 男人c女人视频| 国产综合精品| 国产无限制自拍| 亚洲精品影视| 国产亚洲综合视频| 日韩黄色在线观看| 四季av一区二区三区| 国产一区91精品张津瑜| gogo亚洲国模私拍人体| 成人永久aaa| 屁屁影院国产第一页| 久久久精品影视| 殴美一级黄色片| 亚洲靠逼com| 久久精品免费在线| 色婷婷综合在线| 最新中文字幕在线观看视频| 欧美高清视频www夜色资源网| 国产欧美第一页| 欧美精品一区二区不卡| 日本中文字幕一区二区有码在线| 一区二区三区视频免费在线观看| 在线播放毛片| 欧美丰满老妇厨房牲生活| 国产99在线| 日韩美女视频免费在线观看| 九九久久国产| 国产精品二区在线| 国产精品亚洲片在线播放| 亚洲砖区区免费| 亚洲福利专区| 亚洲天堂网一区| 懂色av噜噜一区二区三区av| 在线 丝袜 欧美 日韩 制服| 国产精品麻豆一区二区| 精品无码m3u8在线观看| 在线精品国精品国产尤物884a| 91丨porny丨在线中文| 精品美女在线播放| 不卡在线视频| 国外成人在线播放| 国产国产一区| 久久99国产精品99久久| 色综合咪咪久久网| 欧美日韩成人免费视频| 精品一区二区三区免费观看| 中文字幕在线播放视频| 亚洲欧洲日本在线| 久久久精品福利| 欧美成人性福生活免费看| 国产乱子伦三级在线播放| 欧美—级高清免费播放| 国产91亚洲精品久久久| 精品国产一区二区三区麻豆小说 | www.亚洲天堂| 最新欧美色图| 999国内精品视频在线| 日本大胆欧美| 日韩少妇内射免费播放18禁裸乳| 狠狠色丁香久久婷婷综合丁香| 青青草视频成人| 亚洲一区视频在线| 91超薄丝袜肉丝一区二区| 亚洲欧美日本精品| h片在线观看视频免费| 91中文字幕在线观看| 成人久久电影| 777米奇影视第四色| 成人午夜视频福利| 中文字幕电影av| 欧美日韩免费观看一区二区三区| 香蕉视频免费在线看| 欧美放荡办公室videos4k| 96视频在线观看欧美| 天天爽天天狠久久久| 欧美一级久久| 黄色短视频在线观看| 亚洲国产精品久久不卡毛片| 国产免费久久久| 日韩中文字幕视频在线| 成人精品国产| 亚洲欧美99| 日本91福利区| 亚洲精品91在线| 欧美在线观看视频一区二区| 色就是色亚洲色图| 欧美亚洲视频在线观看| 卡通动漫精品一区二区三区| 妺妺窝人体色www看人体| 国产精品香蕉一区二区三区| 小嫩苞一区二区三区| 欧美精品日韩一区| 日本中文字幕伦在线观看| 国产精品久久一区| 日韩欧美在线中字| 久久黄色片网站| 综合中文字幕亚洲| 国产又粗又长又大视频| 日韩在线精品视频| 成人国产精品久久| 国内外成人激情免费视频| 国产成人综合在线播放| www.99re7.com| 日韩精品在线看| 日韩不卡视频在线观看| 亚洲一区二三| 国产又黄又大久久| 久久免费在线观看视频| 日韩av一区二区在线观看| 欧美7777| 在线观看日韩羞羞视频| 国产成人在线影院| 日本亚洲欧美在线| 亚洲奶大毛多的老太婆| 国产成人免费精品| 黄色污污在线观看| caoporn国产精品| 亚洲 日本 欧美 中文幕| 日韩中文视频免费在线观看| 精品欧美视频| 野外做受又硬又粗又大视频√| 91免费国产在线| 一级α片免费看刺激高潮视频| 久久综合免费视频| 欧美激情15p| 在线观看免费成人av| 综合久久一区二区三区| 蜜臀av中文字幕| 国产精品www色诱视频| 亚洲国产精品久久久天堂| 美女搡bbb又爽又猛又黄www| 在线视频你懂得一区二区三区| 黄色网页在线播放| 精品国产一区二区三区麻豆小说 | 精品中文视频在线| 久久精品黄色| 黄色大片中文字幕| 国产精品久久久久天堂| 成人爽a毛片一区二区| 国产97在线|亚洲| 国产一区清纯| 四季av中文字幕| 亚洲成人999| 色诱色偷偷久久综合| 极品粉嫩国产18尤物| 国产精品乱人伦中文| 无码国产色欲xxxx视频 | 超碰一区二区三区| av视屏在线播放| 天天色天天爱天天射综合| 视频一区二区三区不卡| 蜜桃传媒视频麻豆第一区免费观看 | 97免费公开视频|