Dorado 5 : 2.1.Dorado5原理图 (T32)

下面部分内容摘自《Dorado5产品白皮书》:

图 2.1:Dorado 5原理图
本图描述了Dorado5展现中间件的运行机制。其中全图可分为Server端和Client端两个区域。分别展示了Dorado5在Server端和Client端的实现机制。
对于图中出现的几个新名词的解释如下:

  • ViewModel:视图模型。一种用于封装界面逻辑和操作逻辑的对象。即视图中包含哪些数据、这些数据以什么方式展现、视图中包含哪些控件、这些控件会激发什么操作等等。 ViewModel一般不用于定义各种控件最终在显示的布局,控件布局应通过其它方式进行定义,例如JSP或者Html。
  • Dataset: 数据集。 数据集是Dorado5架构的核心,用来管理一组数据。结构类似于关系型数据库中的表或视图。 Dataset在运行时会有Server端和Client端两种实例。 Dataset不可以直接显示,一般须通过数据控件来展示其中的数据。
  • Control: 各种可视化的控件。 包含数据控件和非数据控件。 其中非数据控件一般与数据操作没有直接关系(例如:按钮,菜单等)。 而数据控件是指可以直接用于显示或编辑数据的控件(例如:数据表格,编辑框,树状列表等)。 数据控件可以直接与Dataset进行绑定并自动对Dataset中的数据进行展现。
  • BRich客户端: 即Browser Rich Client。是指运行与纯浏览器环境中的具有富客户端表现能力的客户端。


图中的数字标示处的详细介绍如下:

  1. 首先Dorado5将根据用户的配置和定义创建ViewModel对象, ViewModel中包含了各种Control和Dataset并且将根据配置自动的建立其Control和Dataset之间的绑定关系。 ViewModel可通过两种途径将自己输出到客户端, 详见3和4。
  2. 此处的外部数据既可以是直接来自于数据库中的数据,例如ResultSet; 也可以是来自于业务逻辑层的数据,例如DTO(Data Transfer Object)或VO(Value Object)。 这些数据将根据ViewModel的配置被填充到一个或多个Dataset当中。
  3. 由于ViewModel本身并不负责界面的布局,因此须利用JSP等技术等对各种Control进行排列和布局。在JSP中我们可以使用Taglib对布局方式进行定义。
  4. 利用JSP,ViewModel的界面模板将以HTML+XML的形式通过HttpResponse被输出到浏览器当中。 注意:此过程输出的内容只包含各种控件的定义和界面布局,并不包含真正的业务数据。 业务数据将通过步骤5、6输出至Client端。 这种实现方式符合AJAX的4个基本原则中的前两条。
  5. ViewModel中的数据部分将直接以Dataset的形式交付给Dorado Servlet。 Dorado Servlet是一个专门用于实现Dorado的Server端和BRich客户端进行数据交换的服务。它既可以实现由Server端向Client端输出数据,也可以接受从BRich客户端向Server端提交的数据,还可以独立地响应Client端发出的数据请求,以实现BRich客户端中的局部数据刷新等功能。
  6. Dorado Servlet将ViewModel交付的数据转化为XML并输出到BRich客户端中。 同时,如果接受到了BRich客户端以XML提交的数据时, Dorado Servlet也会将其解析为Dataset对象并交换给ViewModel。刷新页面时是通过HttpResponse方式把数据已XML方式输出到页面并给BRich客户端解析,而Dataset数据刷新和页面数据提交是通过Ajax把Server端数据转化为XML到BRich客户端解析或者通过Ajax把BRich客户端数据转化为XML到Server端。
  7. 当4和6的输出转递到浏览器中后,首先将由Dorado提供的BRich Engine对这些信息进行处理。 其中步骤6输出的XML数据将被BRich Engine还原为一个或多个Dataset。如果界面的操作需要对Dataset中的数据进行动态局部刷新(指不刷新整个页面,而只是对其中的数据进行刷新)或提交处理,那么这些数据也将由BRich Engine进行封装以XML的形式通过Ajax发送给Dorado Servlet。
  8. 步骤4输出的HTML/XML界面模板将被解析并还原为各种真正可见的Control。有一些静态的Control通过HttpResponse的方式输出Html就可以显示出来,有些动态显示数据的Control就需要通过DHtml的方式结合Dataset和Control的属性和数据来动态构建显示的Control。例如: 数据表格,编辑框,树状列表,按钮,菜单等。同时根据这些Control在ViewModel中的定义,还原他们与Dataset之间的绑定关系,以便与最终将数据呈现给用户。


