Apache OFBiz源码解读之MVC模型

这篇文章我们来共同探讨一下Apache OFBiz的MVC模型的实现机制。

简介

对于Model-View-Controller的概念此处就不做过多介绍了。如果有想了解的请移步:维基百科。这里我只引用维基百科上关于该item的一张图片,来简单展示一下,这三个component之间的交互机制:

OFBiz实现MVC是通过XML来串联这三者之间的依赖关系。这里牵扯到几个关键的XML元素:<request-map
/>,<view-map />,<handler
/>。这三个XML元素的定义都位于applications/XXX/webapp/WEB-INF/controller.xml中。

从这个文件的命名来看,给人的第一感觉这似乎只是对controller的定义。但准确得说,它定义的是他们之间的映射关系,不仅包含了请求的映射关系,同时还包含了视图的映射关系,以及一系列的处理器比如视图解析处理器,事件处理器等。

我们先来分析每个元素,然后再将它们串联起来,看看它们联合起来是如何工作的。

节点解析

request-map

你可以将其理解为controller的配置,如果你了解或使用过struts的配置或springmvc的annotation,就会发现这个定义跟它们是很相似的:

<request-map uri="createCreditCardAndPostalAddress">
        <security https="true" auth="true"/>
        <event type="service" path="" invoke="createCreditCardAndAddress"/>
        <response name="success" type="request" value="finalizeOrder"/>
        <response name="error" type="view" value="billsetting"/>
    </request-map>

该元素定义了请求的映射关系。它使用名为uri的属性,表述该uri将要映射的请求。内部包含三个常用的子元素,分别是:security,event,response。

  • security:表示该uri应该对应的安全级别(是否应该是https的,是否要进行权限检查)
  • event:该请求触发的事件,这个后面在讲解handler的时候再谈
  • response:指定响应的配置

view-map

一个常见的view-map配置:

<view-map name="billsetting" type="screen" page="component://order/widget/ordermgr/OrderEntryOrderScreens.xml#BillSettings"/>

包含的属性:

  • name:当前view-map的名称,通常被<request-map>子元素<response>的value属性引用
  • type:其表示用什么技术展示视图,通常为screen,该值其实引用的是后面要讲解的handler
  • page:指定真实用于前端展示的视图布局文件

handler

在OFBiz中大致会划分两种类型的handler:event和screen。其实个人认为此处将handler理解为engine更为贴切一点,因为叫handler很容易跟业务联系到一起,而如果称之为engine则可以完全跟业务隔离开来,它只是纯技术组件而已。看看handler的定义就很容易理解了:

<!-- event handlers -->
    <handler name="java" type="request" class="org.ofbiz.webapp.event.JavaEventHandler"/>
    <handler name="soap" type="request" class="org.ofbiz.webapp.event.SOAPEventHandler"/>
    <handler name="xmlrpc" type="request" class="org.ofbiz.webapp.event.XmlRpcEventHandler"/>
    <handler name="service" type="request" class="org.ofbiz.webapp.event.ServiceEventHandler"/>
    <handler name="service-multi" type="request" class="org.ofbiz.webapp.event.ServiceMultiEventHandler"/>
    <handler name="service-stream" type="request" class="org.ofbiz.webapp.event.ServiceStreamHandler"/>
    <handler name="simple" type="request" class="org.ofbiz.webapp.event.SimpleEventHandler"/>
    <handler name="groovy" type="request" class="org.ofbiz.webapp.event.GroovyEventHandler"/>
    <handler name="rome" type="request" class="org.ofbiz.webapp.event.RomeEventHandler"/>
    <handler name="script" type="request" class="org.ofbiz.webapp.event.ScriptEventHandler"/>

    <!-- view handlers -->
    <handler name="screen" type="view" class="org.ofbiz.widget.screen.MacroScreenViewHandler"/>
    <handler name="screenxml" type="view" class="org.ofbiz.widget.screen.MacroScreenViewHandler"/>
    <handler name="screentext" type="view" class="org.ofbiz.widget.screen.MacroScreenViewHandler"/>
    <handler name="screencsv" type="view" class="org.ofbiz.widget.screen.MacroScreenViewHandler"/>
    <handler name="screenfop" type="view" class="org.ofbiz.widget.screen.ScreenFopViewHandler"/>
    <handler name="jsp" type="view" class="org.ofbiz.webapp.view.JspViewHandler"/>
    <handler name="ftl" type="view" class="org.ofbiz.webapp.ftl.FreeMarkerViewHandler"/>
    <handler name="http" type="view" class="org.ofbiz.webapp.view.HttpViewHandler"/>
    <handler name="birt" type="view" class="org.ofbiz.birt.webapp.view.BirtViewHandler"/>

