zk中有两个核心Servlet,org.zkoss.zk.ui.http.DHtmlLayoutServlet和org.zkoss.zk.au.http.DHtmlUpdateServlet
zk的术语:DHtmlLayoutServlet称之为加载器,DHtmlUpdateServlet称之为异步更新引擎(The asynchronous update engine,又简称为AU engine)
在讨论这两个核心Servlet之前,先看一下zk请求的执行流,以帮助理解
当用户在浏览器中键入一个URL或点击一个超链接时,一个请求便被送到了Web服务器,如果URI符合ZK的配置,ZK 加载器则援引担任这一要求 。
ZK 加载器(ZK loader)加载指定的页面然后解释它,以据此创建和适的组件。
当解释完整个页面后,ZK 加载器(ZK loader)将结果送到一个HTML页面。然后这个HTML页面被送回浏览器和ZK客户端引擎(ZK Client Engine)一起。
ZK客户端引擎(ZK Client Engine)坐落在浏览器,以监视由客户的活动触发的事件,例如挪动鼠标,或改变某个值。一旦监测到,它就通知ZK AU引擎通过发送一个ZK请求。
当从客户端引擎接到ZK请求后,如果有需要的话AU引擎就更新相应组件的内容。然后,AU引擎通过调用相关的事件处理程序(如果有的话)来通知应用程序。
如果应用程序选择改变组件的内容,添加或移动组件,AU引擎通过ZK响应(ZK responses)将更新后组件的新内容送至客户端引擎。
这些ZK响应实际上是一些命令,这些命令指示客户端引擎如何更新DOM树的内容。
仔细看完后,我们逐字分析,其实也不难理解其中的道理,但是官方doc有些东西是比较粗略的,
对于一个zk进阶类型的开发者来说,一些概念还是不够清晰,那么现在我们逐句分析
分析之前,需要说明的是:
在web.xml人为修改 zkLoader的serlvet-mapping让其处理au request,或者让auEngine的servlet-mapping处理符合zkLoader mapping的请求,或者非浏览器客户端,不再此讨论范围
1,我们看当用户在浏览器中键入一个URL或点击一个超链接时,这句话概括的不够全面和清晰,这里可以这么说:导致浏览器文档完全刷新的请求(不都是人,可能是定时器行为),均由ZkLoader处理,那么什么操作可以导致浏览器文档完全刷新呢?
2,ZK 加载器(ZK loader)加载指定的页面并解释它,初始化页面,创建组件,事件处理(见加载页面的生命周期)
3,解释完页面后,ZK 加载器(ZK loader)将生成的html发送到客户端浏览器,html中包括js 客户端引擎
4,ZK客户端引擎(ZK Client Engine)坐落在浏览器,以监视由客户的活动触发的事件,例如挪动鼠标,或改变某个值。一旦监测到,它就发送一个ZK请求通知ZK AU引擎。
这里需要解释的是:ZK客户端引擎 其实就是js编写的脚本代码。监视客户端触发的事件这里我们主要讲一下,zk中的事件并不是所有的事件,有些是需要发送到服务器端,有些却不要。凡是通过Component.addEventListener或onXXX给组建注册的事件都会发送到服务器端,其他的事件都不会发送到服务器端。
一旦监测到,它就发送一个ZK请求通知ZK AU引擎。这句话里的通知其实是一些json格式的字符串命令
命令类似如下
dtid=zd_8c&cmd_0=onSelect&uuid_0=z_d__9
&data_0={"items":["z_d__b"],"reference":"z_d__b","clearFirst":true,"pageX":89,"pageY":141,"which":1,"x":83,"y":12}
其中
dt是desktop的缩写 ,完整为desktopid=zd_8c,zd_是固定格式
cmd_0是命令,当前命令是选择命令,
uuid_0是组建的id,在浏览器中可以看到的id,
剩余的是一些当前onSelect事件鼠标的位置
另外这句话隐含了一个重要的内容,实际上zk也是这么处理的:客户端引擎与服务器端AU引擎通信,而导致浏览器文档完全刷新的url请求与ZkLoader通信,需要注意的是这里没有用绝对的口吻说客户端引擎与服务器端通信全部是由事件触发的,只能说大部分,其中特殊的是,当ZkLoader响应给客户页面后,像图片,js动态创建的script src=“”,js创建的图片等等资源,浏览器会发生请求获得这些资源,这样的请求全部由AU Engine处理,ZkLoader仅负责导致浏览器文档完全刷新请求的响应,另外一些AU request来自上传组件和动态多媒体请求(如Dynamic Image)等
下面是zk AU引擎的源代码,从第8行到71行,第8行if (withpi && pi.startsWith(ClassWebResource.PATH_PREFIX)) 判断是否是资源请求
而43行if(withpi)是判断一些特殊服务请求(上传组建和动态多媒体请求(如Dynamic Image))
73行//AU以下代码是为客户端事件服务的
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { final String pi = Https.getThisPathInfo(request); // if (log.finerable()) log.finer("Path info: "+pi); final boolean withpi = pi != null && pi.length() != 0; if (withpi && pi.startsWith(ClassWebResource.PATH_PREFIX)) { //use HttpSession to avoid loading SerializableSession in GAE //and don't retrieve session if possible final ClassWebResource cwr = getClassWebResource(); final HttpSession hsess = shallSession(cwr, pi) ? request.getSession(false): null; Object oldsess = null; if (hsess == null) { oldsess = SessionsCtrl.getRawCurrent(); SessionsCtrl.setCurrent(new SessionResolverImpl(_ctx, request)); //it might be created later } WebApp wapp; Session sess; final Object old = hsess != null? (wapp = WebManager.getWebAppIfAny(_ctx)) != null && (sess = SessionsCtrl.getSession(wapp, hsess)) != null ? I18Ns.setup(sess, request, response, "UTF-8"): I18Ns.setup(hsess, request, response, "UTF-8"): Charsets.setup(null, request, response, "UTF-8"); try { cwr.service(request, response, pi.substring(ClassWebResource.PATH_PREFIX.length())); } finally { if (hsess != null) I18Ns.cleanup(request, old); else { Charsets.cleanup(request, old); SessionsCtrl.setRawCurrent(oldsess); } } return; //done } final Session sess = WebManager.getSession(_ctx, request, false); if (withpi) { final AuExtension aue = getAuExtensionByPath(pi); if (aue == null) { response.sendError(response.SC_NOT_FOUND); log.debug("Unknown path info: "+pi); return; } Object oldsess = null; if (sess == null) { oldsess = SessionsCtrl.getRawCurrent(); SessionsCtrl.setCurrent(new SessionResolverImpl(_ctx, request)); //it might be created later } final Object old = sess != null? I18Ns.setup(sess, request, response, "UTF-8"): Charsets.setup(null, request, response, "UTF-8"); try { aue.service(request, response, pi); } finally { if (sess != null) I18Ns.cleanup(request, old); else { Charsets.cleanup(request, old); SessionsCtrl.setRawCurrent(oldsess); } } return; //done } //AU if (sess == null) { response.setIntHeader("ZK-Error", response.SC_GONE); //denote timeout //Bug 1849088: rmDesktop might be sent after invalidate //Bug 1859776: need send response to client for redirect or others final String dtid = request.getParameter("dtid"); if (dtid != null) sessionTimeout(request, response, WebManager.getWebManager(_ctx).getWebApp().getConfiguration(), dtid); return; } final Object old = I18Ns.setup(sess, request, response, "UTF-8"); try { process(sess, request, response); } finally { I18Ns.cleanup(request, old); } }
5,6,7基本没什么,这里就不讲了