我们通过一个例子来说明上面的原理:
有一个HelloWorld页面通过下面4个代码文件生成的:

图 2.2:HelloWorld 4个代码文件
下面是一个Dorado5 HelloWorld页面:

图 2.3:HelloWorld页面
上面的页面是通过刷新页面得到的。

  1. 在chapter2/sample2_1.jsp页面处理<d:View>的时候,Dorado5会根据clazz定义的class或者config定义的view.xml里clazz属性来创建ViewModel对象(如没有定义,默认为Setting.xml定义的view.defaultViewModel),sample2_1例子当中创建了com.bstek.dorado5.docent.chapter2.Sample2_1ViewModel对象。然后Sample2_1ViewModel会根据/com/bstek/dorado5/docent/chapter2/sample2_1.view.xml的配置信息来创建所有的Dataset和Control对象,针对每个对象在view.xml里的配置内容都创建对应的对象存放在起来,也就是说view.xml里所有的内容都会存放在ViewModel对象里,并把它们之间的关联关系绑定好。所以这里能说明view.xml所有的配置内容都可以通过ViewModel对象来创建。

     <?xml version="1.0" encoding="UTF-8"?> 
     <view clazz="com.bstek.dorado5.docent.chapter2.Sample2_1ViewModel"> 
     <Datasets> 
     <Dataset type="Form" id="datasetForm"> 
     <MasterLink/> 
     <Fields> 
     <Field name="EMPLOYEE_ID" label="员工编号" dataType=""> 
     <Properties/> 
     </Field> 
     <Field name="DEPT_ID" label="部门" dataType=""> 
     <Properties/> 
     </Field> 
     </Fields> 
     <Parameters/> 
     <Properties/> 
     </Dataset> 
     <Dataset type="Custom" listener="com.bstek.dorado5.docent.chapter2.CustomDatasetListener" id="datasetCustom" maxPropertyLevel="1"> 
     <MasterLink/> 
     <Fields> 
     <Field name="EMPLOYEE_ID" label="员工编号" dataType=""> 
     <Properties/> 
     </Field> 
     <Field name="DEPT_ID" label="部门" dataType=""> 
     <Properties/> 
     </Field> 
     <Field name="EMPLOYEE_NAME" label="员工姓名" dataType=""> 
     <Properties/> 
     </Field> 
     <Field name="SEX" label="性别" dataType="boolean"> 
     <Properties/> 
     </Field> 
     <Field name="BIRTHDAY" label="出生日期" dataType="date"> 
     <Properties/> 
     </Field> 
     </Fields> 
     <Parameters> 
     <Parameter dataType="" name="EMPLOYEE_ID"/> 
     <Parameter dataType="" name="DEPT_ID"/> 
     </Parameters> 
     <Properties/> 
     </Dataset> 
     </Datasets> 
     <Controls> 
     <Control type="AutoForm" dataset="datasetForm" id="formForm"> 
     <FormGroup title="查询条件"> 
     <Element name="EMPLOYEE_ID" type="TextEditor" field="EMPLOYEE_ID"> 
     <FieldLabel/> 
     <TextEditor/> 
     </Element> 
     <Element name="DEPT_ID" type="TextEditor" field="DEPT_ID"> 
     <FieldLabel/> 
     <TextEditor/> 
     </Element> 
     <Element name="element1" controlAlign="center" showLabel="false" colSpan="2" type="Custom" controlId="buttonQuery"> 
     <FieldLabel/> 
     </Element> 
     </FormGroup> 
     </Control> 
     <Control type="DataTable" dataset="datasetCustom" id="tableCustom" width="100%"> 
     <Column field="EMPLOYEE_ID" name="EMPLOYEE_ID"/> 
     <Column field="DEPT_ID" name="DEPT_ID"/> 
     <Column field="EMPLOYEE_NAME" name="EMPLOYEE_NAME"/> 
     <Column field="SEX" name="SEX"/> 
     <Column field="BIRTHDAY" name="BIRTHDAY"/> 
     </Control> 
     <Control id="commandForm" queryDataset="datasetCustom" type="QueryCommand" conditionDataset="datasetForm"> 
     <Parameters/> 
     </Control> 
     <Control id="buttonQuery" width="80" command="commandForm" type="Button" value="查询"/> 
     <Control method="save" id="commandSave" type="UpdateCommand"> 
     <DatasetInfos> 
     <DatasetInfo dataset="datasetCustom"/> 
     </DatasetInfos> 
     <Parameters/> 
     </Control> 
     <Control id="buttonSave" width="80" command="commandSave" type="Button" value="提交"/> 
     <Control type="DataPilot" dataset="datasetCustom" id="datapilotCustom"/> 
     </Controls> 
     <Properties/> 
     </view> 

    代码 2.1:view.xml

  2. 页面中的表格数据是通过datasetCustom来得到的,我们只要对datasetCustom新增一些Record就可以,Records的来源可以是ResultSet或者来自于业务逻辑层的数据(For List)。

    package com.bstek.dorado5.docent.chapter2;
    import com.bstek.dorado.data.AbstractDatasetListener;
    import com.bstek.dorado.data.Dataset;
    import com.bstek.dorado.data.ParameterSet;
    import com.bstek.dorado.data.Record;
    import com.bstek.dorado.utils.StringHelper;
    public class CustomDatasetListener extends AbstractDatasetListener {
        public boolean beforeLoadData(Dataset dataset) throws Exception {
            ParameterSet parameters = dataset.parameters();
            String EMPLOYEE_ID = parameters.getString("EMPLOYEE_ID");
            String DEPT_ID = parameters.getString("DEPT_ID");
            // 模拟查询数据
            if (StringHelper.isNotEmpty(EMPLOYEE_ID)
                    || StringHelper.isNotEmpty(DEPT_ID)) {
                Record record = dataset.insertRecord();
                record.setString("EMPLOYEE_ID", EMPLOYEE_ID);
                record.setString("DEPT_ID", DEPT_ID);
            } else {
                Record record = dataset.insertRecord();
                record.setString("EMPLOYEE_ID", "Default");
                record.setString("DEPT_ID", "Default");
                record = dataset.insertRecord();
                record.setString("EMPLOYEE_ID", "Default2");
                record.setString("DEPT_ID", "Default2");
            }
            return false;
        }
    }

    代码 2.2:Dataset数据来源

  3. sample2_1.jsp页面的代码可以看出每个可显示控件都会在页面有自己的标签,然后通过Dorado5封装的布局标签或者Html来进行页面布局。

     <%@ page contentType="text/html; charset=UTF-8"%> 
     <%@ taglib uri="http://www.bstek.com/dorado" prefix="d"%> 
     <html> 
     <head> 
     <title></title> 
     </head> 
     <body scroll="no"> 
     <d:View config="com.bstek.dorado5.docent.chapter2.sample2_1"> 
     <table border="0"> 
     <tr valign="top"> 
     <td><d:AutoForm id="formForm" /></td> 
     </tr> 
     <tr valign="top"> 
     <td> 
     <d:Layout type="hflow"> 
     <d:Pane> 
     <d:Button id="buttonSave" /> 
     </d:Pane> 
     <d:Pane align="right"> 
     <d:DataPilot id="datapilotCustom" /> 
     </d:Pane> 
     </d:Layout> 
     </td> 
     </tr> 
     <tr valign="top"> 
     <td><d:DataTable id="tableCustom" /></td> 
     </tr> 
     </table> 
     </d:View> 
     </body> 
     </html>

    代码 2.3:JSP页面代码

  4. 对应于每个Control标签库都会输出一些Html页面布局和定义元素(通过页面生成的源代码可以看到),也会在页面的最后输出该Control的javascript定义,通过这些javascript的属性定义和DHtml来动态生成Control显示。

     <div id="tableCustom" style="width:100%;height:200;"></div> 
     
     <script language="javascript"> 
     var _t=$$c("DataTable", null, "tableCustom");t.setDataset("datasetCustom");t.setHeaderHeight(20);t.setRowHeight(19);t.setFooterHeight(20);var __t1=t.addColumn("EMPLOYEE_ID");t1.setField("EMPLOYEE_ID");var __t1=t.addColumn("DEPT_ID");t1.setField("DEPT_ID");var __t1=t.addColumn("EMPLOYEE_NAME");t1.setField("EMPLOYEE_NAME");var __t1=t.addColumn("SEX");t1.setField("SEX");var __t1=t.addColumn("BIRTHDAY");_t1.setField("BIRTHDAY"); 
     </script>

    代码 2.4:生成的Html和javascript页面源代码

  5. 页面刷新时:服务端datasetCustom的一些跟页面相关联的属性和数据内容会组装成XML和javascript输出到页面。

     <div style="display: none"><xml id="__datasetCustom" > 
     <rs possibleCount="0" pageCount="1" loadedPages="1"> 
     <r id="1" pageIndex="1" isCurrent="true" state="none" > 
     <new>Default,Default,,,</new> 
     </r> 
     <r id="2" pageIndex="1" state="none" > 
     <new>Default2,Default2,,,</new> 
     </r> 
     </rs> 
     </xml></div> 
     <script language="javascript"> 
     var datasetCustom=$$c("Dataset", null, "datasetCustom", "Custom");var _t=datasetCustom;t.setPageSize(100);var __f=t.addField("EMPLOYEE_ID",0);f.setLabel("\u5458\u5DE5\u7F16\u53F7");var __f=t.addField("DEPT_ID",0);f.setLabel("\u90E8\u95E8");var __f=t.addField("EMPLOYEE_NAME",0);f.setLabel("\u5458\u5DE5\u59D3\u540D");var __f=t.addField("SEX",9);f.setLabel("\u6027\u522B");var __f=t.addField("BIRTHDAY",10);f.setLabel("\u51FA\u751F\u65E5\u671F");var __t=t.parameters();t.addParameter("EMPLOYEE_ID",0).setValue("");_t.addParameter("DEPT_ID",0).setValue(""); 
     </script> 

    代码 2.5:Dataset生成的页面源代码
    数据刷新和数据提交:客户端BRich Engine会把客户端Dataset对象的一些属性或者数据组装成XML通过Ajax提交到服务端,在服务端根据ViewModel来创建服务端Dataset,然后根据服务端Dataset取到数据组装成类似"代码 2.5"中的XML返回到客户端,再通过BRich Engine解析给客户端Dataset。

  6. 页面通过javascript来解析服务端Dataset组装成XML内容来生成记录或者设置一些属性。
  7. 通过Dataset来与后台进行数据交互。当datasetCustom需要数据查询或者数据提交的时候,通过客户端的BRich Engine来驱动Ajax来进行数据交互。
  8. 根据4和6的内容分别通过javascript解析来生成各种可视Control,同时根据Control和Dataset的绑定关系进行绑定,并根据Dataset的记录来填充这些Control。由于这些绑定关系形成了客户端的MVC。tableCustom根据javascript设置的属性绑定了datasetCustom,并根据datasetCustom的属性和记录来填充了Table的行显示;tableCustom点击不同的记录,对应的datasetCustom的当前记录也相应改变,反过来datasetCustom的当前记录在发生改变的时候,tableCustom的当前行数据也在改变。