echo2 ajax框架源码分析

Echo2框架源码分析
Echo2是一个基于Ajax技术的组件式web框架 ,其代码实现中的主要概念有:
1. WebContainerServlet基类。
   a. Echo2的每个应用界面都通过对应的容器servlet来进行访问。     
   b. 每个Echo2应用都需要继承WebContainerServlet基类,并实现newApplicationInstance()函数,在这个函数中创建Echo2应用程序实例。
   c. 在目前Echo2的实现当中,webcontainer功能实现的一部分被剥离到webrender包中,例如WebContainerServlet从WebRenderServlet继承。

2. Service接口。
    interface Service{
        String getId();
        int getVersion();
        void service(nextapp.echo2.webrender.Connection connection);
    }

   a. WebContainerServlet对外提供的服务被分解为一个个通用的Service对象。由service对象实现web调用模型与Echo2组件模型之间的连接,并将Echo2组件模型转换为html界面描述。Echo2框架中的其他对象都与网络无关。
   b. 每个可调用的service对象都需要在WebContainerServlet中的静态(全局)的ServiceRegistry中注册或者在基于每个应用界面的ContainerInstance中注册。
   c. service采用singleton模式,无状态。
   d. 缺省提供的服务有:

       Echo2核心服务,同步客户端与服务器端的状态。这个服务对象会调用各个Component对应的Peer对象来完成操作。     
       serviceId=Echo.Synchronize ==> ContainerSynchronizeService

       serviceId=Echo.Expired ==> SessionExpiredService
      
       返回Echo2客户端的引擎脚本ClientEngine.js。在Echo2框架中每一个js文件也对应于一个service对象,便于前台ajax装载。
       serviceId=Echo.ClientEngine ==> JavaScriptService

       Echo2的初始化界面
       serviceId=Echo.Default ==> WindowHtmlService

       创建一个新的Echo2应用界面
       serviceId=Echo.NewInstance ==> NewInstanceService

       serviceId=Echo.AsyncMonitor ==> AsyncMonitorService

       返回Echo2自带调试器的页面Debug.html
       serviceId=Echo.Debug ==> StaticTextService


处理过程:
Echo2的基本web访问格式为 container_servlet_url?serviceId=[registered service id]&other_parameters
在container servlet中,处理过程非常简单。
        Service service = WebContainerServlet.getServiceRegistry().get(serviceId);
        if (service == null && userInstance != null) {
            service = userInstance.getServiceRegistry().get(id);
        }
        service.service(new Connection(this,request,response));
  
3. ContainerInstance类。
   在NewInstanceService服务中被创建,用来存储所有与某个Echo2应用相关的数据。
   ContainerInstance的主要成员变量有:
     String applicationUri;  Echo2应用程序对应的servlet的url,例如/myapp
     ApplicationInstance applicationInstance;  Echo2应用程序实例对象
     HttpSession session; ContainerInstance被保存在session中。
    

4. ApplicationInstance基类。
   每个Echo应用界面都继承这个基类,并在init()函数中构造缺省主窗口。
   ApplicationInstance的主要成员变量有
     Window defaultWindow;  唯一的主窗口
     WeakReference focusedComponent;  当前焦点所在组件
     UpdateManager updateManager; 记录一次调用过程中对Echo2界面结构所造成的更新。
                                  这些更新将会以xml格式传输到浏览器端进行界面同步。
     List modalComponents;   进入modal状态的组件
  

ApplicationInstance对象在NewInstanceService服务中被创建。它与ContainerInstance的关系是:与网络无关的部分被抽象出来成为applicationInstance。

5. Component基类
   每一个界面组件在服务器端都对应于一个Component对象。
   Component的主要成员变量有:
   String id;
   String renderId; // 加上前缀c_之后对应于生成的html中元素的id, 它与Component.id没有直接关系。
   List children;
   Component parent;
   ApplicationInstance applicationInstance;  // 组件在此applicationInstance中注册。
   MutableStyle localStyle; // 保存该Component的各种属性
   EventListenerList listenerList; // 注册的事件响应函数
 
   我们在创建Component对象之后对其属性进行任何修改都会调用 applicationInstance.notifyComponentPropertyChange()函数,从而这些改变将被收集到applicationInstance.updateManager对象中。在一次调用完毕之后,ContainerSynchronizeService将会根据这些改变将对应的界面变化返回给浏览器端的ClientEngine。

