- Activiti權(quán)威指南
- 冀正
- 3483字
- 2020-11-28 15:59:54
2.3 構(gòu)造流程引擎實(shí)例對(duì)象
了解了流程配置文件的兩種配置方式和流程引擎配置類的全局架構(gòu)之后,接下來(lái)開(kāi)始講解如何構(gòu)造流程引擎實(shí)例對(duì)象,首先使用ProcessEngines類創(chuàng)建ProcessEngine實(shí)例對(duì)象,具體實(shí)現(xiàn)如代碼清單2-4所示,然后將上述兩個(gè)流程配置文件放置到項(xiàng)目的classpath根路徑中。
代碼清單2-4 ProcessEngineTest.java

以上構(gòu)造ProcessEngine實(shí)例對(duì)象的過(guò)程均是圍繞ProcessEngines.getDefault-ProcessEngine()這行代碼展開(kāi),getDefaultProcessEngine方法的相關(guān)實(shí)現(xiàn),如代碼清單2-5所示。
代碼清單2-5 ProcessEngines.java

getDefaultProcessEngine方法僅僅是調(diào)用getProcessEngine方法進(jìn)行下一步的處理,并傳入getProcessEngine方法需要的參數(shù)值default(流程引擎的名稱),接下來(lái)分析getProcessEngine方法的處理邏輯,該方法的相關(guān)實(shí)現(xiàn)如代碼清單2-6所示。
代碼清單2-6 ProcessEngines.java

getProcessEngine方法需要顯式設(shè)置流程引擎的名稱,因此開(kāi)發(fā)者可以直接調(diào)用該方法構(gòu)造流程引擎實(shí)例對(duì)象,該方法的執(zhí)行邏輯如下。
(1)代碼清單2-6中的第4行判斷ProcessEngine實(shí)例對(duì)象是否已經(jīng)被初始化,如果已經(jīng)被初始化,則第7行直接根據(jù)processEngineName參數(shù)值從processEngines集合中查找ProcessEngine實(shí)例對(duì)象并作為該方法的返回值。通過(guò)該操作可以知道,Activiti支持多個(gè)ProcessEngine實(shí)例對(duì)象一起運(yùn)行,只要流程引擎的名稱不一樣即可,相同名稱的流程引擎只會(huì)存在一個(gè)流程引擎實(shí)例,因?yàn)閜rocessEngines為Map數(shù)據(jù)結(jié)構(gòu),流程引擎名稱的配置可以參考代碼清單2-1。流程引擎名稱的默認(rèn)值為default。
(2)如果ProcessEngine實(shí)例對(duì)象沒(méi)有被初始化則調(diào)用init方法初始化ProcessEngine類,該方法的詳細(xì)處理過(guò)程如代碼清單2-7所示。
代碼清單2-7 ProcessEngines.java

