继之前的博客,【思想篇】工作流技术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;//任务id protected boolean approval;//是否同意 @Resource protected 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); //获取流程定义的key String 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!