Activiti監(jiān)聽器的使用

監(jiān)聽器是發(fā)生對應(yīng)任務(wù)相關(guān)事件時執(zhí)行自定義的java邏輯或表達(dá)式
在使用Activiti時氯析,通常是跟業(yè)務(wù)結(jié)合缎岗,而有些業(yè)務(wù)會比較的復(fù)雜静尼,會出現(xiàn)以下的場景:

  • activiti人員的動態(tài)分配
  • 當(dāng)前任務(wù)節(jié)點完成時,需要指定下一個節(jié)點任務(wù)的執(zhí)行人
  • 任務(wù)節(jié)點完成時需要一些復(fù)雜的業(yè)務(wù)處理
  • 任務(wù)到達(dá)某一節(jié)點時,需要監(jiān)控當(dāng)前任務(wù)的以下信息或者日志
  • 當(dāng)前任務(wù)執(zhí)行人處理人物的時候鼠渺,需要觸發(fā)自定義的一些業(yè)務(wù)處理
  • 流程的開始和結(jié)束也有可能對應(yīng)相關(guān)的業(yè)務(wù)處理
  • 不僅是節(jié)點觸發(fā)業(yè)務(wù)鸭巴,在連線上也可以自定義業(yè)務(wù)
    那么是怎么實現(xiàn)這些需求的呢?為了滿足我們的業(yè)務(wù)拦盹,activiti提供了監(jiān)聽器鹃祖,下面詳細(xì)講解activiti監(jiān)聽器的使用。
    任務(wù)相應(yīng)時間包括:



    create:任務(wù)創(chuàng)建后觸發(fā)
    assignment:任務(wù)分配后觸發(fā)
    delete:任務(wù)完成后觸發(fā)
    all:所有事件發(fā)生都觸發(fā)
    表達(dá)式參考上篇的UEL表達(dá)式普舆,這里主要是介紹監(jiān)聽器的使用


    image.png

在class中指定我們的代碼中的監(jiān)聽器類

從activiti監(jiān)聽器的使用范圍惯豆,可以分為三種:

  1. 全局的監(jiān)聽器
  2. 連線的監(jiān)聽器
  3. 節(jié)點的監(jiān)聽器

全局監(jiān)聽

全局監(jiān)聽可以監(jiān)聽流程啟動,任務(wù)執(zhí)行節(jié)點之間的連線和流程結(jié)束奔害,
自定義實現(xiàn)全局監(jiān)聽的類需要實現(xiàn)ExecutionListener 接口楷兽,ExecutionListener 里定義的常量start用來判斷流程的開始,end是用來判斷流程的結(jié)束华临,take時判斷節(jié)點任務(wù)之間連線使用芯杀,也就是上一個節(jié)點完成任務(wù)后可以觸發(fā)。

public interface ExecutionListener extends Serializable {
    String EVENTNAME_START = "start";
    String EVENTNAME_END = "end";
    String EVENTNAME_TAKE = "take";

    void notify(DelegateExecution var1) throws Exception;
}

演示

流程圖


image.png

