標籤:
繼之前的部落格,【思想篇】工作流程技術JBPM4.4開發入門(四),【思想篇】工作流程技術JBPM4.4開發入門(五)本篇部落格來結合代碼簡單說說,如何讓流程來管理業務:
先來看看我們抽出來的代理類:
StartAbstractJBPM:流程啟動節點
package com.hjy.ssh.action;import com.hjy.ssh.beans.AbstractApply;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import javax.annotation.Resource;import org.jbpm.api.ProcessInstance;import org.springframework.context.annotation.Scope;import org.springframework.stereotype.Controller;import java.lang.reflect.InvocationHandler;import java.util.Map;import com.hjy.ssh.service.JBPMService;import com.opensymphony.xwork2.ActionSupport;@Controller@Scope("prototype")public class StartAbstractJBPM extends ActionSupport {private static final long serialVersionUID = 1L;//定義一個屬性變數private Map<String, Object> variablesMap;private String pdKey;protected JBPMService jbpmService;public void common(String pdKey,Map<String, Object> variablesMap,JBPMService jbpmService){this.variablesMap=variablesMap;this.pdKey=pdKey;this.jbpmService=jbpmService;}//想嘗試能否根據其他方式傳參,new的話太耗費資源/*public StartAbstractJBPM(String pdKey,Map<String, Object> variablesMap,JBPMService jbpmService){this.variablesMap=variablesMap;this.pdKey=pdKey;this.jbpmService=jbpmService;}*///動態代理類只能代理介面(不支援抽象類別),代理類都需要實現InvocationHandler類,實現invoke方法。該invoke方法就是調用被代理介面的所有方法時需要調用的,該invoke方法返回的值是被代理介面的一個實作類別 public class LogHandler1 implements InvocationHandler{ // 目標對象 private Object targetObject; //綁定關係,也就是關聯到哪個介面(與具體的實作類別綁定)的哪些方法將被調用時,執行invoke方法。 public Object newProxyInstanceStart(Object targetObject){ this.targetObject=targetObject; //該方法用於為指定類裝載器、一組介面及調用處理器產生動態代理類執行個體 //第一個參數指定產生代理對象的類載入器,需要將其指定為和目標對象同一個類載入器 //第二個參數要實現和目標對象一樣的介面,所以只需要拿到目標對象的實現介面 //第三個參數表明這些被攔截的方法在被攔截時需要執行哪個InvocationHandler的invoke方法 //根據傳入的目標返回一個代理對象 return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(),this); } @Override //關聯的這個實作類別的方法被調用時將被執行 // InvocationHandler介面的方法,proxy表示代理,method表示原對象被調用的方法,args表示方法的參數 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("start-->>"); for(int i=0;i<args.length;i++){ System.out.println(args[i]); } Object ret=null; try{ //原對象方法調用前處理日誌資訊 System.out.println("satrt-->>"); //啟動流程 ProcessInstance pi=(ProcessInstance) jbpmService.startProcessInstanceByKey(pdKey,variablesMap); //調用目標方法 AbstractApply abstractApply=(AbstractApply)args[0]; abstractApply.setExecuteId(pi.getId()); args[0]=abstractApply; ret=method.invoke(targetObject, args); //調用完成當前結點 // >> 辦理完第1個任務“提交申請” jbpmService.completeFirstTask(pi); //原對象方法調用後處理日誌資訊 System.out.println("success-->>"); }catch(Exception e){ e.printStackTrace(); System.out.println("error-->>"); throw e; } return ret; }}}
HandleAbstractJBPMAction:任務辦理節點
package com.hjy.ssh.action;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import javax.annotation.Resource;import org.jbpm.api.ProcessInstance;import java.lang.reflect.InvocationHandler;import com.hjy.ssh.service.JBPMService;public abstract class HandleAbstractJBPMAction<T> extends ModelDrivenBaseAction<T> {protected String outcome;//分支protected String taskId;//任務idprotected boolean approval;//是否同意@Resourceprotected JBPMService jbpmService;//動態代理類只能代理介面(不支援抽象類別),代理類都需要實現InvocationHandler類,實現invoke方法。該invoke方法就是調用被代理介面的所有方法時需要調用的,該invoke方法返回的值是被代理介面的一個實作類別 public class LogHandler implements InvocationHandler{ // 目標對象 private Object targetObject; //綁定關係,也就是關聯到哪個介面(與具體的實作類別綁定)的哪些方法將被調用時,執行invoke方法。 public Object newProxyInstance(Object targetObject){ this.targetObject=targetObject; //該方法用於為指定類裝載器、一組介面及調用處理器產生動態代理類執行個體 //第一個參數指定產生代理對象的類載入器,需要將其指定為和目標對象同一個類載入器 //第二個參數要實現和目標對象一樣的介面,所以只需要拿到目標對象的實現介面 //第三個參數表明這些被攔截的方法在被攔截時需要執行哪個InvocationHandler的invoke方法 //根據傳入的目標返回一個代理對象 return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(),this); } @Override //關聯的這個實作類別的方法被調用時將被執行 // InvocationHandler介面的方法,proxy表示代理,method表示原對象被調用的方法,args表示方法的參數 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("start-->>"); for(int i=0;i<args.length;i++){ System.out.println(args[i]); } Object ret=null; try{ //原對象方法調用前處理日誌資訊 System.out.println("satrt-->>"); //儲存處理資訊 //abstractApprove(); // 2,辦理當前任務,調用完成當前結點 ProcessInstance pi=jbpmService.completeTask(taskId, outcome); //調用工作流程的操作 if(approval==false){ if (pi != null) { // 如果流程還未結束 //結束當前流程 jbpmService.endProcessInstance(pi); } } //調用目標方法 ret=method.invoke(targetObject, args); //調用工作流程,每個都實現這麼一個介面就可以,判斷是否要修改 isEnd(pi); //原對象方法調用後處理日誌資訊 System.out.println("success-->>"); }catch(Exception e){ e.printStackTrace(); System.out.println("error-->>"); throw e; } return ret; }} // 抽象方法,實現儲存處理資訊,以及設定同意不同意,但不需要更新protected abstract void abstractApprove()throws Exception;// 抽象方法,如果為最後的結點且同意了,那麼需要更新的資料表protected abstract void isEnd(ProcessInstance pi);//-----------public String getOutcome() {return outcome;}public void setOutcome(String outcome) {this.outcome = outcome;}public String getTaskId() {return taskId;}public void setTaskId(String taskId) {this.taskId = taskId;}public boolean isApproval() {return approval;}public void setApproval(boolean approval) {this.approval = approval;}}
註:以上的代理使用了兩種方式傳值,由於java不支援多重繼承,故第一種方式更好些,但是第二種方式傳值更加簡單,大家根據需要來選擇即可!
以上這兩個是抽象出來的代理類,那麼它起到了什麼作用呢?
下面我們來一起看看它們的應用:
對應啟動節點:
/** 提交申請 ,啟動工作流程--想成是宿主程式*/public String edit() throws Exception {Long stuCourseId=model.getId();//提交申請//封裝申請資訊,學生的申請資訊StuCourseApply stuCourseApply = new StuCourseApply();stuCourseApply.setStuCourseId(stuCourseId);newCourse=new String(newCourse.getBytes("iso-8859-1"),"utf-8");newTeacher=new String(newTeacher.getBytes("iso-8859-1"),"utf-8");stuCourseApply.setApplicant(getCurrentUser()); // 申請人,目前使用者stuCourseApply.setOldCourse(model.getCourse());stuCourseApply.setNewCourse(newCourse);stuCourseApply.setNewTeacher(newTeacher);stuCourseApply.setOldTeacher(model.getTeacher());stuCourseApply.setTitle("修改課程資訊"); String processDefinitionKeyStr=new String(processDefinitionKey.getBytes("iso-8859-1"),"utf-8"); stuCourseApply.setProcessDefinitionKey(processDefinitionKeyStr);// 調用業務方法(儲存申請資訊) // 1,設定屬性並儲存stuCourseApply stuCourseApply.setApplyTime(sdf.format(new Date())); // 申請時間,目前時間 stuCourseApply.setStatus(StuCourseApply.STATUS_RUNNING); //兩次儲存? stuCourseApplyService.save(stuCourseApply); // 2, 準備流程變數 Map<String, Object> variablesMap = new HashMap<String, Object>();variablesMap.put("stuCourseApply", stuCourseApply);//擷取流程定義的keyString pdKey = stuCourseApply.getProcessDefinitionKey();// 3,啟動流程執行個體開始流轉,並帶上流程變數(當前的申請資訊),調用宿主程式// 調用業務,儲存申請資訊startAbstractJBPM.common(pdKey, variablesMap, jbpmService);StartAbstractJBPM.LogHandler1 logHandler = startAbstractJBPM.new LogHandler1();//放到代理中設定值了//stuCourseApply.setExecuteId(pi.getId());StuCourseApplyService stuCourseApplyService1=(StuCourseApplyService)logHandler.newProxyInstanceStart(stuCourseApplyService); stuCourseApplyService1.save(stuCourseApply);return "toStuApplicationList"; // 成功後轉到"我的申請查詢"}
對應辦理節點:
/** 審批處理 */public String approve() throws Exception {abstractApprove();// 應用代理LogHandler logHandler=new LogHandler(); // 調用業務,更新狀態,更新狀態之前會先調用工作流程的完成當前任務方法StuCourseApplyService stuCourseApplyService1=(StuCourseApplyService)logHandler.newProxyInstance(stuCourseApplyService); stuCourseApplyService1.update(stuCourseApply);return "toStuTaskList"; // 成功後轉到待我審批頁面}
通過這兩段代碼,相信大家可以看出在這兩段代碼中已經不存在工作流程的任何內容,而此時我們的流程卻依然被工作流程來管理著,也就是我們將所有有關工作流程的方法均抽象出來,讓我們的類單純的應對業務,在調用業務的方法時我們調用代理,而此時的代理中已經將工作流程的啟動辦理等一系列操作封裝進去,在我們調用代理方法時已經啟動了工作流程,再處理時也操作了工作流程的辦理,故整個商務程序在我們無需瞭解工作流程的情況下就已經實現了被流程管理。
我們一直在說AOP,那麼什麼是AOP?
AOP(AspectOrientedProgramming):將日誌記錄,效能統計,安全控制,交易處理,異常處理等代碼從商務邏輯代碼中劃分出來,通過對這些行為的分離,我們希望可以將它們獨立到非指導商務邏輯的方法中,進而改變這些行為的時候不影響商務邏輯的代碼---兩個字概括:解耦。
總結:
最想說的一句話:會者不難,難者不會。在學習工作流程的這段期間,各種的不理解,各種的質疑自己,這個是AOP嗎?這樣做是我們想要的嗎?有時候仍會問自己什麼是工作流程,說到底它到底給我們帶來了什麼好處?
工作流程(Workflow),就是“業務過程的部分或整體在電腦應用環境下的自動化”,它主要解決的是“使在多個參與者之間按照某種預定義的規則自動進行傳遞文檔、資訊或任務的過程,從而實現某個預期的營運目標,或者促使此目標的實現”這段話說的真的很棒,但是我覺得我們要做到的不僅僅是這些,要補充的一點就是實現工作流程的AOP!
【代碼篇】JBPM4.4開發流程節點(動態代理實現流程管理業務)