天天看點

osworkflow學習記錄

 因工作需要,學習了下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 對象的開銷,是沒有進行多次配置檔案的解析的。