流程定義的xml文件

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" 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" xmlns:tns="http://www.activiti.org/test" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" expressionLanguage="http://www.w3.org/1999/XPath" id="m1596619898726" name="" targetNamespace="http://www.activiti.org/test" typeLanguage="http://www.w3.org/2001/XMLSchema">
<process id="stuKcsqServiceExemptionApply" isClosed="false" isExecutable="true" name="申請" processType="None">
<startEvent id="startevent1" name="Start"/>
<userTask activiti:assignee="$ {name}" activiti:exclusive="true" id="apply" name="提交申請"/>//
<userTask activiti:candidateUsers="${js}" activiti:exclusive="true" id="jsCheckService" name="教師審核"/>
<userTask activiti:candidateUsers="${wm}" activiti:exclusive="true" id="wmCheckAdmin" name="秘書審核">
<extensionElements>
<activiti:taskListener event="create"/>
</extensionElements>
</userTask>
<endEvent id="endevent1" name="End">
<extensionElements>
<activiti:executionListener class="com.graduate.platform.cultivate.listener.stuExemptionApplyListener" event="end"/>
</extensionElements>
</endEvent>
<userTask activiti:candidateUsers="${yjs}" activiti:exclusive="true" id="yjsCheckAdmin" name="培養(yǎng)辦審核"/>
<sequenceFlow id="flow1" sourceRef="startevent1" targetRef="apply"/>
<sequenceFlow id="flow2" sourceRef="apply" targetRef="jsCheckService">
<extensionElements>
<activiti:executionListener class="com.graduate.platform.cultivate.listener.stuExemptionApplyListener" event="take"/>
</extensionElements>
</sequenceFlow>
<sequenceFlow id="flow3" name="通過" sourceRef="jsCheckService" targetRef="wmCheckAdmin">
<extensionElements>
<activiti:executionListener class="com.graduate.platform.cultivate.listener.stuExemptionApplyListener" event="take"/>
</extensionElements>
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${state==1}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow4" name="通過" sourceRef="wmCheckAdmin" targetRef="yjsCheckAdmin">
<extensionElements>
<activiti:executionListener class="com.graduate.platform.cultivate.listener.stuExemptionApplyListener" event="take"/>
</extensionElements>
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${state==1}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow5" sourceRef="yjsCheckAdmin" targetRef="endevent1"/><sequenceFlow id="flow6" name="不通過" sourceRef="jsCheckService" targetRef="endevent1">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${state==0}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow7" name="不通過" sourceRef="wmCheckAdmin" targetRef="endevent1">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${state==0}]]></conditionExpression>
</sequenceFlow>

自定義監(jiān)聽器類

上面的流程定義中雅潭,我們在在結(jié)束跟每條連線上(除了不同意的分支揭厚,因為不同意分支流程會直接到達(dá)結(jié)束)設(shè)置了我們自定義的監(jiān)聽器類,自定義的監(jiān)聽器類負(fù)責(zé)的業(yè)務(wù)有扶供,當(dāng)前一個節(jié)點完成任務(wù)時監(jiān)聽器會根據(jù)“take”跟“end”來判斷節(jié)點的位置筛圆,當(dāng)連線處被觸發(fā)時也就是當(dāng)前任務(wù)節(jié)點完成了,可以動態(tài)的設(shè)置下一個節(jié)點任務(wù)的執(zhí)行人椿浓,在結(jié)束節(jié)點處判斷state狀態(tài)來處理業(yè)務(wù)申請是否通過太援。

public class stuExemptionApplyListener implements TaskListener, ExecutionListener {

    //操作數(shù)據(jù)庫需要
    ActivityService activityService = (ActivityService) ApplicationContextProvider.getBean("activityService");

    @Override
    public void notify(DelegateTask delegateTask) {
    }

    @Override
    public void notify(DelegateExecution execution) throws Exception {
        // TODO Auto-generated method stub
        String eventName = execution.getEventName();
        //申請人學(xué)號
        String xh = execution.getVariable("name").toString();
        String lcId = execution.getProcessInstanceId();
        String names="";
        String teacherId="";
        if ("take".equals(eventName)) {
            StudentInfo studentInfo  = studentInfoService.selectInfoByXh(xh);
            //獲取部門id
            String deptId=studentInfo.getYxsh();
            if(execution.getCurrentActivityId().equals("apply")){
                String teacherGh= execution.getVariable("js").toString();
                execution.setVariable(ActivityConstants.EXEMPTION_TEACHER, "admin,"+teacherGh);
            }
            if(execution.getCurrentActivityId().equals("jsCheckService")){
                teacherId= Constants.SECRETARY;
                //根據(jù)部門查詢對應(yīng)學(xué)院秘書用戶 拼接用戶名稱和所屬角色
                names = activityService.selectUsersByRole(teacherId, deptId);
                execution.setVariable("wm", names+",admin");
            }else if(execution.getCurrentActivityId().equals("wmCheckAdmin")){
                teacherId=Constants.CULTIVATEOFFICE;
                //培養(yǎng)辦 拼接用戶名稱和所屬角色
                names = activityService.selectUsersByRole(teacherId, "");
                execution.setVariable("yjs", names+",admin");//添加候選人,admin也可以做審核
            }
        }  else  if ("end".equals(eventName)) {
            String s=execution.getVariable("state")+"";
            int state = Integer.parseInt( s);
            StudentApplyService pyXskcsqService = (StudentApplyService ) ApplicationContextProvider.getBean("studentApplyService ");
            StudentApply studentApply =new StudentApply (lcId,state);
            //更新審核狀態(tài)
            studentApplyService.updateByLcid(studentApply );
        }
    }

}