下面根據(jù)代碼清單2-7概括init方法的處理邏輯。
(1)第3行再次確認(rèn)ProcessEngine實(shí)例對(duì)象是否已經(jīng)初始化,經(jīng)過(guò)確認(rèn)該實(shí)例對(duì)象沒(méi)有被初始化,則開(kāi)始執(zhí)行后續(xù)操作,否則不予處理。
(2)如果processEngines集合為空,則執(zhí)行第5行初始化該集合,該集合為Map數(shù)據(jù)結(jié)構(gòu),key為String類型,用于存儲(chǔ)流程引擎的名稱,value值為ProcessEngine實(shí)例對(duì)象,通過(guò)該集合的數(shù)據(jù)結(jié)構(gòu)可知,如果流程引擎的名稱相同則只會(huì)存儲(chǔ)一份ProcessEngine實(shí)例對(duì)象。
(3)第10行定位activiti.cfg.xml文件的位置,然后構(gòu)造ProcessEngine實(shí)例對(duì)象。文件定位的工作比較簡(jiǎn)單,首先獲取類加載器classLoader對(duì)象,然后委托classLoader. getResources("activiti.cfg.xml")方法定位流程配置文件,通過(guò)該方法的處理邏輯可知該文件必須位于項(xiàng)目的根目錄classpath中,第20行調(diào)用initProcessEnginFromResource方法構(gòu)造ProcessEngine實(shí)例對(duì)象。
(4)第23行根據(jù)classLoader定位activiti-context.xml文件的位置,然后開(kāi)始構(gòu)造ProcessEngine實(shí)例對(duì)象。如果該文件存在,第29行直接委托initProcessEngineFrom-SpringResource方法構(gòu)造ProcessEngine實(shí)例對(duì)象。
(5)以上操作執(zhí)行完畢后第31行調(diào)用setInitialized(true)方法,如果該方法執(zhí)行則表明構(gòu)造ProcessEngine實(shí)例對(duì)象工作已經(jīng)完畢。
上述為流程引擎初始化全過(guò)程概覽,接下來(lái)細(xì)化講解該過(guò)程中涉及的內(nèi)容,首先講解Activiti風(fēng)格配置的流程引擎初始化過(guò)程。
2.3.1 初始化流程引擎之Activiti配置風(fēng)格
initProcessEnginFromResource(resource)方法負(fù)責(zé)解析activiti.cfg.xml文件中配置的bean信息,該處理過(guò)程如代碼清單2-8所示。
代碼清單2-8 ProcessEngines.java

進(jìn)入initProcessEnginFromResource方法之后,Activiti并不著急構(gòu)造ProcessEngine實(shí)例對(duì)象,而是做了大量的準(zhǔn)備工作,根據(jù)代碼清單2-8,其處理流程總結(jié)如下。
(1)第2行根據(jù)resourceUrl參數(shù)值從processEngineInfosByResourceUrl集合中獲取元素值,如果獲取到,則第4行從processEngineInfos集合中移除該元素,第5行判斷processEngineInfo對(duì)象是否存在異常信息,如果不存在異常信息,第7行從processEngines集合中移除該元素,緊接著第8行從processEngineInfosByName集合中移除該元素,最后不論processEngineInfo對(duì)象有無(wú)異常信息,都會(huì)執(zhí)行第10行代碼,從processEngineInfosByResourceUrl集合中移除該元素。
(2)第14行將構(gòu)造ProcessEngine實(shí)例對(duì)象的工作交給buildProcessEngine方法完成。
(3)第15~23行開(kāi)始向集合中添加元素,首先獲取流程引擎的名稱并實(shí)例化ProcessEngineInfoImpl類,然后分別對(duì)集合processEngineInfosByName、processEngine-InfosByResourceUrl、processEngineInfos執(zhí)行添加操作。
2.3.2 構(gòu)造流程引擎實(shí)例對(duì)象
上面提到了調(diào)用buildProcessEngine方法構(gòu)造ProcessEngine實(shí)例對(duì)象,接下來(lái)詳細(xì)分析該方法的處理過(guò)程,如代碼清單2-9所示。
代碼清單2-9 ProcessEngines.java

在buildProcessEngine(URLresource)方法中代碼清單2-9中的第4行代碼打開(kāi)流程配置文件的數(shù)據(jù)流,第5、第6行開(kāi)始創(chuàng)建processEngineConfiguration實(shí)例對(duì)象,第7行調(diào)用processEngineConfiguration對(duì)象的buildProcessEngine方法構(gòu)造ProcessEngine實(shí)例對(duì)象,最后執(zhí)行第11行代碼關(guān)閉文件流,雖然很簡(jiǎn)單卻回收了資源。接下來(lái)重點(diǎn)分析第5行和第6行代碼的實(shí)現(xiàn)邏輯。
2.3.3 創(chuàng)建流程引擎配置類實(shí)例
ProcessEngineConfiguration.createProcessEngineConfigurationFromInputStream(inputStream)方法完成流程引擎配置類ProcessEngineConfiguration的實(shí)例化工作,詳細(xì)過(guò)程如代碼清單2-10所示。
代碼清單2-10 ProcessEngineConfiguration.java