handler包含的属性:

  • name:指定handler的名称,通常会被<request-map>子元素的event的type属性以及<view-map>的type属性所引用
  • type:有两种取值:request和view。request应对的是<request-map>中的event的处理器;view对应的是<view-map>的处理器
  • class:指定当前处理器实现类的完全限定名

mvc串接

下面我们以OFBiz收到一个请求为示例,展示其利用MVC模型处理请求的完整过程:

首先我们假设OFBiz web
container收到请求:createCreditCardAndPostalAddress。然后OFBiz会根据每个app下面的controller配置文件中request-map定义,查找并匹配uri为createCreditCardAndPostalAddress的映射节点(就是上文中讲解request-map使用的节点)。

<request-map uri="createCreditCardAndPostalAddress">

然后根据其子元素security的配置,对其进行安全检查:

<security https="true" auth="true"/>

因为有event元素,那么此处会触发一个“事件”(注意,不一定会有event元素)。这里是通过ofbiz的ServiceEngine来调用一个service:

<event type="service" path="" invoke="createCreditCardAndAddress"/>

调用完该service后,根据service执行的结果,匹配不同的响应视图:

<response name="success" type="request" value="finalizeOrder"/>
<response name="error" type="view" value="billsetting"/>

这里(也是通常情况下)有两种不同的响应配置:success,error。而且他们的响应方式不同,我们分开来看:

如果event触发调用createCreditCardAndAddress服务的返回结果为success,那么将触发一个请求(type为request表示再次触发一个请求,但这个请求是服务端的请求,有点像servlet里的forward动作),uri为finalizeOrder(它是另一个request-map的定义):

<request-map uri="finalizeOrder">

其语义为:完成订单创建。

如果event触发调用createCreditCardAndAddress服务的返回结果为error,那么它将会向浏览器展示一个视图(type为view),而该视图的名称为:billsetting。那接下来ofbiz就去查找名为:billsetting的view-map,查找到如下的结果:

<view-map name="billsetting" type="screen" page="component://order/widget/ordermgr/OrderEntryOrderScreens.xml#BillSettings"/>

发现它是一个widget配置(type为screen表示OFBiz中采用的一种xml的widget),而该配置的路径为:component://order/widget/ordermgr/OrderEntryOrderScreens.xml文件中名称为BillSettings的screen。然后就利用名为screen的handler,来解析该screen配置:

<handler name="screen" type="view" class="org.ofbiz.widget.screen.MacroScreenViewHandler"/>

screen

上面提到ofbiz在渲染视图的时候,采用了一个元素名为screen的配置:

<screen name="BillSettings">
    <section>
        <actions>
            <set field="stepTitleId" value="OrderOrderEntryPaymentSettings"/>
            <set field="stepLabelId" value="AccountingPayment"/>
            <script location="component://order/webapp/ordermgr/WEB-INF/actions/entry/BillSettings.groovy"/>
        </actions>
        <widgets>
            <decorator-screen name="CommonOrderCheckoutDecorator">
                <decorator-section name="body">
                  <platform-specific>
                        <html><html-template location="component://order/webapp/ordermgr/entry/billsettings.ftl"/></html>
                    </platform-specific>
                </decorator-section>
              </decorator-screen>
        </widgets>
    </section>
</screen>

这牵扯到OFBiz前端screen以及form的widget布局设计。

section

它是screen的子元素,一个screen可以包含n个section。而它可以又会由actions以及widgets元素组成。