下面我們來分析一下DelegateExecution這個對象扳碍,因為上面的監(jiān)聽器類實現(xiàn)的方法中可以拿到DelegateExecution這個對象提岔,那現(xiàn)在看看這個對象為我們提供了什么。

public interface DelegateExecution extends VariableScope {
    //流程id
    String getId();
    //流程實例id
    String getProcessInstanceId();
    // 用來獲取 start笋敞、end碱蒙、take
    String getEventName();
    // 獲取業(yè)務(wù)id  不建議使用了 廢棄
    /** @deprecated */
    String getBusinessKey();
    //獲取業(yè)務(wù)id
    String getProcessBusinessKey();
    //獲取流程定義的id
    String getProcessDefinitionId();
    // 獲取父id,聽說是并發(fā)的時候有用
    String getParentId();
    //獲取 調(diào)用執(zhí)行的id夯巷。
    String getSuperExecutionId();
    // 獲取當(dāng)前 ActivityId
    String getCurrentActivityId();
    // 獲取 當(dāng)前的 ActivityName
    String getCurrentActivityName();
    // 獲取 TenantId 有多個 tenant 有用
    String getTenantId();
    //這個就不用說了赛惩,可以獲取流程的所有核心service
    EngineServices getEngineServices();
}

節(jié)點監(jiān)聽器

自定義節(jié)點監(jiān)聽器類需要實現(xiàn)TaskListener接口,它跟全局監(jiān)聽器一樣趁餐,定義了不同的監(jiān)聽事件喷兼,分別為:

  • create:任務(wù)被創(chuàng)建且所有的屬性被設(shè)置好后可觸發(fā)
  • assignment:當(dāng)任務(wù)設(shè)置執(zhí)行人后觸發(fā)(注意 當(dāng)流程到達(dá)一個任務(wù)節(jié)點時,會先觸發(fā)assignment事件再觸發(fā)create時間)
  • complete:在任務(wù)完成后澎怒,且在數(shù)據(jù)庫中的運行時數(shù)據(jù)相關(guān)表刪除數(shù)據(jù)之前
  • delete:在任務(wù)將要被刪除之前發(fā)生褒搔。一般是completeTask完成任務(wù)時阶牍,它也會被執(zhí)行
  • all:以上的事件都可以觸發(fā)
public interface TaskListener extends Serializable {
    String EVENTNAME_CREATE = "create";
    String EVENTNAME_ASSIGNMENT = "assignment";
    String EVENTNAME_COMPLETE = "complete";
    String EVENTNAME_DELETE = "delete";
    String EVENTNAME_ALL_EVENTS = "all";

    void notify(DelegateTask var1);
}

使用方式跟全局監(jiān)聽器一樣,這里就不多做代碼演示星瘾,在節(jié)點監(jiān)聽器也可以動態(tài)的分配下一個任務(wù)的執(zhí)行人走孽,可以在任務(wù)的各種狀態(tài)下處理系統(tǒng)的業(yè)務(wù)邏輯。
分析一下DelegateTask對象