上述代碼中,第2行直接調(diào)用第4行定義的方法進(jìn)行處理,并傳入beanName參數(shù)值processEngineConfiguration,該參數(shù)值正是代碼清單2-1中定義的bean的id值,第4行定義的方法處理邏輯也非常簡(jiǎn)單,直接委托BeansConfigurationHelper類中的parseProcessEngineConfigurationFromInputStream(inputStream, beanName)方法進(jìn)行處理,該類的核心定義如代碼清單2-11所示。
代碼清單2-11 BeansConfigurationHelper.java

通過(guò)分析BeansConfigurationHelper類中定義的三個(gè)方法可以得出,Activiti底層通過(guò)該類巧妙地將流程配置文件中定義的bean全部交給Spring容器進(jìn)行管理(包括類的配置、加載和獲取操作),結(jié)合圖2-3總結(jié)parseProcessEngineConfiguration方法的執(zhí)行邏輯。

圖2-3 Activiti類的配置、加載和獲取操作
(1)第3行實(shí)例化DefaultListableBeanFactory類。
(2)第4行實(shí)例化XmlBeanDefinitionReader類,并將beanFactory設(shè)置到xmlBeanDefinitionReader對(duì)象中,該類非常重要,Spring框架使用XmlBeanDefinitionReader類讀取和解析XML文件。
(3)第6行設(shè)置xmlBeanDefinitionReader對(duì)象的驗(yàn)證模式為XSD。
(4)第8行xmlBeanDefinitionReader.loadBeanDefinitions(springResource)方法加載配置文件activiti.cfg.xml中定義的所有bean。
(5)第10行使用beanFactory對(duì)象的getBean方法獲取ProcessEngineConfigurationImpl實(shí)例對(duì)象,對(duì)應(yīng)代碼清單2-1配置的StandaloneProcessEngineConfiguration類,這就是最典型的Spring獲取bean實(shí)例對(duì)象的操作方式。實(shí)例化StandaloneProcessEngineConfiguration類的同時(shí)會(huì)初始化其父類ProcessEngineConfigurationImpl中的各種屬性值,如代碼清單2-12所示。
代碼清單2-12 ProcessEngineConfigurationImpl.java

以上這些服務(wù)類在實(shí)際項(xiàng)目開(kāi)發(fā)中經(jīng)常使用到,但通常是根據(jù)EngineServices實(shí)例對(duì)象獲取以上這些服務(wù)類的實(shí)例對(duì)象,那么為什么這些服務(wù)類的實(shí)例化工作在ProcessEngineConfigurationImpl類中進(jìn)行呢?暫且留下一個(gè)懸念,稍后講解。
(6)實(shí)例化SpringBeanFactoryProxyMap類,該類的定義如代碼清單2-13所示。
代碼清單2-13 SpringBeanFactoryProxyMap.java

