在操作层协调java服务简介
摘要
迄今为止,web应用程序开发的焦点在于将业务逻辑封装成服务。在这篇文章中,Masayuki Otoshi建议将业务流程也剥离出来,就像那些业务过程治理/工作流产品一样,应用基于xml的文档来描述业务。但是这里他深入到了更低的粒度-操作。这篇文章同时展示了可继续的XML如何容许开发人员应用面向对象的概念去有效的表示流程。
在开发web应用程序的过程中,我们经常看到业务流程和逻辑在action中一起被实现,比如JSF中的后台bean和Struts中的action类。在现有框架的帮助下,比如EJB和SPRing,我们能把业务逻辑剥离出来,但是业务流程始终还是嵌入在具体操作中。
BPM(业务流程治理)标准,比如BPMN(业务流程建模符号)和BPEL(业务流程执行语言),提供了一种分离业务流程的途径,那就是应用基于XML文档来描述这种分离。这种方法的另外一个好处可以在SOA(面向服务架构)基础上设计应用程序。但是,这种方法使得在web应用程序不能很好地应用action.actoin的粒度对于BPM/工作流产品来讲太低了。他们通常专注于更高的业务范围,如B2B应用程序和企业级的应用整合,而且他们假定业务分析人员会按照图1所示的方法来描述流程。但是在更低的粒度上,比如action,流程再用的可能性更大。
图1. 粒度比较
在这篇文章中,对于比较小的业务需求范畴,我建议java开发人员使用J-SOFA(Java Services Orchestration for Actions, Action级JAVA服务协调)。J-SOFA是一种协调服务的框架,这里的服务对应于类中的一个方法,无论是POJO(简单洁净Java对象)或者web服务。
由于粒度不同,J-SOFA并不支持消息,状态治理,监控等等的同步。但是不用担心,目前的BPM/工作流产品都支持这些功能,我们可以直接应用这些产品。这篇文章所讲到的服务协调框架主要关注于提供业务流程的可用性,就像服务那样。
图2说明了剥离的业务流程可以被其他应用程序重复利用。
图 2. 可重用的业务流程及服务
版权声明:任何获得Matrix授权的网站,转载时请务必保留以下作者信息和链接
作者:Masayuki Otoshi ;rainh95(作者的blog:http://blog.matrix.org.cn/page/rainh95)
原文:http://www.javaworld.com/javaworld/jw-04-2006/jw-0417-sofa.Html?lsrc=jwrss
Matrix:http://www.matrix.org.cn/resource/article/44/44500_Business+Services.html
要害字:Business;Services
JSF中的简单action
让我们来看看用JSF开发的web应用程序中的一些简单action的代码。我们的例子是一个简单的模型搜索程序:根据用户输入的模型ID返回模型具体信息。
你可以从这个资源下载这个示例的源代码。
在搜索jsp页面上,有一个文本框和一个submit按钮,用户可以输入model id然后提交。这个jsp页面通过一个叫ModelBean的后台bean调用showModel()方法。如列表1所示:
列表 1. search.jsp中的inputText及Submit按钮
<h:inputText id="modelId" value="#{ModelBean.modelId}" />
<h:commandButton type="submit" value="Submit" action="#{ModelBean.showModel}" />
为了产生模型具体信息页面(搜索结果页面),showModel()方法创建Model对象和特征表,再赋值到属性当中
列表 2. 在backing bean中的showModel()方法
public String showModel() {
if (modelId > 0) {
ModelService modelService = new ModelService();
BeanUtils.copyProperties(this, modelService.create(modelId));
setFeatures(modelService.getFeatures(modelId));
}
...
}
高级开发人员可以像上面展示的代码一样将业务逻辑从具体操作中分离出来,通过一个model的service实现创建model和features,再通过interface来调用它。不管怎样,假如其他人来维护后台bean,我们还能保持这个方法这样简单吗?这样做可能被证实非常困难,因为不是所有的开发人员都明白隔离展现层和业务层的好处。假如一个持有不同观点的开发人员开发维护后台bean,她/他可能会将业务逻辑加入到showModel()中去。在项目中,这种状况是很平常的,因为程序设计语言,比如这个例子中用到的java,容许我们用它强大的表现能力去实现任何业务逻辑。因此,我们应该用另外一种语言去实现业务流程,而不是java。
从一个框架的角度来看,预防开发人员沉溺于将流程和逻辑放在一起是非常重要的。描述业务流程的语言可能难于实现逻辑,但与此同时,却能像编程语言一样富有表现力。目前,需要应用BPM/工作流的概念去增加框架的解决方案。对于这个问题,我建议用XML-based文档(程序定义XML)去描述流程,它可以指定需要按照什么顺序调用哪些service。从而,应用了J-SOFA之后, showModel()方法中的流程可以像下面这样表示:
列表 3. process.xml
<process>
<if test="${modelId > 0}">
<service name="modelService" Operation="create">
<return name="model" />
</service>
<service name="modelService" operation="getFeatures">
<return name="features" />
</service>
</if>
</process>
在上面的XML中,modelService的两个操作通过service标签被调用,service标签对应于service组件中所实现的方法。他们也可以被应用于条件或循环语句中,如if,choose,forEach等。然而,他们还是不如编程语言富于表现力。另外,J-SOFA并不能执行从service标签中获得的模型和特性对象的方法,除非是通过getter方法。这些限制条件要求开发人员用XML描述业务逻辑时具备更加复杂的知识才干,不管怎样,它们还是能帮助开发人员决定哪些业务逻辑应该用service类实现。有这些service方式实现的业务逻辑,我们可以开发基于SOA的应用程序,更能快速适应各种各样业务模型的变化。
典型的web应用程序框架不支持服务协调,如JSF和Struts。所以,我们必须在showModel()方法中编写下面的代码去执行处理:
列表 4. 调用流程的showModel()方法
public String showModel() {
ProcessInstance process = new ProcessInstance("process.xml");
ProcessContext context = new ProcessContext();
context.put("modelId", modelId);
process.execute(context);
BeanUtils.copyProperties(this, context.get("model"));
setFeatures((List) context.get("features"));
...
}
无论如何,假如框架拥有支持调用处理的功能,我们不需要创建action。相反,我们需要:
--创建流程定义XML
--创建用于在处理中被调用的service组件
--在JSP页面编写显示处理返回值的代码
在这部分,我解释了流程定义XML如何为action定义流程;无论如何,其中的有些定义可以在真实世界中被重用。在下一节中,我将用另外一个例子去说明如何再利用流程。
可继续的XML
创建流程的时候,我们发现有些流可以被其他的流程共享。举个例子,我创建了4个页面,如下图3所示:模型总览,模型特性,和其他两种分类索引页面。所有的页面包含相同的标题和页脚。前两个模型页面用同一个Model对象来显示模型信息,如模型名称。后两个分类页面同样那个也是用一个Category对象。最后,每个页面有自己单独的页面处理进程。
图3. 流程中的共享流
在这个案例中,每个流,如模型特性,能用subProcess标签标识,它能执行另外一个叫做“sub-process”的流程。
列表 5. modelFeatures.xml调用sub-processes
(注重:在这个和后面的清单中,为了简化代码,service标签中的子标签将被省略)
<process>
<subProcess path="page.xml" /> ---------- (1)
<subProcess path="model.xml" /> ---------- (2)
<service name="modelService" operation="getFeatures" /> ----- (3)
</process>
页面和模型流程隐藏在每个sub-process中,但是我们仍然能找到从(1) 到 (3)的连续流。所以,假如我们要修改流,比如改变流的顺序为(2), (3), (1),那么在另外的流程中,也不得不做这种改变。
为了解决这个问题,J-SOFA支持一种基于继续的解决方法。基本的思路是提供这样一种机制:容许导出一个基础过程中的标签,然后再重写它。
我们可以在process标签中用extends属性创建一个导出过程。在这个例子中,过程的层次结构能用如图4表示:
图 4. 流程等级图
Header和footer在基础过程中定义,model和Category在导出过程中定义,导出过程从基础过程中继续了header和footer标签。每一个代表一个特定的页面的过程,可以看成是model或category过程的扩展。
在page.xml中,我们产生一个header和footer,可是,我们并不知道到底在这个页面中会显示什么内容(模型或者分类?)在此刻,abstract标签会被导出流程中的其他标签重写,可以按如下方式应用:
列表 6. page.xml (基础流程)
<process>
<service id="header" name="commonService" operation="getHeader" />
<service id="footer" name="commonService" operation="getFooter" />
<abstract id="contents" />
</process>
在列表7中,model.xml可以看成是从page.xml得到的,所以,page.xml被列入process标签的extends属性中。在这个XML块中,我们只需要描述需要重写的标签。在这个案例中,model.xml中的group标签重写了abstract标签,它在page.xml中有着相同的id ”contents”.
在这个时候,我们知道必须创建Model对象,可是我们不知道究竟哪个页面会调用这个过程。因此,我们不创建一个具体的过程,而是用abstract标签表示特定页面的内容:
列表 7. model.xml (继续流程)
<process extends="page.xml">
<group id="contents">
<service name="modelService" operation="create" />
<abstract id="pageContents" />
</group>
</process>
如列表8所示,具体页面内容在从model.xml继续的modelFeatures.xml中描述。除了特性表之外所有我们需要创建的服务,都已经在基础过程中定义,所以我们只需要重写abstract标签,用service标签调用getFeature()操作。这样,开发人员可以将焦点放在跟特定页面相关的处理上。
列表 8. modelFeatures.xml (具体流程)
<process extends="model.xml">
<service id="pageContents" name="modelService" operation="getFeatures" />
</process>
当过程实例被实例化时,page.xml,model.xml和modelFeatures.xml这三个XML文档在执行之前被创建,如下面列表9所标示的那样:
列表 9. Model Features的复合流程
<process>
<service id="header" name="commonService" operation="getHeader" />
<service id="footer" name="commonService" operation="getFooter" />
<group id="contents">
<service name="modelService" operation="create" />
<service id="pageContents" name="modelService" operation="getFeatures" />
</group>
</process>
应用XML继续方法,开发人员能够重用在基础过程中表述的工作流。开发人员同样可以提供定义通用流的抽象过程,给其他开发人员描述特定页面的过程。
结论
用XML-based文档描述工作流这个概念已经在BPM和工作流产品中实施。不管怎样,到目前为止,它主要用于高层次业务描述中。在本文中,我们看到,这个概念同样适用于web应用程序中的action。
服务协调框架将直接帮助开发人员决定哪些流应该用过程XML描述,哪些逻辑应用用service实现。结果是,应用程序会基于SOA设计和开发,重用性会变得越来越好。
关于作者
Masayuki Otoshi 是一个家公司的开发Web应用的高级程序员。他还负责这家公司的应用框架的设计与开发。