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 对象的开销,是没有进行多次配置文件的解析的。

猜你喜欢

转载自blog.csdn.net/lanfeng330/article/details/7059275