因工作需要,學習了下osworkflow。網絡上的資料到是有很多。但很多都是大同小異。而《osworkflow開發指南》說實話,并不适合入門。到是看到另一個入門教程,覺得很好。循序漸進,由淺入深。有興趣的可以去看下。
http://www.blogjava.net/alex/archive/2006/08/15/63715.html
看完這個教程後,突然有種疑問,例子中是一個請假流程的示範,這個流程應該很好了解,很熟悉。做入門的例子講解很好。
在不同的調用者執行流程中的步驟時,都是重新建立BaseWorkflow對象,和DefaultConfiguration對象的。
如:send方法,和allow方法中都是這樣處理的。這樣是不是很耗資源呢?每次建立對象,都會涉及到流程的一些配置檔案的加載、解析。
看了下源碼,了解了一下大緻的流程:
在開始流程的時候,建立完BaseWorkflow對象後,會調用initalize方法進行初始化,
這一步,應該是再資料庫建立一些基本資訊,存儲流程的基本資訊,如流程名,狀态等。同時傳回一個流程Id值,
這個值很關鍵,相當于流程執行個體的句柄。
在其後的流程執行中基本就是對流程執行個體對象的doAction()方法的調用了。該方法的參數中有個就是前面說的:流程Id值--workflowId。
通過workflowId到資料庫中查詢對應的workflowName。這個workflowName也就是workflows.xml中的:
<workflow name="leave" type="resource" location="leave.xml"/>
name屬性。這樣也就可以确定現在的操作是在哪個流程上進行的。
開始前面的疑問的探讨。在調用BaseWorkflow的initalize和doAction方法時都會調用其父類【AbstractWorkflow】的一個方法getConfiguration(),其他很多地方也調用到該方法。
public Configuration getConfiguration() {
Configuration config = (configuration != null) ? configuration : DefaultConfiguration.INSTANCE;
if (!config.isInitialized()) {
try {
config.load(null);
} catch (FactoryException e) {
log.fatal("Error initialising configuration", e);
//fail fast, better to blow up with an NPE that hide the error
return null;
}
}
return config;
}
由源碼可以看到,在不調用BaseWorkflow的setConfiguration方法時,AbstractWorkflow的configuration屬性一直是空的。也就會傳回DefaultConfiguration.INSTANCE對象。
DefaultConfiguration.INSTANCE是一種單例模式的應用。該類隻要加載進記憶體,就有一個DefaultConfiguration執行個體對象了,隻是這個執行個體對象是沒有初始化的,沒有加載,解析流程的相關配置檔案的。
向後就有對這個config 對象是初始化的判斷。如果沒有初始化,就進行初始化----config.load()。
load方法中做了兩件事(此處以DefaultConfiguration為例):
1.完成對自身的初始化---加載,解析osworkflow.xml配置檔案
2.完成對自身的WorkflowFactory類型的屬性factory的配置及factory自身的初始化。
如下源碼:
Element root = (Element) doc.getElementsByTagName("osworkflow").item(0);
Element p = XMLUtil.getChildElement(root, "persistence");
Element resolver = XMLUtil.getChildElement(root, "resolver");
Element factoryElement = XMLUtil.getChildElement(root, "factory");
以下源碼完成對factory的配置:
try {
clazz = factoryElement.getAttribute("class");
if (clazz == null) {
throw new FactoryException("factory does not specify a class attribute");
}
factory = (WorkflowFactory) ClassLoaderUtil.loadClass(clazz, getClass()).newInstance();
factory 預設的執行個體類型為URLWorkflowFactory,在此處就被換成osworkflow.xml配置檔案中指定的具體類型了---XMLWorkflowFactory。
<factory class="com.opensymphony.workflow.loader.XMLWorkflowFactory">
<property key="resource" value="workflows.xml" />
</factory>
XMLWorkflowFactory,URLWorkflowFactory都是實作了WorkflowFactory接口的具體類。
在第二個任務中:factory的配置及factory自身的初始化。看以下源碼:
factory.init(properties); //properties存儲有 <property key="resource" value="workflows.xml" />節點資訊
factory.initDone();
在factory.initDone()方法中完成對workflows.xml檔案的,加載,解析。并存儲在自身的緩存中(一個Map對象),
key值就是workflows.xml中的name屬性值。
<workflow name="leave" type="resource" location="leave.xml"/>
value值就是對該節點的解析完後生産的一個對象WorkflowConfig
以下為解析workflows.xml的源碼
Element root = (Element) doc.getElementsByTagName("workflows").item(0);
workflows = new HashMap();
String basedir = getBaseDir(root);
List list = XMLUtil.getChildElements(root, "workflow");
for (int i = 0; i < list.size(); i++) {
Element e = (Element) list.get(i);
WorkflowConfig config = new WorkflowConfig(basedir, e.getAttribute("type"), e.getAttribute("location"));
workflows.put(e.getAttribute("name"), config);
注:此時還沒有完成對具體流程定義檔案的加載解析。
前面說到很多地方調用了getConfiguration(),而調用完後,會接着調用Configuration的getWorkflow(workflwoName)方法。
看下這個方法會做些什麼。
getWorkflow方法最終是調用的WorkflowFactory中的同名方法。此處也就是XMLWorkflowFactory中的getWorkflow方法
WorkflowConfig c = (WorkflowConfig) workflows.get(name); //@1
if (c == null) {
throw new FactoryException("Unknown workflow name \"" + name + '\"');
}
if (c.descriptor != null) { //@2
if (reload) {
File file = new File(c.url.getFile());
if (file.exists() && (file.lastModified() > c.lastModified)) {
c.lastModified = file.lastModified();
loadWorkflow(c, validate);
}
}
} else {
loadWorkflow(c, validate);
}
c.descriptor.setName(name);
return c.descriptor;
在@1處,先從緩存中取出具體流程定義檔案的配置資訊。主要就是:類型,名稱,路徑等。
由前面的分析知道,這個配置資訊對象---WorkflowConfig隻是存儲了流程定義檔案的名稱,路徑等資訊,并沒有加載,解析具體的流程定義檔案。
是以在@2處進行的判斷就相當于是判斷流程定義檔案是否解析了。
如果解析了但需要重新解析,或者根本就沒有解析,那就進行解析。
從這也可以看出,新版支援流程檔案的熱修改。也就是修改了流程定義檔案,不需要重新開機伺服器。
整個流程大緻就是這樣了。
那麼前面的疑問也有初步的解釋了。
BasicWorkflow 繼承 AbstractWorkflow,AbstractWorkflow持有Configuration對象。并擁有getConfiguration方法。而getConfiguration在BasicWorkflow 沒有調用
setConfiguration方法時,傳回的是一個單例的Configuration對象。
這樣多次建立BasicWorkflow 對象的開銷,是沒有進行多次配置檔案的解析的。