关于基于JavaSript的RIA客户端数据处理

关于基于JavaSript的RIA客户端数据处理(上)

一、引子

这个blog已经荒废将近一年了,久也不写,自然有很多的理由,但更多的怕是懒吧。不说闲话了,转入正题。


关于基于JavaScript的RIA客户端数据处理这个话题,我打算分成两篇文章来写,一篇陈述我所总结出的基于B/S结构的RIA客户端数据处理的问题,另一篇则陈述针对这些问题我所提出的技术解决方案构架。


二、RIA?RIA!

关于RIA尤其是基于Ajax的RIA怕是屡见不鲜了吧?尤其是在Google推手之后,文字处理、表格处理、幻灯片放映这种看起来非常客户端的应用,都可以采用Ajax的技术来实现了。作为一个关注企业级应用开发的技术人员,一个很自然的想法就会产生,是否可以采用这种技术来改进我们基于Java EE技术开发的B/S结构的企业应用呢?


先说有没有必要,答案是肯定的。B/S被广为诟病的一个问题就是降低了最终用户的操作效率,以我的经验来说,用户虽然普遍的感到基于浏览器的界面要漂亮得多,用鼠标操作也很直观,但是却实在比以前的界面复杂而且操作困难。而且每次页面提交后的等待也实在是对工作效率的一个降低。当然,我这里也没有必要意义列举B/S在客户端的缺点,实际上这个问题是被广泛认同的。


再说可行性,可行性分为两种:技术上的可行性以及工程开发上的可行性。


技术上的可行性就无须验证了,Google Reader、Gmail、Google Docs的稳定运行都是非常好的证明。


但是它是否一定适合时间要求相对比较严格的工程开发呢?


这就需要一个非常稳定的平台来进行支持,而且由于工程开发的特殊性,最好还要有可视化的开发和调试环境才更有说服力。目前看来是没有非常完善的,但是很多的Ajax框架,如Ext、GWT、Tibco GI以及服务端框架Struts2、JSF等,都在以自己的方式实现着。关于这个方面的探讨我打算放到下一个系列《基于MDA的企业应用RIA解决方案》里面讨论,不在这里多费口舌了。


技术上是可行的,而如果又一个非常稳定和成熟的平台支持的话,在工程开发上也是可行的,那么这个平台怎样才算是稳定和成熟的呢?本系列讨论的就是其中的一部分,客户端的数据处理。


三、定义、缩略语