action

在actions元素下,你可以定义若干个不同种类的action:

  • label:将值绑定到label
  • entity action:利用entity engine进行action操作
  • set action:简单的赋值以及groovy表达式语法
  • script action:调用一个groovy脚本
  • service action:调用一个服务

widgets

widgets是OFBiz布局的特点之一,它可以将一个完整的html页面拆分为一个个小块的widget,最终的页面是通过widget组合而成。

这里首先定义了一个名为:CommonOrderCheckoutDecorator的decorator-screen。所谓的decorator-screen你可以将其理解为页面的模板或者占位。比如,就一个页面而言,部分内容与空间是固定的,主要变化的是某个特定的区域。此时布局一个新页面的时候就没必要为其每个区域都重复编写html,对于公共区域直接引用已经定义好的模板即可。

比如此处的CommonOrderCheckoutDecorator装饰器screen,其定义中,它又引用了该app的一个main-decorator:

<decorator-screen name="main-decorator" location="${parameters.mainDecoratorLocation}">

这个是当前app最外层的装饰器模板。这样就形成了widget的两层嵌套关系:BillSettings引用了CommonOrderCheckoutDecorator,而CommonOrderCheckoutDecorator又引用了main-decorator,这种嵌套关系,也同时建立了页面显示的联系。

一个通常的应用,其mainDecoratorLoaction参数可以在其web.xml中的context-param配置中找到:

<context-param>
    <param-name>mainDecoratorLocation</param-name>
    <param-value>component://order/widget/ordermgr/CommonScreens.xml</param-value>
    <description>The location of the main-decorator screen to use for this webapp; referred to as a context variable in screen def XML files.</description>
</context-param>

回到正题,在BillSettings的第一个decorator-screen:CommonOrderCheckoutDecorator,还有一个decorator-section:body,它是对内容区域的模板占位。

widget内部拥有一个platform-specific子元素,它可以看做是一种switch-case语句。OFBiz widget
工具集没用对render html
UI的方式进行限制。理论上,你可以采用任何技术来render浏览器能显示内容。在这里UI被render成HTML,而且还使用了html模板,该模板的路径通过location属性指定。此处该模板使用的是freemarker(这也是OFBiz中用得最多的一种模板技术)。

数据绑定

就前端展现而言,除了需要有由html标签组成的模板,还需要绑定数据才能形成完整的页面。

OFBiz提供了两种绑定数据的方式:

  1. form-action:如果你使用form widget,绑定数据的最好方式是采用action子元素。这可以有效得帮助你增强该form的复用性。
  2. groovy script
    action:如果你使用的是freemarker模板,此时form便是由html原生标签表示,那么推荐的方式是采用groovy脚本来获取数据并绑定至模板来显示,本文所举的示例就是这种模式。

原文发布时间为:2015-02-01

本文作者:vinoYang

本文来自合作伙伴CSDN博客,了解相关信息可以关注CSDN博客。

时间: 2024-08-24 07:48:09

Apache OFBiz源码解读之MVC模型的相关文章

Apache Beam WordCount编程实战及源码解读

概述:Apache Beam WordCount编程实战及源码解读,并通过intellij IDEA和terminal两种方式调试运行WordCount程序,Apache Beam对大数据的批处理和流处理,提供一套先进的统一的编程模型,并可以运行大数据处理引擎上.完整项目Github源码 负责公司大数据处理相关架构,但是具有多样性,极大的增加了开发成本,急需统一编程处理,Apache Beam,一处编程,处处运行,故将折腾成果分享出来. 1.Apache Beam编程实战–前言,Apache B

Apache OFbiz entity engine源码解读

简介 最近一直在看Apache OFbiz entity engine的源码.为了能够更透彻得理解,也因为之前没有看人别人写过分析它的文章,所以决定自己来写一篇. 首先,我提出一个问题,如果你有兴趣可以想一下它的答案: JDBC真的给数据访问提供了足够的抽象,以至于你可以在多个支持jdbc访问的数据库之间任意切换而完全不需要担心你的数据访问代码吗? 我曾经在微博上有过关于该问题的思考: 其实这个感慨正是来自于我之前在看的一篇关于jdbc的文章,里面提到了jdbc中的一些设计模式(工厂方法),提供