該類實(shí)現(xiàn)了Map接口,其內(nèi)部持有BeanFactory實(shí)例對(duì)象,該實(shí)例對(duì)象主要是用于獲取配置文件中定義的bean實(shí)例對(duì)象,所以只要開(kāi)發(fā)人員能夠獲取到SpringBeanFactoryProxyMap實(shí)例對(duì)象,就可以獲取到流程配置文件中定義的所有bean實(shí)例對(duì)象,Activiti這樣設(shè)計(jì)很精巧,由衷感謝Activiti框架給開(kāi)發(fā)者提供如此優(yōu)秀的功能。
(7)將SpringBeanFactoryProxyMap實(shí)例對(duì)象設(shè)置到processEngineConfiguration對(duì)象中,因此只要能夠獲取processEngineConfiguration對(duì)象,就可以通過(guò)該對(duì)象的getBeans方法獲取流程配置文件中定義的所有bean實(shí)例對(duì)象,不過(guò)由于這個(gè)地方Activiti使用的是Map數(shù)據(jù)結(jié)構(gòu),因此使用時(shí)需要進(jìn)行類型轉(zhuǎn)換。
2.3.4 初始化流程引擎
代碼清單2-9中,buildProcessEngine()方法用于創(chuàng)建ProcessEngine實(shí)例對(duì)象,因?yàn)閍ctiviti.cfg.xml配置文件中定義的流程引擎配置類為StandaloneProcessEngineConfiguration,但是該類中并沒(méi)有定義buildProcessEngine方法,那么buildProcessEngine方法肯定在其父類中進(jìn)行了實(shí)現(xiàn),接下來(lái)分析該方法在其父類中的處理邏輯,如代碼清單2-14所示。
代碼清單2-14 ProcessEngineConfigurationImpl.java

該方法的處理邏輯總結(jié)如下。
(1)調(diào)用init方法初始化各種屬性值。
(2)實(shí)例化ProcessEngineImpl類。
2.3.5 初始化流程引擎之Spring配置風(fēng)格
上述一系列的講解均是針對(duì)Activiti配置文件的風(fēng)格構(gòu)造ProcessEngine實(shí)例對(duì)象,接下來(lái)詳細(xì)分析Spring風(fēng)格的配置文件activiti-context.xml(可以參考2.1.2節(jié))構(gòu)造ProcessEngine實(shí)例對(duì)象的整個(gè)過(guò)程,以下講解均是圍繞代碼清單2-7中的initProcess-EngineFromSpringResource(resource)方法展開(kāi)的,該方法的實(shí)現(xiàn)如代碼清單2-15所示。
代碼清單2-15 ProcessEngines.java

initProcessEngineFromSpringResource方法需要接收一個(gè)resource參數(shù),其處理邏輯如下。
(1)第3行委托ReflectUtil類的靜態(tài)方法loadClass加載SpringConfigurationHelper類(該類位于activiti-spring-5.21.0.jar包中)。關(guān)于類加載機(jī)制本書(shū)不詳細(xì)講解,其相關(guān)實(shí)現(xiàn)可跟進(jìn)loadClass方法進(jìn)行查看。
(2)第4行通過(guò)springConfigurationHelperClass對(duì)象查找SpringConfigurationHelper類中的buildProcessEngine方法。
(3)第5行通過(guò)反射方式調(diào)用SpringConfigurationHelper類中的buildProcessEngine方法,然后使用processEngine變量存儲(chǔ)該方法的返回值。
(4)第6行獲取流程引擎的名稱。
(5)以上步驟執(zhí)行完后,第7~9行將流程引擎的詳細(xì)信息添加到processEngineInfosBy-Name和processEngineInfosByResourceUrl集合中。
2.3.5.1 反射構(gòu)造ProcessEngine
上面代碼通過(guò)反射方式調(diào)用SpringConfigurationHelper類中的buildProcessEngine方法,該方法的定義如代碼清單2-16所示。
代碼清單2-16 SpringConfigurationHelper.java

該方法的處理邏輯總結(jié)如下。
(1)第2行加載activiti-context.xml文件。
(2)第3行獲取類型為ProcessEngine的實(shí)例對(duì)象,第4~5行如果activiti-context.xml文件中沒(méi)有定義類型為ProcessEngine的bean則程序報(bào)錯(cuò),第7行如果發(fā)現(xiàn)activiti-context.xml文件中配置了多個(gè)類型為ProcessEngine的bean,則從beansOfType集合中取出第一個(gè)ProcessEngine實(shí)例對(duì)象并作為方法的返回值,從beansOfType集合的處理邏輯可以看出流程配置文件中不管定義多少個(gè)ProcessEngine類,程序只會(huì)使用一個(gè)而且是第一個(gè)。接下來(lái)重點(diǎn)分析activiti-context.xml文件中定義的ProcessEngineFactoryBean類是如何被Spring實(shí)例化的,首先查看代碼清單2-2中定義的ProcessEngineFactoryBean,該工廠類負(fù)責(zé)生成ProcessEngine實(shí)例對(duì)象,暫且將關(guān)注點(diǎn)放到ProcessEngineFactoryBean類的getObject方法的處理邏輯中,如代碼清單2-17所示。
代碼清單2-17 ProcessEngineFactoryBean.java

