activiti 動態配置 activiti 監聽引擎啟動和初始化(高階原始碼篇)
1.1.1. 前言
使用者故事:現在有這樣一個需求,第一個需求:公司的開發環境,測試環境以及線上環境,我們使用的資料庫是不一樣的,我們必須能夠任意的切換資料庫進行測試和釋出,對資料庫連線字串我們需要加密,保證我們的資料庫連線不能被發現。必須確保我們的資料庫不能暴露出去,第二個需求,我們需要監控activiti 工作流引擎,在流程啟動的之前,我們要保證我們的所有屬性都注入進去,如果有些屬性沒有注入進去,我們是不能讓流程啟動起來的。也就是進行必要餓屬性檢測,如果沒有期望的屬性,直接報錯,在流程例項化之後,輸出log日誌,確保我們的屬性都是正確的。並且監控引擎中所有的值。
第一個需求如何解決呢?
我們來看一下常規的資料庫配置,程式的配置如下:
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/activiti"/>
<property name="jdbcDriver" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUsername"
<property name="jdbcPassword" value="123"/>
</bean>
看到上面的配置,是不是似曾相識的感覺,沒錯就是這麼簡單,但是如何確保能任意的切換呢,仔細想想還是很簡單的,那裡變化隔離那裡嘛,這個時候我們可以把這些這些變化的東西隔離出去,放到jdbc.properties中,新建jdbc.properties把變化的內容設定進去到jdbc.properties,jdbc.properties內容如下:
jdbcUrl=jdbc:mysql://localhost:3306/activiti
jdbcDriver=com.mysql.jdbc.Driver
jdbcUsername=root
jdbcPassword=123
上面把變化的東西隔離出來,那程式怎麼能夠使用jdbc.properties中的配置資訊呢?這是一個很關鍵的問題,我們之前用的activiti版本是5.12,所以當時的設計就是修改原始碼,主要修改org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl類,在init()方法中我們設計一個鉤子函式,然後子類負責實現上面的這些功能,主要就修改的類是org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl,修改ProcessEngineConfigurationImpl類中的資料庫連線的資訊,由於目前最新的版本activiti 5.19版本,這個版本已經支援了這種實現,具體的實現都差不多,只是我們不用修改原始碼,直接擴充套件了,這不就是軟體設計中的開閉原則。對新增開放對修改關閉。
下面看下activiti 5.19是如何可以實現這種功能需求的。
1.1.2. activiti 原始碼
首先看一下org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl類中的init()方法哪個可以實現這種需求,瞭解底層核心實現才能很方便的擴充套件嘛。
ProcessEngineConfigurationImpl中的init()部分方法如下:
//初始化configurators集合
initConfigurators();
//呼叫configurator.beforeInit方法
configuratorsBeforeInit();
...
//呼叫configurator.configure()方法
configuratorsAfterInit();
上面的三個核心方法實現如下所示:
protected List<ProcessEngineConfigurator> configurators;
protected List<ProcessEngineConfigurator> allConfigurators;
protected boolean enableConfiguratorServiceLoader = true; // Enabled by default. In certain environments this should be set to false (eg osgi)
protected void initConfigurators() {
//初始化陣列集合
allConfigurators = new ArrayList<ProcessEngineConfigurator>();
// Configurators that are explicitely added to the config
//如果集合存在值就迴圈新增進去
if (configurators != null) {
for (ProcessEngineConfigurator configurator : configurators) {
allConfigurators.add(configurator);
}
}
// Auto discovery through ServiceLoader 預設是true
if (enableConfiguratorServiceLoader) {
ClassLoader classLoader = getClassLoader();
if (classLoader == null) {
classLoader = ReflectUtil.getClassLoader();
}
//迴圈新增進去
ServiceLoader<ProcessEngineConfigurator> configuratorServiceLoader
= ServiceLoader.load(ProcessEngineConfigurator.class, classLoader);
int nrOfServiceLoadedConfigurators = 0;
for (ProcessEngineConfigurator configurator : configuratorServiceLoader) {
allConfigurators.add(configurator);
nrOfServiceLoadedConfigurators++;
}
if (nrOfServiceLoadedConfigurators > 0) {
log.info("Found {} auto-discoverable Process Engine Configurator{}", nrOfServiceLoadedConfigurators++, nrOfServiceLoadedConfigurators > 1 ? "s" : "");
}
//如果集合不為空 存在多個值的話就按照升序排列 根據什麼作為排序規則呢,很顯然根據getPriority中的值進行排序 比如a類getPriority 10 b類getPriority 100 那麼排序後就是 a,b現執行a類對的再執行b類的。
if (!allConfigurators.isEmpty()) {
// Order them according to the priorities (usefule for dependent configurator)
Collections.sort(allConfigurators, new Comparator<ProcessEngineConfigurator>() {
@Override
public int compare(ProcessEngineConfigurator configurator1, ProcessEngineConfigurator configurator2) {
int priority1 = configurator1.getPriority();
int priority2 = configurator2.getPriority();
if (priority1 < priority2) {
return -1;
} else if (priority1 > priority2) {
return 1;
}
return 0;
}
});
// Execute the configurators
log.info("Found {} Process Engine Configurators in total:", allConfigurators.size());
for (ProcessEngineConfigurator configurator : allConfigurators) {
log.info("{} (priority:{})", configurator.getClass(), configurator.getPriority());
}
}
}
}
//迴圈所有allConfigurators 執行beforeInit方法
protected void configuratorsBeforeInit() {
for (ProcessEngineConfigurator configurator : allConfigurators) {
log.info("Executing beforeInit() of {} (priority:{})", configurator.getClass(), configurator.getPriority());
configurator.beforeInit(this);
}
}
//迴圈所有allConfigurators 執行configuratorsAfterInit方法
protected void configuratorsAfterInit() {
for (ProcessEngineConfigurator configurator : allConfigurators) {
log.info("Executing configure() of {} (priority:{})", configurator.getClass(), configurator.getPriority());
configurator.configure(this);
}
}
1.1.3. activiti 動態配置實戰
好了上面說了原始碼的實現,下面來點乾貨吧,看看如何使用自定義的類實現修改配置檔案。
我們使用兩個類看他們的執行過程是否跟上面原始碼解析的一直:
定義com.daling.ch1.init.MyProcessEngineConfigurator1如下所示:
@Service
public class MyProcessEngineConfigurator1 extends AbstractProcessEngineConfigurator{
public static int DEFAULT_CONFIGURATOR_PRIORITY = 100;
public void beforeInit(
ProcessEngineConfigurationImpl processEngineConfiguration) {
System.out.println("1111111111111");
}
public void configure(
ProcessEngineConfigurationImpl processEngineConfiguration) {
}
@Override
public int getPriority() {
return DEFAULT_CONFIGURATOR_PRIORITY;
}
}
定義com.daling.ch1.init.MyProcessEngineConfigurator2如下所示:
@Service
public class MyProcessEngineConfigurator2 extends AbstractProcessEngineConfigurator{
public static int DEFAULT_CONFIGURATOR_PRIORITY = 200;
public void beforeInit(
ProcessEngineConfigurationImpl processEngineConfiguration) {
System.out.println("2222222222222222");
}
public void configure(
ProcessEngineConfigurationImpl processEngineConfiguration) {
}
@Override
public int getPriority() {
return DEFAULT_CONFIGURATOR_PRIORITY;
}
}
在spring中開啟 包的掃描程式碼: <context:component-scan base-package="com"/>
activiti 引擎配置如下所示:
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<property name="jdbcUrl" value="jdbc:mysql://localhost3306/activiti"></property>
<property name="jdbcDriver" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUsername" value="root"/>
<property name="jdbcPassword" value="a"/>
<property name="configurators">
<list>
<bean class="com.daling.ch1.init.MyProcessEngineConfigurator2"></bean>
<bean class="com.daling.ch1.init.MyProcessEngineConfigurator1"></bean>
</list>
</property>
<property name="activityFontName" value="宋體"></property>
</bean>
測試程式碼如下:
ProcessEnginesDemodemo =newProcessEnginesDemo();
RepositoryServicerepositoryService2 =demo.getRepositoryService();
先來執行一下程式看輸出是否正確,部分輸出如下所示:
1111111111111
20:20:18.245 [main] INFO o.a.e.i.c.ProcessEngineConfigurationImpl - Executing beforeInit() of class com.daling.ch1.init.MyProcessEngineConfigurator2 (priority:200)
2222222222222222
確實說明了程式的執行順序就是按照getPriority()方法值升序執行的。
ProcessEngineConfigurator類的定義如下:
public interface ProcessEngineConfigurator {
/**
* Called <b>before</b> any initialisation has been done.
* This can for example be useful to change configuration settings
* before anything that uses those properties is created.
*
* Allows to tweak the process engine by passing the {@link ProcessEngineConfigurationImpl}
* which allows tweaking it programmatically.
*
* An example is the jdbc url. When a {@link ProcessEngineConfigurator} instance
* wants to change it, it needs to do it in this method, or otherwise
* the datasource would already have been created with the 'old' value
* for the jdbc url.
*/
void beforeInit(ProcessEngineConfigurationImpl processEngineConfiguration);
/**
* Called when the engine boots up, before it is usable, but after
* the initialisation of internal objects is done.
*
* Allows to tweak the process engine by passing the {@link ProcessEngineConfigurationImpl}
* which allows tweaking it programmatically.
*
* An example is the ldap user/group manager, which is an addition to the engine.
* No default properties need to be overridden for this (otherwise the {@link #beforeInit(ProcessEngineConfigurationImpl)}
* method should be used) so the logic contained in this method is executed
* after initialisation of the default objects.
*
* Probably a better name would be 'afterInit' (cfr {@link #beforeInit(ProcessEngineConfigurationImpl)}),
* but not possible due to backwards compatibility.
*/
void configure(ProcessEngineConfigurationImpl processEngineConfiguration);
/**
* When the {@link ProcessEngineConfigurator} instances are used, they are first
* ordered by this priority number (lowest to highest).
* If you have dependencies between {@link ProcessEngineConfigurator}
* instances, use the priorities accordingly to order them as needed.
*/
int getPriority();
}
ProcessEngineConfigurator類圖關係如下:
上面的類圖AbstractProcessEngineConfigurator類實現了ProcessEngineConfigurator介面,AbstractProcessEngineConfigurator是一個抽象類,所以我們使用的時候繼承AbstractProcessEngineConfigurator類複寫裡面的方法即可(模板方法)。
上面我們自定義的類MyProcessEngineConfigurator1中的方法
publicvoidbeforeInit(
ProcessEngineConfigurationImplprocessEngineConfiguration){
System.out.println("1111111111111");
}
publicvoidconfigure(
ProcessEngineConfigurationImplprocessEngineConfiguration){
}
我們可以拿到ProcessEngineConfigurationImpl這個物件其實就是StandaloneProcessEngineConfiguration的父類,裡面有很多的屬性我們就可以修改了。
怎麼修改呢?只要拿到這個物件我們就很方便修改了。
具體的修改如下:
//虛擬碼 載入jdbc.properties檔案 獲取配置的值,進行解密然後設定進去 解密演算法後續講解,當然懂的話可以自己直接修改。
descJdbc(ProperUtils.load());
processEngineConfiguration.setJdbcDriver(jdbcDriver);
processEngineConfiguration.setJdbcPassword(jdbcPassword);
processEngineConfiguration.setJdbcUrl(jdbcUrl);
processEngineConfiguration.setJdbcUsername(jdbcUsername)
上面提到的引擎初始化完畢會呼叫configure()中的方法,所以這裡可以自定義實現的功能即可。