SpringMVC源码解读之HandlerMapping_java

概述 对于Web开发者,MVC模型是大家再熟悉不过的了,SpringMVC中,满足条件的请求进入到负责请求分发的DispatcherServlet,DispatcherServlet根据请求url到控制器的映射(HandlerMapping中保存),HandlerMapping最终返回HandlerExecutionChain,其中包含了具体的处理对象handler(也即我们编程时写的controller)以及一系列的拦截器interceptors,此时DispatcherServlet会根据返

Spark jdbc postgresql数据库连接和写入操作源码解读

概述:Spark postgresql jdbc 数据库连接和写入操作源码解读,详细记录了SparkSQL对数据库的操作,通过java程序,在本地开发和运行.整体为,Spark建立数据库连接,读取数据,将DataFrame数据写入另一个数据库表中.附带完整项目源码(完整项目源码github). 1.首先在postgreSQL中创建一张测试表,并插入数据.(完整项目源码Github) 1.1. 在postgreSQL中的postgres用户下,创建 products CREATE TABLE pr

Ajax::prototype 源码解读_javascript技巧

AJAX之旅(1):由prototype_1.3.1进入javascript殿堂-类的初探  还是决定冠上ajax的头衔,毕竟很多人会用这个关键词搜索.虽然我认为这只是个炒作的概念,不过不得不承认ajax叫起来要方便多了.所以ajax的意思我就不详细解释了. 写这个教程的起因很简单:经过一段时间的ajax学习,有一些体会,并且越发认识到ajax技术的强大,所以决定记录下来,顺便也是对自己思路的整理.有关这个教程的后续,请关注http://www.x2design.net 前几年,javascri

Apache Spark源码走读

http://www.aliyun.com/zixun/aggregation/13383.html">Spark是发源于美国加州大学伯克利分校AMPLab的集群计算平台,它立足于内存计算,性能超过Hadoop百倍,即使使用磁盘,迭代类型的计算也会有10倍速度的提升.Spark从多迭代批量处理出发,兼收并蓄数据仓库.流处理和图计算等多种计算范式,是罕见的全能选手.Spark当下已成为Apache基金会的顶级开源项目,拥有着庞大的社区支持--活跃开发者人数已超过Hadoop MapReduc

jQuery源码解读之removeAttr()方法分析

 这篇文章主要介绍了jQuery源码解读之removeAttr()方法分析,较为详细的分析了removeAttr方法的实现技巧,非常具有实用价值,需要的朋友可以参考下     本文较为详细的分析了jQuery源码解读之removeAttr()方法.分享给大家供大家参考.具体分析如下: 扩展jQuery原型对象的方法: 代码如下: jQuery.fn.extend({ //name,传入要DOM元素要移除的属性名. removeAttr: function( name ) {   //使用jQue

jQuery源码解读之hasClass()方法分析

 这篇文章主要介绍了jQuery源码解读之hasClass()方法,以注释形式较为详细的分析了hasClass()方法的实现技巧,具有一定参考借鉴价值,需要的朋友可以参考下     本文较为详细的分析了jQuery源码解读之hasClass()方法.分享给大家供大家参考.具体分析如下:   代码如下: jQuery.fn.extend({ hasClass: function( selector ) { //将要检查的类名selector赋值给className, l为选择器选择的当前要检查的j

jQuery源码解读之addClass()方法分析

 这篇文章主要介绍了jQuery源码解读之addClass()方法,注释形式较为详细的分析了addClass()方法的实现技巧与相关注意事项,具有一定参考借鉴价值,需要的朋友可以参考下     本文较为详细的分析了jQuery源码解读之addClass()方法.分享给大家供大家参考.具体分析如下: 给jQuery原型对象扩展addClass功能,jQuery.fn就是jQuery.prototype 代码如下: jQuery.fn.extend({ /* 可以看出这是一个函数名叫addClass