6. ComponentSynchronizePeer接口。
   Peer具体实现浏览器端与服务器端Component的状态同步。它基本的功能是根据Component的状态信息生成html。(Echo2中并不直接输出html文本,而是不断向一个DOM文档中追加节点,最后再序列化为文本)。
   Peer可以选择实现ActionProcessor接口,以响应用户输入事件,例如按下按钮。
   Peer对象采用singleton模式,需要在全局的SynchronizePeerFactory中注册。ContainerSynchronizeService 运行时将会根据Component的Class映射到对应的Peer, 如果不存在,则递归的查找父类对应的Peer。

7. ClientEngine.js
   Echo2前台通过ClientEngine这个Ajax引擎与服务器端交互。

一个具体的访问过程如下:
前台访问url : /myapp
webContainerServlet.doGet(request,response)
    webContainerServlet.process(request,response)
        NewInstanceService.service(connection)
            applicationInstance := webContainerServlet.newApplicationInstance()
            session.setAttribute("Echo2UserInstance:/myapp", applicationInstance)
            applicationInstance.doInit
                window := applicationInstance.init()
                    applicationInstance.setStyleSheet();
                applicationInstance.doValidation()
           
        WindowHtmlService.serivce(connection)
            render applicationInstance.defaultWindow


至此生成 前台Echo2启动页面
<body onload="EchoClientEngine.init('/myapp');">
<form style="padding:0px;margin:0px;" action="#" id="c_0" onsubmit="return false;" />
</body>

接着前台js中
EchoClientEngine.init(baseServerUri)
    将客户端程序版本等信息打包为EchoClientMessage之后通过xmlhttp以post方式发送到/myapp?serviceId=Echo.Synchronize
<messagepart processor="EchoClientAnalyzer"><property type="text" name="navigatorAppName"

value="Netscape"/>...</messagepart>

服务器端处理:
ContainerSynchronizeService.service
    this.renderInit(clientMessage,serverMessage);
        this.processClientMessage(clientMessage);
        ContentPane content = applicationInstance.getDefaultWindow().getContent());
        componentUpdate = new ServerComponentUpdate(content);
        syncPeer = SynchronizePeerFactory.getPeerForComponent(content);
        syncPeer.renderAdd(serverMessage,componentUpdate, targetId,content);
    serverMessage.render(response.getWriter());

客户端接收到消息内容大致为
<messagepart processor="EchoDomUpdate"><domadd parentid="c_0">...</domadd>
</messagepart>

用户点击前台按钮之后,ClientEngine向服务器发送消息/myapp?serviceId=Echo.Synchronize
<clientmessage><messagepart processor="EchoAction">
<action componentid="c_8" name="click"/>
</messagepart></clientmessage>

服务器端处理:
ContainerSynchronizeService.service
    this.renderUpdate(clientMessage, serverMessage);
       this.processClientMessage(clientMessage);
           actionProcessor.process(containerInstance, clientMessage.actionElement);
               syncPeer = SynchronizePeerFactory.getPeerForComponent(actionElement.componentId);
               ((ActionProcessor)syncPeer).process(containerInstance,component,actionElement);
       applicationInstance.updateManager.processClientUpdates();
           actionComponent.processInput();
               getButtonModel().fireActionPerformed();
                  AbstractButton.fireActionPerformed();  触发在Component中注册的事件监W人S听Xw B器
      this.processServerUpdates();  根据updateManager收集到的更新信息生成更新消息。
          for each update
             updatedCompenent.peer.renderUpdate();             
    serverMessage.render(response.getWriter());279

  • 上一篇文章:
  • 下一篇文章:
  • Copyright © 2007-2012 www.chuibin.com 六维论文网 版权所有