public interface DelegateTask extends VariableScope {
  //數(shù)據(jù)庫中的taskId主鍵
  String getId();
  
  // 任務(wù)名稱 
  String getName();
  
  //修改任務(wù)名稱 
  void setName(String name);
 
  //獲取任務(wù)的描述信息
  String getDescription();
  
  //修改任務(wù)的描述信息
  void setDescription(String description);
  
  // lower priority: [0..19] lowest, [20..39] low, [40..59] normal, [60..79] high
  // [80..100] highest
  //任務(wù)處理的優(yōu)先級范圍是0-100
  int getPriority();
  
  //修改優(yōu)先級
  void setPriority(int priority);
  
  // 獲取流程實例id 
  String getProcessInstanceId();
  
  //獲取執(zhí)行id
  String getExecutionId();
  
  // 獲取流程定義id
  String getProcessDefinitionId();
  // Adds the given user as a candidate user to this task.添加候選人
  void addCandidateUser(String userId);
  
  // 添加多個候選人
  void addCandidateUsers(Collection<String> candidateUsers);
  
  //添加候選組
  void addCandidateGroup(String groupId);
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末琳状,一起剝皮案震驚了整個濱河市磕瓷,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌念逞,老刑警劉巖困食,帶你破解...
    沈念sama閱讀 216,744評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異翎承,居然都是意外死亡硕盹,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評論 3 392
  • 文/潘曉璐 我一進店門叨咖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瘩例,“玉大人,你說我怎么就攤上這事甸各《庀停” “怎么了?”我有些...
    開封第一講書人閱讀 163,105評論 0 353
  • 文/不壞的土叔 我叫張陵趣倾,是天一觀的道長聘惦。 經(jīng)常有香客問我,道長儒恋,這世上最難降的妖魔是什么善绎? 我笑而不...
    開封第一講書人閱讀 58,242評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮碧浊,結(jié)果婚禮上涂邀,老公的妹妹穿的比我還像新娘瘟仿。我一直安慰自己箱锐,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,269評論 6 389
  • 文/花漫 我一把揭開白布劳较。 她就那樣靜靜地躺著驹止,像睡著了一般。 火紅的嫁衣襯著肌膚如雪观蜗。 梳的紋絲不亂的頭發(fā)上臊恋,一...
    開封第一講書人閱讀 51,215評論 1 299
  • 那天,我揣著相機與錄音墓捻,去河邊找鬼抖仅。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的撤卢。 我是一名探鬼主播环凿,決...
    沈念sama閱讀 40,096評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼放吩!你這毒婦竟也來了智听?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,939評論 0 274
  • 序言:老撾萬榮一對情侶失蹤渡紫,失蹤者是張志新(化名)和其女友劉穎到推,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體惕澎,經(jīng)...
    沈念sama閱讀 45,354評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡莉测,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,573評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了唧喉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片悔雹。...
    茶點故事閱讀 39,745評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖欣喧,靈堂內(nèi)的尸體忽然破棺而出腌零,到底是詐尸還是另有隱情,我是刑警寧澤唆阿,帶...
    沈念sama閱讀 35,448評論 5 344
  • 正文 年R本政府宣布益涧,位于F島的核電站,受9級特大地震影響驯鳖,放射性物質(zhì)發(fā)生泄漏闲询。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,048評論 3 327
  • 文/蒙蒙 一浅辙、第九天 我趴在偏房一處隱蔽的房頂上張望扭弧。 院中可真熱鬧,春花似錦记舆、人聲如沸鸽捻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽御蒲。三九已至,卻和暖如春诊赊,著一層夾襖步出監(jiān)牢的瞬間厚满,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評論 1 269
  • 我被黑心中介騙來泰國打工碧磅, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留碘箍,地道東北人遵馆。 一個月前我還...
    沈念sama閱讀 47,776評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像丰榴,于是被迫代替她去往敵國和親团搞。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,652評論 2 354