下面是本文中使用的技术名词的定义以及缩略语简介。

  1. RIARichInternet Application的缩写。RIA是拥有传统本地应用的功能和效果的Web应用。RIA一般把UI相关的处理交给了Web客户端,但是大量的数据(包括应用的状态、数据等)还是交给服务端处理

  2. Ajax:又写为AJAX,是"AsynchronousJavaScript and XML"的缩写。是一种使用浏览器技术进行RIA开发的技术

  3. ECMA Script:由EuropeanComputer Manufacturers Association(欧洲计算机制造商协会)维护的一个脚本语言标准。当前最通行的版本是ECMA-262 Edition 3,通常也被称为JavaScript 1.5

  4. dojo:由dojo foundation管理的一个开源JavaScript框架。提供了很好的JavaScript扩展,目前被IBMSun等大公司支持和使用

  5. JsonJavaScriptObject Notation的缩写。它是一种纯文本的数据对象传输协议,在Ajax的应用中被广泛采用

  6. CRUDCreate(创建)、Retrieve(获取)、Update(更新)和Delete(删除)这四种简单的数据操作的缩写

  7. Form:本文中的Form指的是经过Ajax扩展的简单的HTML电子表单。表单内部可以拥有很多如ComboBoxTextBox等构件。一般来说,一个表单对应着一个业务的数据对象

  8. Tree:树形显示结构化数据的构件,由于数据是高度结构化的,往往可以采用懒加载等技术来提高性能

  9. Grid:以表格形式显示和编辑数据的UI构件。一般分为表头(标题栏)、表身(数据列表)和表尾(合计、状态显示等)三个部分。其中,表头可以是复合的表头,而表身可以是复合的格式(TreeGridComboBoxCheckBox等)。一个Grid可以有一个复杂的Grid定义

  10. Enhanced Grid:下面简称为EGrid,是Grid的扩展。在Grid的功能基础之上提供了数据获取和数据持久化的能力,可以大大的减少开发应用的时间(Ext中的Grid可以认为是一个简化了的EGrid

  11. CodeList:代码表,一般用在下拉列表数据处,在系统的实现中,由于性能以及标准的要求,下拉列表一般都是采用代码保存数据,但是用户在填写表单的时候需要看到正常的文字而不是代码,这就需要系统通过CodeList进行文字和代码的转换

  12. LRULeastRecent Used的缩写,是一种简单的缓存策略,如果缓存已满,那么就释放最近最少使用的那个缓存数据

  13. REST:是REpresentationalState Transfer的缩写,是一种基于轻量级WebService协议


四、客户端数据处理的技术场景

在本部分里将会针对下面六个分类对客户端数据处理的技术场景进行概述
  • 数据绑定(Data Binding

  • 数据变更追踪(Data Change Tracking

  • 数据访问(Data Access

  • 数据缓存(Data Caching

  • 数据查询(Data Query

  • 数据处理(Data Processing


4.1 数据绑定(Data Binding

概念:

以非常简单的声明方式实现FormGrid这种数据填写、修改构件与数据之间的对应关系。


场景:

  • Form绑定:把一个Form与一个数据对象通过声明的方式绑定,使得Form的修改直接可以体现在数据对象中,而数据对象的修改也可以马上由Form体现出来

  • Grid绑定:把一个Grid和一个数据对象集合通过声明或者设置数据源的方式绑定,使得在Grid上的写操作都可以体现在数据对象集合中,而数据对象集合的操作也可以马上由Grid构件体现出来

  • Tree绑定:由于Tree的特殊性,与其说Tree与一个数据对象集合绑定,不如说Tree使用了一个可以根据查询提供数据的数据来源更合适。当然,由于Tree有可能被可视化的修改(如组织机构管理),所以这个数据来源必须支持对数据的写操作

  • 其它:如菜单的绑定等,相对于上述三个都属于非常用的绑定,而且如果上述绑定能够实现,那么其他的绑定也可以采用同样的技术实现

 4.2 数据变更追踪(Data Change Tracking

概念:

对数据的整个生命周期进行追踪。主要的目的就是可以通过记录看出数据做了哪些改变,然后根据这些改变做出相应的处理,其使用场景有Grid的变更提示、Form修改的追踪等。


场景:

  • Grid变更提示:在Grid中以明显的方式提示用户做了哪些修改

  • 数据集合变更追踪:根据变更的效果,可以有选择的发送合适的数据给服务端进行处理

  • Form修改的追踪:为用户在提交Form时提示用户哪些重要的字段进行了修改提供支持

4.3 数据访问(Data Access

概念:

服务端与客户端数据传输的格式未必相同,而且数据对象属性的类型等信息也需要一些Meta Data API来进行支持。所以,抽象出一个抽象的拥有Meta Data的数据访问层是有意义的。


场景:

  • 数据对象元数据信息访问:可以通过这些元数据信息了解数据对象的各个属性以及对应的数据类型(这一点对于Json格式的数据传输尤其重要,因为Json规范里面没有DateTime类型

  • 数据写操作的统一事件定义:通过Data Access API来访问数据,所有的写操作都会有统一的事件定义,在JavaScript 1.5中,普通的JavaScript对象是做不到的

  • 统一的数据读取方式:不管数据是XML格式的还是Json格式的,访问数据的方式都是统一的

4.4 数据缓存(Data Caching

概念:

数据缓存是指把获得的数据以一定的策略缓存起来,以备使用的技术。Cache是客户端数据处理中涉及到功能和性能的重要部分,原因请见下面的场景


场景:

  • 离线运行的CodeList:系统如果需要离线运行,那么CodeList肯定需要客户端缓存

  • 离线表单填写:如果允许用户离线填写表单,那么需要采用客户端缓存策略保存用户的数据

  • 性能考虑:向服务端发请求,服务端查询并返回属于非常耗费资源和时间的操作,所以对于服务端的数据,客户端应该有一定的缓存以及同步策略

4.5 数据查询(Data Query

概念:

数据查询的概念非常简单,根据用户的查询条件向服务端发送查询请求,得到服务端返回的数据对象集合。

但由于数据查询是系统中最常用的功能,所以,数据查询需要非常强大的功能。简单列举如下。


场景:

  • 样本查询:给出一个样本,比如{age : 20, gender : 'male'},返回所有符合样本的结果

  • 模糊查询:用户可以提供非明确的查询参数(如李??,查询所有姓李的,名字为三个字的人),返回结果供用户进一步的筛选

  • Range查询:根据Start IndexCount返回整个大数据集中的一部分,一般用在分页查询处理方面,当然也可以用在其他的需要整个数据集的一部分的地方

  • 逻辑查询:采用逻辑操作对数据进行过滤查询,比如(employee中所有满足age < 40 && age >30 && gender = ‘male’ && married条件的员工——年龄大于30且小于40的已婚男员工),实际上上面所说的查询都可以以某种方式归为逻辑查询的一种,之所以明确的提出来,是因为查询的语法有可能有所不同,并且有些构件或者服务会对某些查询有特别的需求或者加强的支持以简化开发的难度,提高开发的效率

  • 自定义查询:根据用户的需要可以对上面的查询进行有机地结合

4.6 数据处理(Data Processing

概念:

在数据查询、显示、修改和持久化的过程中,用户有可能还需要做一些监控,以及一些自定义的操作,如过滤、排序等。这就需要一套数据处理的事件以及数据处理的方法。这些统一归到数据处理用例中。列举如下。


场景:

  • 数据处理事件:数据的查询以及持久化过程中的事件

  • 数据过滤:通过数据过滤(查询前、查询后)可以满足权限控制等功能的要求

  • 排序:可以对数据进行自定义的排序(查询、查询结果

五、小结

客户端的数据处理能力是客户端最主要的基础能力,它直接影响到了应用服务器的负荷以及用户UI响应的速度和效率。所以,客户端的数据处理支持是一个RIA平台非常重要的考核点。可以上面的六类场景去考核各种RIA平台,同时,它们也为我提出我的RIA客户端数据处理解决方案提供了基础的用例。

关于基于JavaSript的RIA客户端数据处理(下)

一、引子

上一篇讨论了关于客户端数据处理的一些问题,以简单的用例场景的方式描述了出来。很明显,要想实现一个功能完整的Rich客户端的话,必须能够满足上述用例场景的需求。能否根据这些需求做出合理的设计,是一个挑战。尤其对于设计而言,不同的人有着不同的风格,而且由于背景不同,也会有不同的见解。本文中,我只是陈述出自己的一些想法和设想,更多的是希望能够抛砖引玉,通过在这个方面的讨论也能增进我的理解。呵呵。

很显然,blog的形式更适合作为思路的介绍以及探讨的平台,而不是详细设计的文档。而且很明显这一篇文章是承载不了所有的详细设计的。我争取把我在各个细化的方面的想法在后续的文章里面发出来。如果时间允许的话,整理出初始的文档和代码,建立一个小的开源项目未尝不可(因为如此,所有的JS都是采用英文来注释──其实还有一个原因是练习英文 :))。这都是后话了。

二、缩略语

  1. RIARichInternet Application的缩写。RIA是拥有传统本地应用的功能和效果的Web应用。RIA一般把UI相关的处理交给了Web客户端,但是大量的数据(包括应用的状态、数据等)还是交给服务端处理

  2. Ajax:又写为AJAX,是"AsynchronousJavaScript and XML"的缩写。是一种使用浏览器技术进行RIA开发的技术

  3. Prototype:开源的优雅的Ajax以及JavaScript基础扩展框架。由于被Rails采用而闻名,使用相当广泛
  4. jQuery:功能类似Prototype + script.aculo.us的开源Ajax框架。据我所知,Google用得比较多
  5. Ext:对YUI的一个扩展的UI构件库。经过改进后,可以使用jQuery或者Prototype作为基础。目前好像正在完善自身的基础Ext base。以优秀的构件闻名。虽然目前仍然属于商业构件库,但是License相对宽松,一般的商业应用可以免费使用
  6. dojo:由dojo foundation管理的一个开源JavaScript框架。提供了很好的JavaScript扩展,目前被IBMSun等大公司支持和使用

  7. JsonJavaScriptObject Notation的缩写。它是一种纯文本的数据对象传输协议,在Ajax的应用中被广泛采用

  8. CRUDCreate(创建)、Retrieve(获取)、Update(更新)和Delete(删除)这四种简单的数据操作的缩写

  9. Form:本文中的Form指的是经过Ajax扩展的简单的HTML电子表单。表单内部可以拥有很多如ComboBoxTextBox等构件。一般来说,一个表单对应着一个业务的数据对象

  10. Tree:树形显示结构化数据的构件,由于数据是高度结构化的,往往可以采用懒加载等技术来提高性能

  11. Grid:以表格形式显示和编辑数据的UI构件。一般分为表头(标题栏)、表身(数据列表)和表尾(合计、状态显示等)三个部分。其中,表头可以是复合的表头,而表身可以是复合的格式(TreeGridComboBoxCheckBox等)。一个Grid可以有一个复杂的Grid定义

  12. Enhanced Grid:下面简称为EGrid,是Grid的扩展。在Grid的功能基础之上提供了数据获取和数据持久化的能力,可以大大的减少开发应用的时间(Ext中的Grid可以认为是一个简化了的EGrid

  13. CodeList:代码表,一般用在下拉列表数据处,在系统的实现中,由于性能以及标准的要求,下拉列表一般都是采用代码保存数据,但是用户在填写表单的时候需要看到正常的文字而不是代码,这就需要系统通过CodeList进行文字和代码的转换

  14. LRULeastRecent Used的缩写,是一种简单的缓存策略,如果缓存已满,那么就释放最近最少使用的那个缓存数据

  15. REST:是REpresentationalState Transfer的缩写,是一种基于轻量级WebService协议

  16. JDBC:是Java DataBaseConnectivity对缩写,是基于Java的数据库连接规范。

三、基础

既然决定采用Ajax,就最好采用一个基础,在这个有很多优秀的Ajax框架可供选择的时代,谁要是还要赤手空拳的来写Ajax应用,就未免有点儿过于勇敢,而近于鲁莽了。这篇文章不是Ajax框架对比文章,我不打算在这里一一列举各个流行的Ajax框架的优缺点,我只拿下面几个进行讨论,dojo、Prototype、jQuery、Ext。

先提需求,框架应该:
  1. 以一种形式支持面向对象(毕竟开发人员多数都是Java程序员,更有可能的是,他/她只对Java熟悉)
  2. 以一种方式来支持命名空间和分包机制(开发企业应用与开发网站不同,复杂的不是技术而是业务。没有命名空间和分包支持,对于大型应用,基本不可控制。──设想一下如果Java没有package关键字会怎样)
  3. 有完善的模块封装与管理规范或者最佳实践,能够自动处理模块间的依赖则更好(模块的定义不在这里解释了,一个例子说明吧,一个Jar就是一个模块。模块化和构件化是实现、维护和管理大型应用的重要手段)
  4. 提供丰富的调优选项,使得对复杂的应用调优是可能的(这一点对于UI层尤为重要,因为它直接面对使用者)
  5. 便于调试(这一点对于开发者致关重要)

综合上面几点,我选择dojo作为基础。Sorry Prototype。

四、整体构架


整体构架如下图所示(为了便于理解UI构件与其对关系,我把需要数据处理的UI构件也加了上去,图中蓝背景色的部分就是):



一目了然,整个客户端数据处理由3个部分构成,DataSource、DataSet和DataStore。下面分别进行简单的介绍。

五、DataSet

DataSet的概念很简单,就是数据对象的集合。它的构架如下图所示(注意:DataSet只是一套标准的API,可以有不同的实现——比如XML形式的):

如图所示,DataSet由四个部分构成,Record Set(数据集合)、Change Tracker(数据变更追踪)、Meta Data(数据对象源信息)和Cursors(游标API)。

分别介绍如下:
  1. Record Set:Record Set就是Record的集合,一个Record就是一个数据对象,它由一系列的属性(Property)构成,属性是一个简单的字符串,其对应的值可以是任意类型。Record提供属性读写的方法,可以直接在Record上对属性进行读写,并且,Record会为写操作提供一个事件。Record Set内部的元素必须是Record,Record Set支持对内部Record的CRUD操作。并且会有相应的事件触发
  2. Change Tracker:ChangeTracker为DataSet提供所有写操作的追踪,可以通过配置选择Change Tracker是否记录修改,如果记录修改,采用的是针对每一个Record记录增加、删除以及针对每一个属性记录First和Last修改的策略
  3. Meta Data:提供DataSet中Record的元数据(属性名、属性对应类型、其他自定义数据——校验、Form Label信息、表头信息等)以及DataSet的元数据(在全部数据中的位置等)。
  4. Cursors:Cursor其实就是Record的迭代器,可以通过对dataset查询(一般都是非常简单的filter或者range)得到,推荐通过Cursor进行Record访问,而不是通过Index,因为通过迭代器访问Record,DataSet拥有更多的能力。虽然通过Cursor完全可以访问所有的Record及其中的数据,但是由于DataSet拥有MetaData,所以还是采用DataSet作为数据绑定的主体

DataSet是整个客户端数据处理构架的核心,所有的数据访问都应该通过DataSet的API。这样客户端的数据处理才是统一的一个整体。

解决的用例问题:
  • 数据绑定:Form绑定、Grid绑定
  • 数据变更追踪:Grid变更提示、数据集合变更追踪、Form修改的追踪
  • 数据访问:数据对象元数据信息访问、数据写操作的统一事件定义、统一的数据读取方式

六、DataSource

DataSource的功能是提供对数据进行查询以及数据的持久化(持久化到客户端或者服务器端)。与DataSet一样,不同的格式数据就会有不同的DataSource,一种DataSource代表了一种客户端与服务端的数据传输协议。但是由于DataSource的逻辑与结构过于复杂(事实上,DataSet的实现也完全依赖DataSource的实现,可以类比JDBC),不应该对每一种格式都重新实现一个DataSource,由此,DataSource不应该是一套标准的API,而是使用了Adapter模式,来满足不同的数据来源,其构架如下图所示:
看上去很简单?实际上这是最复杂的部分,关于这个部分,至少可以再写3篇文章来详细探讨,在本文中,就不过度讨论了。:)

如图所示,DataSource由Cache、Query、Persist以及Providers构成,分别介绍如下:
  1. Cache:Cache的概念在前面已经阐述了,不在这里多说了。在这里简单的介绍一下客户端Cache的策略以及技术,由于基于浏览器的数据缓存技术非常重要,拟在后续的文档《客户端的缓存策略与相关技术》中对其进行详细讨论
    • 缓存策略(这一方面客户端数据的缓存与服务端的数据缓存考虑的问题应该是类似的,唯一的区别是,客户端的数据缓存不必考虑集群支持。:))
      • LRU:基础的Cache算法,如果缓存已满时,根据最近很少使用算法来确定哪些对象需要被清除
      • Priority:按照优先级高低来清理缓存空间的策略,当缓存已满时,按照优先级高低来确定哪些对象需要被清除,可以与LRU算法共存
      • Refreshable:有时效性的数据,或者在运行时有可能会被修改需要同步或者刷新的数据。可以设置数据过期时间,到时间则数据处于stale状态,再度访问该数据时,如果不能重新获得该数据,则报错
      • Expireable:临时性数据,可以设置失效时间,到时间则数据失效,在缓存需要清理时,缓存会清理掉这些失效的对象
    • 缓存持久化(属于高级的缓存策略,依赖客户端基于JavaScript的数据存储技术)
      • Save(持久化):把缓存当中的数据持久化到本地或者服务端,其用处如下
        • 通过把数据持久化可以增加Cache的容量
        • 数据本地缓存提供了离线表单填写的能力
      • Retrieve、Delete:对缓存中数据的基本操作
      • Sync:离线缓存的本地数据,可以与远程服务端进行同步,从而实现离线表单提交以及实现离线CodeList等功能
    • 缓存技术
      • Client
        • 内存引用
        • Cookie
        • Google Gear
      • Server(服务端协助客户端做一些缓存,这在传统的B/S下是匪夷所思的设计,在RIA的情况下却未尝不是一种手段)
        • WebService或者REST
        • Post + Get
  2. Query:其实Query只是一个方法,由于有可能会向服务端发XmlHttpRequest,所以这个方法需要回调方法。其参数应该是一个Query对象,一个配置对象,有一些事件的关键字(onBegin、onError、onSuccess等)。Query对象以及配置对象的格式在本文中就不详细说明了,留给以后慢慢说(因为这个部分是我最喜欢的部分)。很明显,如果查询成功,查询的结果应该是DataSet,一般来说,DataSource是DataSet来源。不同的DataSource,查询的语言未必相同。不一定所有的查询对象都支持SQL语法(比如可以支持HQL或者XQuery啊),而且我最推荐采用的是服务端提供一个服务抽象层,接受Json格式的查询请求,并返回Json格式的数据
  3. Persist:数据持久化。只是Save和Update两个方法。接受DataSet和配置对象作为参数(这两个方法也应该是异步的)。如果DataSet里面没有足够的元数据信息,需要在配置对象里面提供这些信息。这个部分不比Query部分复杂
  4. Providers:数据的来源,提供了Query和Persist API的实现,是DataSource的底层实现,它使DataSource的实现可以跨不同协议而使用。给DataSource提供不同的Provider,DataSource就可以支持不同的数据传输协议。

DataSource是数据查询和持久化的核心,所有的客户端的数据查询和持久化基本都应该采用DataSource,从而获得DataSource提供的强大而灵活的特性。

解决的用例问题:
  • 数据缓存:离线运行的CodeList、离线表单填写、性能考虑
  • 数据查询:样本查询、模糊查询、Range查询、逻辑查询、自定义查询
  • 数据处理:数据处理事件、过滤、排序

七、DataStore

DataStore基于DataSource和DataSet的实现,实现了dojo的data api。这样既实现了对需要dojo api的dojo构件的支持,又满足了类似Tree、EGrid这种既需要绑定又需要数据来源的高级构件的要求。总体来说,DataStore只是薄薄的一层接口,实际的实现完全依赖于DataSource。

解决的用例问题:
  • 数据绑定、查询、处理:Tree绑定、ComboBox数据源、EGrid绑定

八、待讨论的问题

客户端的数据处理事实上要比我想象得还要复杂,我还有一些设计决策没有确定,列举下来以供讨论吧。

8.1 客户端的事务处理?

由于很多数据处理都放在了客户端。客户端的数据处理操作相应增多而且复杂,是否应该在DataSource中添加写事务的处理?以便对数据操作进行更细粒度的管理,而不是仅仅停留在Query、Save和Track阶段?

8.2 权限数据的来源

如果可以连接到服务端,用户在向服务端登录后,服务端会返回用户的权限信息列表。客户端接收后,可以相应的提供权限控制。但是,如果客户端离线运行,用户无法向服务的登录,权限信息列表无从获得,怎么提供权限控制?

探讨方案1:离线客户端无须登录,由于没有权限控制列表,默认拥有系统最低权限(固定的配置在客户端),由系统开发人员决定用户在离线时可以进行何种操作。用户在线提交数据的时候,服务端也要根据相应的权限进行验证以防止越权的数据操作

探讨方案2:离线客户端仍须登录,权限控制列表使用用户在线登录时缓存的数据。其余与在线方式相同。如果用户没有在线登录过,那么离线应用无法使用。

8.3 跨域数据的来源

由于浏览器的安全性策略,JavaScript无法向除本身域之外的其他服务器发送请求。也就是说,对于JavaScript而言,所有的数据必须来源于同一个域的服务器。对客户端进行集成非常不利。

探讨方案1:服务端提供一个服务接入层,接受如Spring Bean、EJB、JMS、WebService等形式的服务,并且把所有的服务都转化成为一种固定格式的协议与客户端交互。所有的服务集成与协议转化都交给服务端,对客户端完全透明。

探讨方案2:服务端提供一个简单的Proxy,通过一定的协议把请求和回复转发给客户端,处理交给客户端来做

九、小结

虽然到了这里,但是对于基于JavaScript的RIA客户端数据处理的讨论却才刚刚开始,还需要很多后续的展开讨论。但是,上午已经过去了,需要去吃午饭了。:)

posted @ 2007-12-15 13:15 guitarpoet 


你可能感兴趣的:(关于基于JavaSript的RIA客户端数据处理)