該方法的處理邏輯總結(jié)如下。
(1)第3行調(diào)用configureExpressionManager方法設(shè)置表達(dá)式管理器。
(2)第4行調(diào)用configureExternallyManagedTransactions方法設(shè)置事務(wù)管理器,具體實(shí)現(xiàn)如代碼清單2-18所示。
代碼清單2-18 ProcessEngineFactoryBean.java

如果processEngineConfiguration對(duì)象為SpringProcessEngineConfiguration實(shí)例對(duì)象,則進(jìn)行如下處理,否則不予理會(huì)。
? 將processEngineConfiguration對(duì)象轉(zhuǎn)換為SpringProcessEngineConfiguration實(shí)例對(duì)象。
? 如果engineConfiguration對(duì)象中的getTransactionManager方法返回值不為空,則將事務(wù)交給Spring框架管理,可以參考12.8節(jié)。通過(guò)這里的處理可以發(fā)現(xiàn),如果使用Spring管理事務(wù),則流程引擎配置類必須為SpringProcessEngineConfiguration。
(3)第9行開(kāi)始構(gòu)造流程引擎實(shí)例對(duì)象,processEngineConfiguration為SpringProcess-EngineConfiguration類型。buildProcessEngine方法的處理邏輯如代碼清單2-19所示。
代碼清單2-19 SpringProcessEngineConfiguration.java

buildProcessEngine方法的處理邏輯總結(jié)如下。
? 第2行委托父類構(gòu)造ProcessEngine實(shí)例對(duì)象,因?yàn)楫?dāng)前類SpringProcessEngine-Configuration的父類為ProcessEngineConfigurationImpl,所以該操作會(huì)觸發(fā)代碼清單2-14中的邏輯。
? 第3行通知ProcessEngines類,ProcessEngine實(shí)例已經(jīng)初始化。
? 第4行開(kāi)始自動(dòng)部署流程資源。
Activiti整合Spring框架時(shí),提供了自動(dòng)部署資源的特性,這樣流程引擎啟動(dòng)時(shí)會(huì)自動(dòng)將指定的資源文件部署到數(shù)據(jù)庫(kù)中,具體的使用方式可以參照代碼清單2-2中的配置,autoDeployResources(processEngine)方法的處理邏輯比較簡(jiǎn)單,本書(shū)不做過(guò)多解釋。
注意
ProcessEngineFactoryBean類實(shí)現(xiàn)了FactoryBean接口。
- 編程卓越之道(卷3):軟件工程化
- C和C++安全編碼(原書(shū)第2版)
- 編程珠璣(續(xù))
- OpenStack Cloud Computing Cookbook(Fourth Edition)
- Practical Windows Forensics
- Python金融數(shù)據(jù)分析
- Xamarin.Forms Projects
- FFmpeg入門(mén)詳解:音視頻原理及應(yīng)用
- PhpStorm Cookbook
- Python機(jī)器學(xué)習(xí)算法與實(shí)戰(zhàn)
- Node.js全程實(shí)例
- 零基礎(chǔ)入門(mén)學(xué)習(xí)Python(第2版)
- Mastering Android Game Development
- PHP從入門(mén)到精通(第4版)(軟件開(kāi)發(fā)視頻大講堂)
- ElasticSearch Cookbook(Second Edition)