﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-canonical-随笔分类-Witrix开发平台</title><link>http://www.blogjava.net/canonical/category/4850.html</link><description /><language>zh-cn</language><lastBuildDate>Mon, 18 Jan 2010 20:33:00 GMT</lastBuildDate><pubDate>Mon, 18 Jan 2010 20:33:00 GMT</pubDate><ttl>60</ttl><item><title>资源文件管理</title><link>http://www.blogjava.net/canonical/archive/2010/01/17/309866.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Sun, 17 Jan 2010 09:51:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2010/01/17/309866.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/309866.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2010/01/17/309866.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/309866.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/309866.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; html主要通过内置的&lt;script&gt;,&lt;link&gt;, &lt;img&gt;等标签引入外部的资源文件，一般的Web框架并没有对这些资源文件进行抽象，因此在实现组件封装时存在一些难以克服的困难。例如一个使用传统JSP Tag机制实现的Web组件中可能用到js1.js, js2.js和css1.css等文件，当在界面上存在多个同样的组件的时候，可能会生成多个重复的&lt;script&gt;和&lt;link&gt;标签调用，这将对页面性能造成严重的负面影响。资源管理应该是一个Web框架的内置组成部分之一。在Witrix平台中，我们主要借助于tpl模板引擎来输出html文本, 因此可以通过自定义标签机制重新实现资源相关的html标签, 由此来提供如下增强处理功能:<br />
<br />
1. 识别contextPath<br />
&nbsp;&nbsp; tpl模板中的所有资源相关标签都会自动拼接Web应用的contextPath, 例如当contextPath=myApp时<br />
&nbsp;&nbsp; &lt;script src="/a.js"&gt;&lt;/script&gt; 将最终输出 &lt;script src="/myApp/a.js" ...&gt;<br />
<br />
2. 识别重复装载<br />
&nbsp;&nbsp; &lt;script src="a.js" tpl:once="true"&gt;&lt;/script&gt; <br />
&nbsp;&nbsp; tpl:once属性将保证在页面中script标签实际只会出现一次.<br />
<br />
3. 识别组件内相对路径<br />
&nbsp; 开发Web组件时,我们希望所有资源文件都应该相对组件目录进行定位,但是直接输出的&lt;script&gt;等标签都是相对于最终的调用链接进行相对路径定位的. 例如在page1.jsp中调用了组件A, 在组件A的实现中, 输出了&lt;script src="my_control.js"&gt;&lt;/script&gt;<br />
&nbsp;我们的意图一般是相对于组件A的实现文件进行定位, 而不是相对于page1.jsp进行定位. tpl模板引擎的相对路径解析规则为永远相对于当前文件进行定位. 例如<br />
&nbsp; &lt;c:include src="sub.tpl" /&gt; <br />
在sub.tpl中的所有相对路径都相对于sub.tpl文件进行定位.<br />
<br />
4. 编译期文件有效性检查<br />
&nbsp;&nbsp; 在编译期, tpl引擎会检查所有引入的资源文件的有效性. 如果发现资源文件丢失, 将直接抛出异常. 这样就不用等到上线后才发现文件命名已修改等问题. <br />
<br />
5. 缓存控制<br />
&nbsp; 浏览器缺省会缓存css, js等文件, 因此系统上线后如果修改资源文件可能会造成与客户端缓存不一致的情况. 一个简单的处理方式是每次生成资源链接的时候都拼接文件的修改日期或者版本号, 这样既可利用客户端缓存, 又可以保证总是使用最新版本. 例如<br />
&nbsp; &lt;script src="a.js"&gt;&lt;/script&gt;将会输出 &lt;script src="/myApp/myModule/a.js?344566" ...&gt;<br />
<br />
6. 字符集选择<br />
&nbsp; 为了简化国际化处理, 一般提倡的最佳实践方式是坚持使用UTF-8编码. 但是很多情况下可能使用系统内置的GBK编码会更加方便一些, 另外集成一些既有代码时也存在着不同字符集的问题. 在Witrix平台中, 所有输出的资源标签都会标明对应的字符集, 如果没有明确设置就取系统参数中的缺省字符集.<br />
&nbsp;例如 &lt;script src="a.js"&gt;&lt;/script&gt; 将会输出 &lt;script ... charset="GBK"&gt;&lt;/script&gt;<br />
<br />
7. 缺省theme支持<br />
&nbsp; 为了支持多种页面风格, 往往不是简单的替换css文件即可实现的, 它可能意味着整个组件的实现代码的更换. Witrix平台中通过一系列缺省判断来简化这一过程. 例如如下代码表明如果设置了ui_theme系统参数, 并且对应的特殊实现存在, 则使用特殊实现, 否则系统缺省实现. <br />
&nbsp; &lt;c:include src="${cp:ui_theme()}/ctl_my_ctl.tpl" &gt;<br />
&nbsp;&nbsp;&nbsp; &lt;c:include src="default/ctl_my_ctl.tpl" /&gt;<br />
&nbsp; &lt;/c:include&gt;<br />
<img src ="http://www.blogjava.net/canonical/aggbug/309866.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2010-01-17 17:51 <a href="http://www.blogjava.net/canonical/archive/2010/01/17/309866.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>AOP的应用</title><link>http://www.blogjava.net/canonical/archive/2009/12/13/305770.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Sun, 13 Dec 2009 03:34:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2009/12/13/305770.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/305770.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2009/12/13/305770.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/305770.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/305770.html</trackback:ping><description><![CDATA[<p>&nbsp;&nbsp;&nbsp;&nbsp; AOP(Aspect Oriented Programming)早已不是什么新鲜的概念，但有趣的是，除了事务(transaction), 日志(Log)等寥寥几个样板应用之外，我们似乎找不到它的用武之地。<a href="http://canonical.javaeye.com/blog/34941" mce_href="/blog/34941">http://canonical.javaeye.com/blog/34941</a>
很多人的疑惑是我直接改代码就行了，干吗要用AOP呢？AOP的定义和实现那么复杂，能够提供什么特异的价值呢？<br />
&nbsp;&nbsp;&nbsp; Witrix平台依赖于AOP概念来完成领域模型抽象与模型变换，但是在具体的实现方式上，却与常见的AOP软件包有着很大差异。<a href="http://canonical.javaeye.com/blog/542622" mce_href="/blog/542622">http://canonical.javaeye.com/blog/542622</a>
AOP的具体技术内容包括定位和组装两个部分。简化切点定位方式和重新规划组装空间，是Witrix中有效使用AOP技术的前提。<br />
&nbsp;&nbsp;&nbsp;
在Witrix平台中，对于AOP技术的一种具体应用是支持产品的二次开发。在产品的实施过程中，经常需要根据客户的特定需求，修改某些函数的实现。我们
可以选择在主版本代码中不断追加相互纠缠的if-else语句，试图去包容所有已知和未知的应用场景。我们也可以选择主版本代码和定制代码独立开发的方
式，主版本代码实现逻辑框架，定制代码通过AOP机制与主版本代码融合，根据具体场景要求对主版本功能进行修正。AOP的这种应用与所谓的横切概念是有所
区别的。典型的，一个横切的切点会涉及到很多类的很多方法，而函数定制则往往要求准确定位到某个业务对象的某个特定的业务方法上。传统AOP技术的切点定
义方式并不适合这种精确的单点定位。在Witrix平台中，我们通过直接的名称映射来定义切点。例如，修正spring中注册的MyObject对象的
myFunc方法，可以在app.aop.xml文件中增加如下标签<br />
</p>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">myObject</span><span style="color: #ff0000;">.myFunc</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在原函数执行之前执行<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">aop:Proceed</span><span style="color: #0000ff;">/&gt;</span><span style="color: #000000;">&nbsp;</span><span style="color: #008000;">&lt;!--</span><span style="color: #008000;">&nbsp;执行原函数内容&nbsp;</span><span style="color: #008000;">--&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在原函数执行之后执行<br />
</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">myObject.myFunc</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
<br />
</span></div>
<br />
<p>
[spring对象名.方法名]这种映射方法比基于正则字符串匹配的方式要简单明确的多。spring容器本身已经实现了对象的全局管理功能，spring对象名称必然是唯一的，公开发布的，相互之间不冲突的，没有必要再通过匹配运算重新发现出它的唯一性。<br />
&nbsp;&nbsp; 对于一些确实存在的横切需求，我们可以通过Annotation机制来实现切点坐标标定，将复杂的切点匹配问题重新划归为[对象名.方法名]。<br />
</p>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">@AopClass({"myObject","otherObject"})<br />
&nbsp;&nbsp;class&nbsp;SomeClass{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@AopMethod({"myFunc","otherFunc"})<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;someFunc(){}<br />
&nbsp;&nbsp;}<br />
<br />
</span></div>
&nbsp;
<p>针对以上对象，在app.aop.xml文件中可以定义</p>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">I-myObject</span><span style="color: #ff0000;">.I-myFunc</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif"  alt="" />.<br />
</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">I-myObject.I-myFunc</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
<br />
</span></div>
<img src ="http://www.blogjava.net/canonical/aggbug/305770.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2009-12-13 11:34 <a href="http://www.blogjava.net/canonical/archive/2009/12/13/305770.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>HTML模型增强</title><link>http://www.blogjava.net/canonical/archive/2009/05/30/278973.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Fri, 29 May 2009 16:44:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2009/05/30/278973.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/278973.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2009/05/30/278973.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/278973.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/278973.html</trackback:ping><description><![CDATA[&nbsp; html最早的设计目标只是作为某种多媒体文档展现技术，其设计者显然无法预料到今天Web应用的蓬勃发展，一些设计缺陷也就难以避免。特别是html规范中缺乏对于复杂交互式组件模型的支持，直接导致企业应用的前台开发困难重重。AJAX技术可以看作是对这种困境的一种改良性响应，它试图通过javascript语言在应用层创建并维护一系列复杂的交互机制。很多完善的ajax框架走得相当遥远，最终基本将html作为一种底层&#8220;汇编&#8221;语言来使用。例如，一个很整齐美观的类Excel表格可能是由一个个div拼接而成，与html原生的table元素已经没有任何关系。<br />
&nbsp; <br />
&nbsp;&nbsp; Witrix平台中对于前台html模型也作了一定的增强，但基本的设计思想是尽量利用原生控件，并尽量保持原生控件内在的数据关系，而不是重新构建一个完整的底层支撑环境。采用这种设计的原因大致有如下几点：<br />
1. 前台技术目前竞争非常激烈，我们优先选择的方式是集成第三方组件，尽量保持原生环境有利于降低集成成本。<br />
2. 通过javascript构造的控件可能存在性能瓶颈和其他浏览器内在的限制。例如一般Ajax框架提供的Grid控件都无法支撑大量单元格的显示。<br />
3. Witrix平台的tpl模板技术可以非常方便的生成html文本，并提供强大的控件抽象能力，因此在前台动态创建并组织界面元素在Witrix平台中是一种不经济的做法。<br />
4. Witrix平台提供的分解机制非常细致，存储于不同地方的不同来源的代码会在不同的时刻织入到最终的页面中，基于原生环境有利于降低平台快速演进过程中的设计风险。<br />
<br />
&nbsp;&nbsp; Witrix平台中对于html模型的增强主要关注于以最少的代码实现界面控件与业务逻辑的自然结合。基本结构包括：<br />
1. 通过ControlManager对象在前台建立一种container结构，统一管理控件的注册和获取。js.makeControl(elmOrId)返回特殊注册的控件对象或者根据原生html元素生成一个包装对象。<br />
2. 通过js.getWxValue(elm)和js.setWxValue(elm,value)这两个函数统一对控件的值的存取过程。<br />
3. 通过js.regListener(elm,listenerFunc)统一管理控件之间的相关触发，实现控件之间的相互监听。当js.setWxValue(elm,value)被调用时，注册在ControlManager中的listenerFunc将被调用。<br />
4. stdPage.setFieldValue(fieldName,value)和stdPage.getFieldValue(fieldName,value)统一针对业务字段的值的存取过程，这里fieldName对应于实体上的业务字段名。<br />
5. 通过ajax.addForm(frmId)等函数统一前台提交参数的提取过程，通过stdPage.buildAjax()等函数统一后台服务的调用方式。<br />
6. 通过stdPage对象统一封装业务场景中的"常识"。<br />
基于以上一些基础机制，Witrix平台即可提供一些复杂的业务组件封装。例如&lt;input name="productCode" onkeypress="stdPage.keyPressToLoadRefByCode({objectName:'SomeProduct',queryField:'productCode'})" .../&gt;通过简单的调用一个js函数即可实现如下功能：<br />
a. 在文本框中输入回车的时候自动提交到后台查找对应产品代码的产品，并更新前台多个相关字段的值<br />
b. 如果没有查找到相应产品，则弹出对话框根据界面上已有的部分字段信息提示客户添加新的产品信息。<br />
c. 如果找到多个对应产品，则弹出列表允许客户选择其一。<br />
d. 具体的处理过程可以通过函数参数进行精细的控制。<br />
在meta文件中，结合上下文环境中的元数据信息，我们在缺省情况下可以直接使用 &lt;ds:LoadRefByCodeInputor objectName="SomeProduct" /&gt;标签，不需要任何其他附加参数。<br />
<br />
&nbsp;&nbsp; Witrix平台中一般利用原生控件来保存数据值，而不是将数据保存在分离的js对象中。例如对于一个选择控件，经常要求选择得到的是实体的id,而显示在界面上的是某个其他字段的值。Witrix平台中一般的实现结构是<br />
&nbsp;&nbsp; &lt;input type="hidden" name="${fieldName}" value="${entity[dsMeta.idField]}" id="${id}" textId="text_${id}" /&gt;<br />
&nbsp;&nbsp; &lt;input type="text" value="${entity[dsMeta.nameField]}" id="text_${id}" /&gt;<br />
通过textId等扩展属性即可明确定义控件多个部分之间的关联关系，同时保证控件的实现完全与html规范相兼容。<br />
&nbsp;&nbsp; Witrix平台中目前使用的"标准化"的扩展属性有textId(对应文本显示控件的id), showName(某些无文字显示的选择控件需要保留显示字段值), op(字段作为查询条件提交时的比较算符)，validator(字段值对应的检验函数)，setWxValue/getWxValue(重定义控件值的存取行为)，serializer(特殊处理前台控件的提交参数)等。扩展属性不仅可以引入说明信息，还可以引入丰富的控件行为。<br />
&nbsp;&nbsp; <br />
<br />
<br />
<img src ="http://www.blogjava.net/canonical/aggbug/278973.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2009-05-30 00:44 <a href="http://www.blogjava.net/canonical/archive/2009/05/30/278973.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>报表引擎：结构生成与结构转换</title><link>http://www.blogjava.net/canonical/archive/2007/09/02/142036.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Sun, 02 Sep 2007 01:45:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2007/09/02/142036.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/142036.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2007/09/02/142036.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/142036.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/142036.html</trackback:ping><description><![CDATA[&nbsp;&nbsp; 传统上报表引擎主要完成两项工作：结构描述和结构转换。一般报表设计人员通过可视化设计工具完成对报表结构的描述，然后报表引擎根据这些描述生成不同格式的报表文件，如PDF格式，XLS格式等。这一图景中报表设计工具扮演着关键角色，因为它不仅仅是向用户提供一个直观的界面，更重要的是配置过程本身就是一种分步骤的结构构造过程。理想的情况是用户指定报表中具体有哪些单元格，表格具体有哪些列，而在运行期报表引擎负责向单元格中填充数据。但是对于设计期只能进行动态描述，无法预先确定所有结构元素的报表（例如交叉表的列只能在执行时确定），这种报表模型就会出现问题。一般处理方式都是在报表引擎中内置所有可能的动态报表模型。无论设计工具多么复杂，其内置的原理如果是基于静态结构模型，就无法建立一种抽象机制，这样我们就只能通过重复劳动来应对众多结构类似但是略有不同的报表。<br />
&nbsp;&nbsp; Witrix平台的报表引擎是对程序友好的，它引入了编译期结构运算，在报表编译时可以通过程序吸收大部分结构差异性。在Witrix平台中，报表制作分为三个阶段：设计期 -&gt; 编译期 -&gt; 运行期。报表引擎负责完成三种工作：结构描述，结构生成和结构转换。具体实现动态结构生成的过程其实非常简单。目前所有的Witrix配置文件都通过基础配置引擎进行解析，它定义了统一的dynamic和extends元机制。<br />
&nbsp;&nbsp; &lt;report dynamic="true"&gt;<br />
定义了dynamic="true"的报表定义文件首先作为tpl模板文件来运行，其运行结果再作为报表格式解析。在这种模型下，报表引擎并没有内置如何把动态结构拼接出来的知识，这些知识存在于编译期，而tpl标签的抽象能力使得我们可以把复杂的报表结构生成过程抽象成简单的标签调用形式。<br />
&nbsp;&nbsp; &lt;report dynamic="true"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;body&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;table&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;thead&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;c:forEach var="_h" items="${cols}"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ....<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/table&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/body&gt;<br />
&nbsp;&nbsp; &lt;/report&gt;<br />
<br />
==&gt; <br />
&nbsp;&nbsp; &lt;report dynamic="true"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;body&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;rpt:GenCrossTable tableMeta="${tableMeta}" loopVar="tableData" /&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/body&gt;<br />
&nbsp;&nbsp; &lt;/report&gt;<br />
<br />
&nbsp;&nbsp; 在编译期通过tpl封装可以解决大部分结构生成问题，在运行期报表引擎主要负责的结构问题就简化为数据行展开和单元格合并等确定操作。<br />
&nbsp;&nbsp; Witrix报表引擎的另一个特点是运行期结构生成过程和结构转换过程同时进行，因此不需要在内存中构造一个完整的报表数据对象，大大减轻了内存压力。Witrix报表引擎输出的文件格式目前有html, XML格式的Word文件和XML格式的Excel文件等。每一种输出格式相当于定义了一种渲染模型，它们都是对报表模型的一种展现方式。从某种程度上说这些模型的结构都是等价的，但是完成模型转换所需要的操作往往不是局域化的。例如在html的table中某一单元格具体对应哪一列是受到其他单元格的rowspan和colspan属性影响的, 在Excel中则需要明确指定列的index属性。为了简化运行期逻辑，内置的报表模型必须提供一些冗余结构，从而兼容多种渲染模型。<br />
<img src ="http://www.blogjava.net/canonical/aggbug/142036.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2007-09-02 09:45 <a href="http://www.blogjava.net/canonical/archive/2007/09/02/142036.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>静态描述与动态调整</title><link>http://www.blogjava.net/canonical/archive/2007/05/27/120327.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Sun, 27 May 2007 10:48:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2007/05/27/120327.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/120327.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2007/05/27/120327.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/120327.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/120327.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 描述所关注的是&#8220;what&#8221;，而运行所关注的是&#8220;how&#8221;。在现代软件开发中，描述信息作占的比重日益加大。甚至一种极端的倾向是把所有业务逻辑都写在各种格式的配置文件中. 配置文件目前多采用xml格式，它的优点是自说明的：属性名直接标示了其基本含义，但是这也在一定程度上加重了命名的负担, 造成了配置文件的臃肿。因为在普通的程序语言中，可以用来传递信息的结构更加丰富，例如参数的相对位置，参数类型, 匿名函数, 指针引用等。而一般配置文件中没有定义合适的继承，封装等抽象机制，很难如同普通程序语言那样进行有效的结构压缩。<br>&nbsp;&nbsp;&nbsp; 在很多灵活的弱类型语言中，借助各式语法糖(syntax sugar)可以实现描述性的运行结构, 或者可以看作是构造性的描述, 它在部分程度上消解了描述的诠释问题, 不需要额外的解释器即可实现描述结构的解析. 这有些类似于编译理论中的语法制导翻译, 在动态结构组装方面具有明显的优势. <a  href="http://www.blogjava.net/canonical/articles/19697.html">http://www.blogjava.net/canonical/articles/19697.html</a>. 但是独立的描述信息仍然是有着重要作用的, 关键是作为元数据存在的描述信息可以以多种方式被使用, 并可以被部分使用. 此外一些特殊设计的描述文件可以很自然的汇集系统各个方面的信息到同一层面加以展示,而一个通用语言无论语法如何灵活, 抽象能力如何强大, 毕竟受限于先天的结构, 要做到这一点还是不现实的. <br>&nbsp;&nbsp;&nbsp; 在witrix平台中配置文件的设计一般是综合考虑静态描述和动态调整的需要, 在设计上分成静态描述段和动态运行的init段, 系统将确保init段中的tpl代码会在适当的时候被调用. <br><br><br><img src ="http://www.blogjava.net/canonical/aggbug/120327.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2007-05-27 18:48 <a href="http://www.blogjava.net/canonical/archive/2007/05/27/120327.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>多版本支持</title><link>http://www.blogjava.net/canonical/archive/2007/04/22/112835.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Sun, 22 Apr 2007 15:15:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2007/04/22/112835.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/112835.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2007/04/22/112835.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/112835.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/112835.html</trackback:ping><description><![CDATA[&nbsp;&nbsp; 在商业产品开发中，如何有效的控制同一产品的多个衍生版本是一个非常重要的问题。客户的需求是多样化，差异化的。这些差异有些很小，可以通过参数配置，资源装载，skin切换等方式加以吸收，而有些则要求对界面布局和程序逻辑等作出较大调整。Witrix开发平台在系统基础架构方面为程序的客户化提供了有力的支持。<br>&nbsp;&nbsp; 1. 多版本控制的关键首先在于系统良好的模块划分。因此Witrix平台的beans,auth-map(权限归约规则)等配置文件格式都支持import/include等基础的分解策略,字符串资源和错误码映射等支持多重定义文件，而对于sql.xml(外部sql语句定义), meta.xml, biz.xml, hbm.xml等配置文件采用分模块动态装载机制。<br>&nbsp;&nbsp; 2. 在Witrix系统中定义了一个特殊的custom目录，规定了一般性的覆盖规则：custom目录作为系统根目录的影子目录，如果custom目录下存在同名文件，则优先装载custom目录下的文件。例如,如果custom目录下存在/_config/my/my.biz.xml文件，同时在根目录下也存在/_config/my/my.biz.xml文件, 则实际装载的是custom目录下的实现。这里的一个关键在于只有meta.xml(元数据)，biz.xml(BizFlow描述文件)，.lib.xml(tpl模板库)等具有一定完整性的文件才支持custom机制，而并不是所有资源都采用custom机制。如果每一个tpl文件，css文件，js文件等都优先从custom目录下装载，则很快就会出现循环引用，相对路径计算将会变得非常混乱，更重要的是我们将无法定义资源删除语义。<br>&nbsp;&nbsp; 3. 元数据文件，BizFlow描述文件，PageFlow描述文件等都支持复杂的extends机制，使得我们在扩展时只需要对于系统差异部分进行描述，而不是大段拷贝代码。<br>&nbsp;&nbsp; 4. tpl模板库和sql-map机制等采用的是追加覆盖策略。例如custom目录下的ui.xml标签库文件并不是直接覆盖系统根目录下的ui.xml文件，而是按照标签名进行细粒度的覆盖。系统编译时会自动检查覆盖标签的所有参数要求和原标签相兼容（例如允许增加参数而不允许减少参数），确保所有引用到原标签的tpl代码仍然有效。实际上整个witrix平台多版本扩展机制的一个设计目标就是确保平台主系统向各个分支产品的单向信息流动。在具体的表现上就是我们随时可以拷贝平台主系统覆盖到分支产品的相应目录，所有扩展实现与主系统实现保持分离状态。当然为了保持设计的弹性，系统中也定义了开关参数用来有选择的跳过一致性检查。<br>&nbsp; <br><img src ="http://www.blogjava.net/canonical/aggbug/112835.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2007-04-22 23:15 <a href="http://www.blogjava.net/canonical/archive/2007/04/22/112835.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>PageFlow: Managed Navigation</title><link>http://www.blogjava.net/canonical/archive/2006/11/05/79254.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Sun, 05 Nov 2006 12:35:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2006/11/05/79254.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/79254.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2006/11/05/79254.html#Feedback</comments><slash:comments>7</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/79254.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/79254.html</trackback:ping><description><![CDATA[  按照Tim Berners-Lee的原始设想，互联网的核心概念是超链接(hyperlink), 每一个可访问的资源都具有自己的URI(Universal Resource Identifier), 我们通过唯一的url可以访问到这些资源。从概念上说，每一个页面可以由一个两元组[title, url]来进行描述，title是url显示在界面上时的可读表述。在这一描述下，我们可以建立基本的页面浏览模型，包括浏览历史模型:我们可以把浏览过的页面保存在浏览历史列表中，通过选择历史列表中的条目实现页面跳转。但是随着网页的动态性不断增加，页面的资源语义逐渐丧失，url所对应的不再是一种静态的资源表述概念，而逐渐演变成为一种动态的访问函数. 例如<br />  /view.jsp?objectName=MyObj&amp;objectEvent=ViewDetail&amp;id=1<br />  一个url模式所对应的网页个数在理论上可能是无限多的. 从单一数据值到函数是系统复杂性本质上的一种飞跃. 为了不致在这种无限的世界中迷失方向,我们显然需要对于浏览过程进行更加有效的组织,建立更加有约束性的导航模型. 一种常见的导航模式是在页面的上方显示一条线性的导航路径, 与浏览历史模型不同的是, 页面转换时不是 <br />  list &gt; view item 1 ==&gt;  list &gt; view item 1 &gt; view item 2 <br />而是<br />   list &gt; view item1 ==&gt; list &gt; view item2<br />  为了支持导航路径, 最简单的方式是将页面模型扩展为三元组[id, title, urlExpr], 其中urlExpr是动态表达式, 而id是固定的值. 例如 id=view, urlExpr=/view.jsp?objectName=MyObj&amp;objectEvent=ViewDetail&amp;id=${id}<br />  导航路径的变化规则为navHistory.removeAfter(newPage.id).add(newPage)<br />  为了进一步约化导航路径, 可以将页面模型扩展为<br />  [id, title, urlExpr, group, before, beforeGroup], <br />  其中group定义了页面的分组, 同组的页面在导航路径中相互替代, 而before和beforeGroup定义了页面在导航路径中的相对顺序. 例如对于<br />  [id='list' beforeGroup="detail"] [id='view' group='detail'] [id='update' group='detail']<br />在页面转换时, 导航路径变化不是<br />  list &gt; view item1 ==&gt; list &gt; view item1 &gt; update item1 <br />而是 <br />  list &gt; view item1 ==&gt; list &gt; update item1<br />  在以上的页面模型中, 每一个id对应的是一个不同的页面模板(页面布局), 但是有时我们也需要在同一个页面布局中浏览无限分级的栏目, 此时可以将页面模型扩展为<br />  [id, title, urlExpr, group, before, beforeGroup, path]<br />  <br />  将以上的导航模型应用于一般的web应用时还需要克服一个小小的困难: 动态url的副作用. 例如/update.do?id=1&amp;value=2这种具有动作语义的url访问直接破坏了页面的浏览模型，例如现在我们不再能够按F5键刷新页面，不能通过window.location=window.location调用来刷新页面，在页面回退时我们也将遇到种种困难。为了防止重复提交，一种常见的设计模式是分离POST和GET方法，即通过Form POST向上提交数据，处理完毕后通过redirect指令通知浏览器再次发起GET方法得到新的页面.具体做法如下   <br />    /redirect_after_action.do?id=1&amp;value=2<br />  在执行完action之后, 服务器调用response.sendRedirect(get_url), 通知前台浏览器使用新的url重新取回页面. 这样最终我们可以确保所有页面都是可以通过url直接访问的(没有隐含的post参数),而且是可以重复访问的(无副作用因而可以反复刷新)!<br />  另一种方式是使用ajax方式提交更新请求,当ajax访问成功后, 在回调函数中进行页面跳转.例如:<br />  new js.Ajax().setObjectName('Test')<br />      .setObjectEvent('Update').addForm(myForm)<br />      .callRemote(function(returnValue){<br />        window.location = '/view.jsp?id=1';<br />      })   <br />这里我们也可以根据returnValue的不同选择跳转到不同页面.<br />  在Witrix平台中, 基于Jsplet框架我们定义了PageFlow对象, 它将可配置的导航模型引入到任意页面的组织过程中. 在跳转到一个新的页面的时候, 访问url如下:<br />  /pageflow.jsp?objectName=MyNav&amp;objectEvent=NavToPage&amp;_pageId=view&amp;id=3<br />在重新访问历史页面的时候，只需要<br /> /pageflow.jsp?objectName=MyNav&amp;objectEvent=NavToHistoryPage&amp;_pageId=view<br />  基于jsplet框架的对象化特性，MyNav对象在session中保持了flow的所有状态变量，不需要任何框架上特殊的支持。我们可以在任意页面跳出pageflow, 并在任何时刻选择跳回pageflow, 这些动作不会影响到flow对象的状态。而通过objectScope的设置我们可以精细的控制flow对象的生命周期。同时基于对象化设置，访问页面时我们只需要资源的相对名称(relative identifier), 例如对于各种不同的flow, 页面id都可以叫做view, 通过_pageId=view来访问。<br />  Apache软件基金会旗下有一个beehive项目, <a href="http://beehive.apache.org/docs/1.0m1/pageflow/pageflow_overview.html">http://beehive.apache.org/docs/1.0m1/pageflow/pageflow_overview.html</a>, 其中也定义了所谓pageflow的概念, 这是始创于BEA的一项技术. 但是与Witrix Page Flow不同的是, Beehive Page Flow的核心概念仍然是从struts引申而出的action的概念，所谓的flow是action的连接，而不是通过资源id之间的连接。虽然beeive宣称支持各种对象injection, 并且管理了flow对象的生命周期，但是它并没有规范出this指针这一面向对象最重要的概念之一。Witrix Page Flow关注的重点是已存在的页面之间的有效组织, 它所描述的是某种具有静态资源语义的页面模板之间的关联, 这种关联是较松散的,而不是通过action强关联的. 实际上基于Ajax技术和jsplet框架的消息路由机制, 在每一个页面上都可以进行多种业务操作,甚至更换页面，而不发生pageId的变动, 并不是所有的操作过程都需要在page flow描述中对外暴露。另外一个比较细节化的不同之处是Beehive使用Java Annotation, 而Witrix PageFlow使用xml描述文件, 并实现了pageflow的继承和部分覆盖等高级构造方法. 使用xml描述文件的必要性在于需要复用WebAction, 支持复杂的变换操作, 并集成tpl模板引擎等. Annotation的方式看似简单,但这种简单性同时将系统中的所有组分绑定到了一起, 它也不允许复杂的变换操作的存在. 实际上Beehive的目标并不是真正支持页面编制(包括页面上的页面操作)和页面组织的分离. <img src ="http://www.blogjava.net/canonical/aggbug/79254.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2006-11-05 20:35 <a href="http://www.blogjava.net/canonical/archive/2006/11/05/79254.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>BizFlow extends CRUD</title><link>http://www.blogjava.net/canonical/archive/2006/07/15/58366.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Sat, 15 Jul 2006 14:25:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2006/07/15/58366.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/58366.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2006/07/15/58366.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/58366.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/58366.html</trackback:ping><description><![CDATA[
		<p>  CRUD(Create Read Update Delete)是一般应用程序中最基础的操作，但是用户的需求却很难直接映射到CRUD操作上。例如常见的需求如下:<br /> 1. 不同的业务处理处于不同状态的业务对象:<br />     业务A处理状态为X的业务对象,而业务B处理状态为Y的业务对象<br /> 2. 业务对象处于不同状态的时候允许的操作不同: <br />    状态处于X的业务对象允许操作U, 而状态处于Y的业务对象允许操作V<br /> 3. 不同的业务操作可能修改业务对象的不同属性: <br />     操作U修改业务对象的属性P, 操作V修改业务对象的属性Q<br /> 4. 具有不同权限的人能够从事的业务不同: <br />      角色R处理业务A, 角色S处理业务B<br /> 5. 具有不同权限的人即使从事同一业务,所能够操作的业务对象集合也不同: <br />     角色R处理部门M的业务对象,角色S处理部门N的业务对象.<br /> 6. 具有不同权限的人即使可以操作同一业务对象,所能够采取的业务操作也不同: <br />      角色R只能进行操作U, 角色S只能进行操作V<br /> 7. 在业务对象上执行操作之后可能造成状态变迁: <br />      处于状态X的业务对象上执行操作U后状态变为Y</p>
		<p>以上这些需求往往是系统中最易变的部分, 而它们在概念上恰恰表现为对CRUD的一种限制性描述. 因此通过如下扩展我们可以定义BizFlow的概念: BizFlow = CRUD + Filter. 根据这种观念, witrix平台中BizFlow被实现为DaoWebAction的一种无缝扩展. <br />   在jsplet框架中我们通过如下url模式来访问后台的CRUD操作:<br />   /list.jsp?objectName=MyObj&amp;objectEvent=Query<br />为了实现BizFlow只需通过spring为DaoWebAction配置一个xml配置文件, 此后仍然可以通过<br />    /list.jsp?objectName=MyObj&amp;objectEvent=Query <br />来访问后台的CRUD操作,只是后台会自动应用配置文件中的 bizId="default", bizActionId="Query-default"等配置项. <br />如果我们采用如下url来访问<br />    /list.jsp?objectName=MyObj&amp;objectEvent=Query&amp;$bizId=test&amp;$bizActionId=test    <br />则后台将应用配置项 bizId=manage, bizActionId=Query-test, 而<br />    /list.jsp?objectName=MyObj&amp;objectEvent=BizAction&amp;$bizId=test&amp;$bizActionId=test    <br />则对应于配置项 bizId=manage, bizActionId=BizAction-test.<br />   应用BizFlow配置项之后,所有前台代码都可以不做出任何改变, 因为它们只是对于给定数据的展现. <br />   <br />   BizFlow可以看作是CRUD加上简单的流程控制和权限控制所构成, 但是它与完整的工作流模型还是有着显著区别的. 工作流中所关注的重点首先是流程实例而不是业务对象实例, 在一个流程中是否存在唯一的业务对象,以及业务对象的状态是否随着流程流转发生变化完全是一件独立的事情,它们并不属于抽象的工作流模型本身. 理论上说,一个业务对象可以同时参与多个流程. 在工作流建模中主要通过流程步骤的先后顺序的约束来描述业务进程, 处于同一状态的业务对象可能处在不同的流程步骤中. 而BizFlow可以看作是状态驱动的, 当前业务步骤直接由业务对象的状态决定. 在BizFlow中因为视角是业务对象的状态,因此我们直接面对的是大量处于同一状态的不同的业务处理过程, 而workflow中往往建模的时候强调单流程实例视角,而一般缺乏对于流程实例相关性的描述. 现在国内很多人认为工作流就是状态机其实是对workflow概念的一种误读. <br />  </p>
<img src ="http://www.blogjava.net/canonical/aggbug/58366.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2006-07-15 22:25 <a href="http://www.blogjava.net/canonical/archive/2006/07/15/58366.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]witrix平台中的ajax支持</title><link>http://www.blogjava.net/canonical/archive/2006/02/22/32013.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Wed, 22 Feb 2006 12:36:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2006/02/22/32013.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/32013.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2006/02/22/32013.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/32013.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/32013.html</trackback:ping><description><![CDATA[<p>web程序需要完成&nbsp; html &lt;--&gt; java
之间的映射，在界面越来越复杂，越来越多变的今天，这项工作也变得越来越困难。按照级列设计理论的观点，我们应该去寻求一些中间的过渡步骤。在
witrix平台中，tpl模板引擎正扮演了这种中间角色。通过tpl模板我们实现了如下映射路径</p>
<p>html &lt;--&gt; tpl &lt;--&gt; java </p>
<p>注
意到这里html与tpl之间，以及tpl与java之间的映射都不是trivial的同构关系，而是都可能存在着复杂的运算过程，从而实现了html与
java映射过程中复杂性的分解与均摊。tpl与java之间的关联主要通过EL(expression
language)表达式来完成，而html与tpl的映射则主要通过自定义标签(tag)机制。<br>
注意到tpl所提供的中间层具有独立的重大意
义，它并不是臆造的或者是简单的技术驱动的结果。实际上，在web开发中除了java结构与html结构之外还存在着第三种结构，即用户眼中的界面结构，
本来它与html所描述的结构是简单的一一对应的，但是随着界面技术的发展，html的描述能力逐渐被耗尽，成为了internet时代的"汇编语言"。
现在一个简单的页面片断就可能对应着大量html代码，因而丧失了"所写即所见"的简单性。tpl通过强大的抽象能力在某种程度上恢复了程序员对于界面表
现结构的直观控制能力，并在一定程度上保留了html所见即所得的特性。</p>
<p>在witrix平台中因为存在着tpl这一强大的抽象层，使得我们对于ajax的支持可以采取更加灵活的方式。<br>
ajax(Asynchronous JavaScript + XML)的标准结构是<br>
html &lt;--&gt; js &lt;==&gt; xml &lt;==&gt; java </p>
<p>在
这种结构中通过xml信道的只是数据，而界面的表达逻辑与展现逻辑完全由js来控制。这种结构发展的一个极端是所有的界面展现结构都由
javascript动态构造出来，而完全丧失了html静态描述的特点,丧失了所见即所得的设计。与直接实现html&lt;--&gt;java之间
的映射情况类似，直接实现 html &lt;--&gt;
js之间的映射也是困难的，尽管dom模型的支持可能使得js映射的难度要低于java映射。</p>
<p>在witrix平台中ajax的方案为<br>
html &lt;--&gt; js &lt;==&gt; tpl &lt;--&gt; java</p>
<p>即tpl取代了ajax标准方案中xml的位置，使得映射过程的复杂性得以分散化。</p>
<p>结合jsplet框架的拉模式（pull mode)，我们定义了如下ajax访问接口<br>
js.ajax.load(<a href="mailto:%7Brequest=%27objectName=/@Test&amp;objectEvent=query%27,tpl:%27/test.tpl:partA%27,targetId:%27testDiv%27">{request='objectName=/@Test&amp;objectEvent=query',tpl:'/test.tpl:partA',targetId:'testDiv'</a>});</p>
<p>1。 远程服务请求就是一段普通的http post request， 避免了额外的xml编码解码需求。<br>
2。请求到的数据先由tpl文件来进行处理。注意到这里tpl文件的url分成两部分，前一部分是tpl文件的虚拟路径，而 ：后面的部分，即partA指出请求的是该tpl文件内的partA部分，而不是整个tpl文件。<br>
3。返回的html结果被填充到targetId所指定的html元素中。</p>
<p>test.tpl文件的内容<br>
＜html＞<br>
＜body＞</p>
<p>＜tpl:define id="partA"＞<br>
＜img tpl:tag="ui:EditTable" /＞<br>
＜/tpl:define＞</p>
<p>＜div id="testDiv"＞<br>
＜img tpl:tag="ui:ViewTable" /＞<br>
＜/div＞</p>
<p>＜/body＞<br>
＜/html＞</p>
tpl具有强大的结构构造能力，在这里我们以非常小的代价实现了tpl片断的定义，例如test.tpl中的partA部分。这里通过id访问tpl片断就如同js中通过id来访问html片断一样。<br>
最后提一个很重要的思想：大量零碎的代码片断需要集中存放，否则人的精力会被耗散。一个反例就是struts中的action, 明明只干那么点事，偏偏要占据一个单独的java文件，占据大量单独的配置条目，最终给程序员带来很大的困扰。<img src ="http://www.blogjava.net/canonical/aggbug/32013.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2006-02-22 20:36 <a href="http://www.blogjava.net/canonical/archive/2006/02/22/32013.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]程序中的结构表达</title><link>http://www.blogjava.net/canonical/archive/2005/12/28/25798.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Wed, 28 Dec 2005 14:49:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2005/12/28/25798.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/25798.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2005/12/28/25798.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/25798.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/25798.html</trackback:ping><description><![CDATA[<p><font color="#000000" size="3">　　循环结构是imperative
language的重要组成部分，一般也是程序中比较难以理解的部分。特别是没有软件技术背景的朋友，明显对于循环的理解力较弱。在Von　
Neumann体系结构中，赋值语句是必须的，因而引出了存储概念，也引入了时间概念，因为我们可以区分出赋值前和赋值后的时刻。引入时间之后，本质性的
影响是程序串行化，而强迫我们从并行思考转入串行处理。很多时候这是一种不自然的情况，在我们的自然思维中，我们看到的或想到的也许只是一组静态结构，但
在程序中表达的时候却往往不可避免的需要引入一个动态过程。而我们控制动态结构的能力总是不足的。最近对于函数式语言及处理风格的越来越强烈的要求可能也
从侧面反映了大家对这种结构失配的不满。<br>　　　但是串行思维毫无疑问也是我们正常思维模式的一部分（当然这种思维模式在多大程度上是因为Von
Neumann
体系造成的，也是个很有趣的问题）。例如在页面渲染的时候，我们可能希望预先把所有用到的数据都转载到内存中，赋予不同的变量名，然后在页面模板中我们只
要知道如何把这些数据变量表现出来就可以了。先做完A再做B，这是分层的思想，也是典型的串行思维。而基于数据进行处理，也是Von
Nenuman体系的基本思想。但是如果处处要求预先计算并赋值，往往增加了很多额外的步骤(glue
code)，并且增大了对内存（计算空间）的需求。分层之后，还存在着一个各个层次之间结构匹配的维护问题。<br>　　　面向对象在结构表达方面是一种
巨大的进步。经过多年的发展，我们在表达静态结构关系方面已经是驾轻就熟了。通过属性关联，我们可以沿着对象图进行结构遍历。如果使用成员函数，在这种遍
历过程中还可以包容更多的动态特性。而在数据持久化方面，ORM的盛行也在一定程度上证明了对象图的有效性。使用对象图可以大大降低对赋值语句的需求，减
轻了明确建模的压力（每一次赋值都要求着一个明确的变量名，一个概念），也缓解了Von Neuman体系结构的束缚。例如，我们不再需要<br>　var user = loadUser(userId);<br>&nbsp; var userOrgnization = loadOrgnization(user.orgId);<br>&nbsp; var userOrgnizationName = userOrgnization.name<br>而是直接使用　　user.orgnization.name</font></p>
<font color="#000000" size="3">&nbsp;&nbsp;&nbsp;
目前面向对象所表达的大多数结构还是基于数据语义的，而我们对于函数等高阶结构的控制能力仍然较弱。设计模式在这方面提供了一些经验，但还是远远不够的。
在我们经验不多的时候，我们需要依赖于明确的实体数据，而在我们的理解逐步深入之后我们就可以通过Visitor,
Iterator等模式支撑起整个结构。高阶结构比低阶结构难以控制，一方面是因为动态性本身比静态性难以理解，另一方面函数对信息的使用和流动是一种主
动约束,如果约束的不正确，会造成结构的失效。而数据的使用是完全开放的，很多决定都可以延迟到使用时刻决定。当然，开放性带来的问题也早就众所周知了：
不受限制的使用将导致无法控制的困境。在基础的数据层封装方面，一般我并不提倡大量使用domain
model似的具有丰富语义的数据对象。因为数据是共享的，应该存在一个开放的数据层，在其上可以建立业务对象。混杂在一起会限制系统的演化。</font><img src ="http://www.blogjava.net/canonical/aggbug/25798.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2005-12-28 22:49 <a href="http://www.blogjava.net/canonical/archive/2005/12/28/25798.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]IVarValueSet: Map语义的扩展</title><link>http://www.blogjava.net/canonical/archive/2005/12/28/25792.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Wed, 28 Dec 2005 14:22:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2005/12/28/25792.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/25792.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2005/12/28/25792.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/25792.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/25792.html</trackback:ping><description><![CDATA[<p><font color="#000000" size="3">&nbsp;</font>&nbsp; 关系数据库提供的是集合存储模型， query(fields, condition) ==&gt; list of records, 可以从条件集合映射到记录集合。<br>
当condition退化为单一的key, 而fields采用默认值的时候，我们就退化到Map语义, 从key对象映射到value对象，而不是从集合映射到集合。<br>
很
多时候我们只需要这种简单Map语义的存储模型，例如用户偏好设置的存储。在这种受限的模型下我们也可以更直接的实现cache支持。如果我们希望在
Map的基础上稍微扩展一些集合操作的特性，可以通过key的结构扩展来实现。即规定key采用类似url格式的字符串，实现key空间的树形结构。在
witrix平台中，这种树形结构的映射关系通过IVarValueSet接口来实现。<br>
　interface IVarValueSet{<br>
&nbsp;&nbsp;&nbsp;&nbsp; IVariant getVar(String name);</p>
&nbsp;&nbsp;&nbsp;&nbsp; // 得到前缀为prefix的所有变量构成的子集合，注意这里自然退化的特点<br>
&nbsp;&nbsp;&nbsp;&nbsp; IVarValueSet getSubSet(String prefix);<br>
&nbsp; }<br>
变量名的格式规定为　a.b.c 或者/a/b/c. 这种变量结构的组织和划分方式其实与JBoss项目中的TreeCache结构类似。<img src ="http://www.blogjava.net/canonical/aggbug/25792.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2005-12-28 22:22 <a href="http://www.blogjava.net/canonical/archive/2005/12/28/25792.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]validate: javascript 客户端验证框架</title><link>http://www.blogjava.net/canonical/archive/2005/12/12/23564.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Mon, 12 Dec 2005 14:26:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2005/12/12/23564.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/23564.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2005/12/12/23564.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/23564.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/23564.html</trackback:ping><description><![CDATA[<p>&nbsp;&nbsp;&nbsp; 在witrix平台中，validate.js提供了完整的客户端输入校验框架。其基本思想是为每个输入控件指定验证函数（validator属性），在提交Form的时候自动调用该验证函数即可。<br>
&lt;form action="test.jsp"&gt;<br>
&lt;input type="text" name="userName" validator="js.validate.checkNotEmpty(value,'用户名')" /&gt;<br>
&lt;input type="button" value="submit" onclick="js.validate.submitForm(this.form)" /&gt;<br>
&lt;/form&gt;<br>
&nbsp;&nbsp;&nbsp;
witrix平台的一个基本设计原则是模块的独立性，不仅各个模块之间的耦合很少，我们还尽量避免使用配置文件。与struts等web框架不同，
witrix的输入校验不依赖于外部配置文件，可以完全独立的使用。虽然jsplet框架也提供了服务器端校验的支持，但在实际使用过程中却很少使用。客
户端校验提供了更好的用户体验。而如果我们需要进一步确保业务逻辑的稳定性，例如避免用户伪造客户端url请求，数据校验需要在业务逻辑对象层进行而不应
是在解析用户请求的时候。针对每个form所写的配置文件有很多不方便的地方，例如witrix平台支持从数据库描述文件直接生成操作界面的快速开发，校
验规则在数据描述文件中指定，而同一个字段可能在多个界面中出现，如果针对Form写校验配置文件，就会出现冗余，而难以保证结构的同步。实际上，一个结
构在界面上表现一次，又在校验配置文件中表现一次，就必然会出现同步问题，解决的方法就是面向对象设计中的对象化，局部化，而不是一个个分离的处理层。</p>
&nbsp;&nbsp;&nbsp; 很多客户端的校验框架使用的是一个万能的校验函数，通过参数不同来实现不同校验。例如<br>
&lt;input type="text" name="userName" validateType="required" /&gt;<br>
这种方式的扩展性不好。正如面向对象设计中的通常做法，我们通过使用回调函数（虚拟函数）来实现可扩展的设计。<img src ="http://www.blogjava.net/canonical/aggbug/23564.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2005-12-12 22:26 <a href="http://www.blogjava.net/canonical/archive/2005/12/12/23564.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]tpl与FreeMarker的标签对比</title><link>http://www.blogjava.net/canonical/archive/2005/12/12/23560.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Mon, 12 Dec 2005 14:18:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2005/12/12/23560.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/23560.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2005/12/12/23560.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/23560.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/23560.html</trackback:ping><description><![CDATA[<p>witrix平台中的tpl模板技术是一种通用的xml动态标签技术，不仅可以用于文本生成，而且可以用于任何需要动态标签的地方，例如工作流引擎
的配置和执行脚本。tpl模板引擎采用的不是jsp
tag的标准机制，而是重新设计并实现的。在开发的后期，因为jstl标准出现，我们对标签的命名作了一定的修改，以尽量符合标准的调用接口。tpl模板
语言完全符合xml规范，其标签定义都是完全独立开发的。在开发tpl的时候，我们甚至没有看到任何类似于c:forEach和c:if的标签设计。但是
我们发现，tpl的动态处理功能与jstl虽然命名不同，但是基本是等价的，所以修改是非常直接的过程。 </p>
<p>FreeMarker是一种流
行的文本模板语言，其语法类似于xml
tag，但是命名规则不同。这实在是一种令人费解的设计。有意思的是，我们发现tpl的功能集也包含了FreeMarker的功能集。这实际上表明了一件
事情，xml动态标签存在一些必然的功能需求，无论是jsp tag, FreeMarker还是tpl,
所不同的只是表现形式而已。但这种表现形式的差异却又造成了实际功能上的巨大差异。 </p>
<p>tpl与FreeMarker具体对比如下。 </p>
<p>宏定义<br>＜#macro greet person＞<br>＜font size="+2"＞Hello ${person}＜/font＞<br>＜/#macro＞]]＞</p>
<p>＜c:lib namespace="test"＞<br>＜greet demandArgs="person"＞<br>＜font size="+2"＞Hello ${person}＜/font＞<br>＜/greet＞<br>＜/c:lib＞</p>
<p>tpl具有更加强大的变量域控制手段，可以通过importVars参数来指定是否使用调用环境中的变量作为缺省参数。另一方面，tpl具有更加灵活的参数校验规则，可以通过demandArgs, otherArgs等参数来指定对自定义标签参数的校验规则。 <br>调用宏<br>＜@greet person="Fred" /＞ <br>＜test:greet person="Fred" /＞</p>
<p>嵌套内容<br>＜#macro border＞<br>＜table border="4" cellspacing="0" cellpadding="4"＞＜tr＞＜td＞<br>＜#nested＞<br>＜#nested＞ <br>＜/tr＞＜/td＞＜/table＞<br>＜/#macro＞ <br>＜c:lib namespace="test"＞<br>＜border type="bodyTag"＞<br>＜table border="4" cellspacing="0" cellpadding="4"＞＜tr＞＜td＞<br>＜cp:compile src="${tagBody}" /＞<br>＜/tr＞＜/td＞＜/table＞<br>＜/border＞<br>＜/c:lib＞</p>
<p>tpl的＜cp:compile＞指令在执行时可以指定xslt参数，从而在编译tagBody之前应用xslt变换。 <br>复杂嵌套<br>与FreeMark一样,嵌套内容可以是复杂内容</p>
<p>＜@border＞<br>＜ul＞<br>＜@do_thrice＞<br>＜li＞＜@greet person="Joe"/＞<br>＜<a>/@do_thrice</a>＞<br>＜/ul＞<br>＜<a>/@border</a>＞ <br>＜test:border＞<br>＜ul＞<br>＜test:do_thrice＞<br>＜li＞＜test:greet person="Joe" /＞＜/li＞<br>＜/test:do_thrice＞<br>＜/ul＞<br>＜/test:border＞</p>
<p>导入库<br>＜#import "/lib/my_test.ftl" as my＞ <br>＜c:lib src="/lib/my_test.ftl" namespace="my" /＞</p>
<p>创建或替代变量<br>＜#assign mail="<a href="mailto:jsmith@other.com">jsmith@other.com</a>" /＞ <br>＜c:set var="mail" value="<a href="mailto:jsmith@other.com">jsmith@other.com</a>" default="xx"/＞</p>
<p>判断<br>＜#if animals.python.price ＜ animals.elephant.price＞<br>Pythons are cheaper than elephants today.<br>＜/#if＞ <br>＜c:if test="${lt(animals.python.price,animals.elephant.price)}"＞<br>Pythons are cheaper than elephants today.<br>＜/c:if＞</p>
<p>tpl因为是xml语法，算术操作符＜和＞必须转义后才能使用，使用起来很不方便，因而最终决定tpl不支持操作符，通过lt(), gt()等函数来实现功能。 <br>循环<br>＜#list animals as being＞<br>＜tr＞＜td＞${being.name}＜td＞${being.price} Euros<br>＜/#list＞ <br>＜c:forEach var="being" items="${animals}" ＞<br>＜tr＞＜td＞${being.name}＜td＞${being.price} Euros<br>＜/c:forEach＞</p>
<p>tpl提供＜c:tile＞等多种循环方式 <br>include指令<br>＜#include "/copyright_footer.html"＞ <br>＜c:include src="/copyright_footer.html" /＞</p>
<p>tpl强大的模板功能加上jsplet框架面向对象的能力，使得我们可以轻易的封装复杂的界面组件。而且这种封装能力还不需要Tapestry那种复杂的配置文件。tpl对portal应用的支持也是一个自然的发展过程。</p>
<img src ="http://www.blogjava.net/canonical/aggbug/23560.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2005-12-12 22:18 <a href="http://www.blogjava.net/canonical/archive/2005/12/12/23560.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]Exceptions: 统一异常处理</title><link>http://www.blogjava.net/canonical/archive/2005/12/02/22302.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Fri, 02 Dec 2005 15:00:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2005/12/02/22302.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/22302.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2005/12/02/22302.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/22302.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/22302.html</trackback:ping><description><![CDATA[<p>&nbsp;&nbsp;&nbsp; 在witrix平台中，异常处理没有采用java语法支持的checked exception,
也不提倡使用自定义的异常类，
而是定义了少数几个RuntimeException基类，一般是CommonException(RuntimeException的派生类)。<br>
&nbsp;&nbsp;&nbsp;
在我自己的经验中，checked exception从未发挥过实质性的作用。checked
exception在某种程度上破坏了封装性原则。我们一般不会在最细的粒度上处理异常，而是在某个统一的模块节点处进行。如果使用checked
exception,
则从最底层的调用到具体异常处理层的整个调用堆栈上的函数都必须明确标记自己不处理该异常，这是完全不必要的负担。这种细粒度上的负担往往将程序员引导到
错误的方向上去，例如编写catch块直接捕获异常<br>
&nbsp; try{<br>
&nbsp;&nbsp;&nbsp;&nbsp; ...<br>
&nbsp; }catch(MyException e){<br>
&nbsp;&nbsp;&nbsp;&nbsp; e.printStackTrace();<br>
&nbsp; }<br>
在witrix平台中通过包装类来将checked exception包装为RuntimeException, 而且除了在最终代码处理模块决不屏蔽异常。<br>
&nbsp;try{<br>
&nbsp;&nbsp;&nbsp; ...<br>
&nbsp;}catch(IOException e){<br>
&nbsp;throw Exceptions.source(e); // 此时会自动trace异常堆栈及异常消息<br>
&nbsp;}</p>
<p>（后来看到Bruce Eckel的文章<a href="http://www.mindview.net/Etc/Discussions/CheckedExceptions">Does Java need Checked Exception</a>，发现大家在对待checked exception的态度上倒是心有戚戚焉。）</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;
一般使用自定义的异常类似乎是要将类名作为错误返回码使用，利用java编译器可以做所谓的强类型检查，这实在是一种概念上的浪费。毕竟创建并维护一个
java类还是有一定的代价的，特别是错误码经常变动而且数量不菲。实际上，java类库的设计中也是尽量重用已有的异常类，例如整个jdbc包只抛出
SQLException异常，xml包只抛出SAXException异常。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 使用异常，常见的方法是抛出一个字符串消息，例如 throw new
MyException("the object manager does not contains the object :" +
objectName);<br>
这种做法的主要问题是，字符串异常消息无法进行进一步的处理，因而只能直接显示给最终用户，这一方面限制了错误显示的格式和方式，另一方面也不利于程序的多语言支持。<br>
&nbsp;&nbsp;&nbsp;&nbsp; witrix平台中抛出异常的标准方法为 <br>
&nbsp;throw Exceptions.code(errorCode).param(paramValue).param(paramName,paramValue);<br>
例如<br>
&nbsp;&nbsp;&nbsp; throw Exceptions.code("web.CAN_err_missing_object_in_manager").param(objectName).param(objectManager);</p>
<p>class Exceptions{<br>
&nbsp;&nbsp;&nbsp; public static CommonException code(String errorCode){<br>
&nbsp;&nbsp;return new CommonException(code);<br>
&nbsp;}<br>
}</p>
<p>class CommonException extends RuntimeException{<br>
&nbsp;public CommonException param(Object paramValue){<br>
&nbsp;&nbsp;...<br>
&nbsp;&nbsp;return this;<br>
&nbsp;}<br>
}<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Exceptions规定只使用规范格式的错误码而不是任意格式的异常消息。这样在捕获异常之后，就可以根据错误码和当时的语言Locale设置来决定最终显示的消息格式。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 同时CommonException采用流式设计来支持任意数量的自定义参数。这一方面减少了自定义异常类的需求，另一方面也避免了将参数与错误码混合的倾向，即我们就不会倾向于 <br>
使用 throw Exceptions.code("the object manager does not contains the object :" + objectName);</p>
<img src ="http://www.blogjava.net/canonical/aggbug/22302.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2005-12-02 23:00 <a href="http://www.blogjava.net/canonical/archive/2005/12/02/22302.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>tpl标签定义</title><link>http://www.blogjava.net/canonical/archive/2005/11/27/21580.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Sun, 27 Nov 2005 12:32:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2005/11/27/21580.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/21580.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2005/11/27/21580.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/21580.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/21580.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; tpl自定义标签的设计目标之一是尽量减少配置说明项. 在tpl标签库中, 标签定义格式如下<br>
&nbsp;&nbsp;&nbsp; &lt;标签库名称&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;自定义标签名 demandArgs="argA, argB" <br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; importVars="varA, varB"<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
otherArgs="optionalArgA, optionalArgB" localScope="trueOrFalse" &gt;<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 自定义标签的内容, 可以是任何tpl代码<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/自定义标签名&gt;<br>
<br>
&nbsp;&nbsp;&nbsp; &lt;/标签库名称&gt;<br>
<br>
&nbsp;&nbsp;&nbsp; demandArgs中指定调用时必须给定的参数的名称列表,
importVars指定从调用环境中导入的变量的名称列表,otherArgs指定可选参数的名称列表. demandArgs,
importVars和otherArgs这三者的集合包含了所有该自定义标签能够接受的参数. tpl编译器会检查这些调用规则是否被满足.
在运行的时候, 未指定的可选参数会被初始化为null.<br>
<br>
&nbsp;&nbsp;&nbsp; 在调用时明确的指定的变量值会覆盖importVars导入的变量值. 例如<br>
&nbsp;&nbsp;&nbsp; &lt;c:set var="varA" value="a" /&gt;<br>
&nbsp;&nbsp;&nbsp; &lt;MyLib:自定义标签名 /&gt; // 根据importVars设定, 在此标签内varA的值为a<br>
&nbsp;&nbsp;&nbsp; &lt;MyLib:自定义标签名 varA="b" /&gt; // args设定会覆盖importVars导入的值,因此在标签内部 varA的值为b<br>
&nbsp;&nbsp;&nbsp; // 调用标签完成后, varA的值恢复为a<br>
<br>
&nbsp;&nbsp;&nbsp; tpl中的参数声明方式是非常简化的,但是它仍然保留了最关键的信息:变量名称. 而在弱类型的Expresison Language中, 变量类型本来就不重要. 与jsp tag中的标签声明作个对比.<br>
&nbsp;&nbsp;&nbsp;&nbsp; &lt;tag&gt;<br>
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&lt;name&gt;template&lt;/name&gt;<br>
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&lt;tagclass&gt;edu.thu.web.tags.TemplateTag&lt;/tagclass&gt;<br>
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &lt;bodycontent&gt;JSP&lt;/bodycontent&gt;<br>
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&lt;attribute&gt;<br>
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&lt;name&gt;src&lt;/name&gt;<br>
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&lt;required&gt;true&lt;/required&gt;<br>
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &lt;rtexprvalue&gt;true&lt;/rtexprvalue&gt;<br>
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&lt;/attribute&gt;<br>
&nbsp;&nbsp; &nbsp;&lt;/tag&gt;<br>
&nbsp;&nbsp;&nbsp; jsp tag这种标签声明方式非常冗长, 提供的有效信息密度很低, 而相对于tpl标签的声明方式所能够提供的附加信息也没有很大的意义. 这种设计上的问题也深深的影响到JSF等派生技术.<br>
<br>
&nbsp;&nbsp;&nbsp; localScope参数指定了此自定义标签是否具有局部变量环境, 如果为true(缺省值),&nbsp;
则调用此标签的时候会自动进行变量压栈处理, 在标签内部无法访问参数列表之外的变量, 运行中所产生的临时变量也不会影响到外部环境.
tpl中的变量堆栈与webwork的ValueStack机制是有一些差异的.
webwork2中的ognl语言在访问OgnlValueStack中的对象的时候缺省采用的是一种递归查找机制, 即在当前环境中找不到对象,
则自动查找上一层环境中的变量. tpl中的标签结构可以多重嵌套, 产生非常复杂的结构, 所以缺省情况下tpl标签采用了类似于函数堆栈的设计,
在子标签中的代码一般情况下是无法访问父标签环境中的变量的(除非指定了localScope参数为true).
localScope支持与importVars机制相结合之后, 我们可以实现比OgnlValueStack更加灵活也更加稳健的变量访问策略.
&nbsp;<br>
<br>
<img src ="http://www.blogjava.net/canonical/aggbug/21580.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2005-11-27 20:32 <a href="http://www.blogjava.net/canonical/archive/2005/11/27/21580.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>tpl标签结构</title><link>http://www.blogjava.net/canonical/archive/2005/11/22/21045.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Tue, 22 Nov 2005 15:09:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2005/11/22/21045.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/21045.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2005/11/22/21045.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/21045.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/21045.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; witrix平台中的tpl模板技术的重点在于标签定义的设计, 在于如何最大限度的发挥xml格式的表达能力。<br>
&nbsp;&nbsp;&nbsp; tpl自定义标签的基本结构如下：<br>
&nbsp;&nbsp;&nbsp; &lt;Namespace:TagName tpl:tag="realTagName"<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tpl:noborder="${booleanExprInCompileContext}"<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tpl:ignore="${booleanExprInCompileContext}" <br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; attrName="stringOrExpression" cp:attributeInCompileContext="atringOrExpression"<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OtherNamespace:OtherAttrName="stringOrExpression"<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bodyContent<br>
&nbsp;&nbsp;&nbsp; &lt;/NameSpace:TagName&gt;<br>
&nbsp;&nbsp;&nbsp;
自定义标签总是处在某一名字空间中， tpl名字空间中的属性由tpl编译器负责进行解析并处理,
而cp名字空间中的属性在编译期能够访问，其他名字空间的属性被完全忽略，
一般只有decorator会识别这些属性(例如cache:timeout).所有无名字空间的属性都相当于是自定义标签的调用参数，在标签运行的时候
可以在标签内部访问到。<br>
&nbsp;&nbsp;&nbsp; tpl通过对namespace的使用， 避免了系统属性, decorator属性与普通标签属性之间的相互干扰， 这与JSF和Tapestry都是不同的。<br>
&nbsp;&nbsp;&nbsp;
tpl:tag属性指定此标签在编译时对应的真实标签名,
即编译期会识别RealTagName而不是Namespace:TagName。tpl:noborder为true的时候相当于是直接编译
bodyContent, 例如用来在编译期控制是否在界面上使用某种边框。<br>
&nbsp;&nbsp;&nbsp; tpl:ignore为true的时候，此标签将被忽略而不会被编译。<br>
&nbsp;&nbsp;&nbsp;
bodyContent在编译期成为tagBody变量, 由自定义标签自己决定如何处理,
这种方式比FreeMarker的&lt;#nested&gt;机制要灵活和强大的多. 例如在标签内部我们可以使用&lt;cp:compile
src="${tagBody}" /&gt; 这等价于 FreeMarker的&lt;#nested&gt;. 也可以使用<br>
&nbsp;&nbsp;&nbsp; &lt;cp:compile
src="${tagBody.existingChild('header')}" /&gt;从bodyContent中取出header子节点.
甚至我们可以对tagBody进行加工之后再进行编译.<img src ="http://www.blogjava.net/canonical/aggbug/21045.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2005-11-22 23:09 <a href="http://www.blogjava.net/canonical/archive/2005/11/22/21045.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]meta-enhanced Map</title><link>http://www.blogjava.net/canonical/archive/2005/11/22/21010.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Tue, 22 Nov 2005 09:53:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2005/11/22/21010.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/21010.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2005/11/22/21010.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/21010.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/21010.html</trackback:ping><description><![CDATA[&nbsp; hibernate等O/R
Mapping软件包中使用javabean来作为数据的载体， 但这些bean一般除了get/set等数据访问方法之外没有什么其它的业务方法。
前一段时间有人认为这是所谓贫血的领域模型(Anemia Domain Model)，引发了一场讨论。
其实这些bean的作用仅是表达了领域内的数据关系， 本身并不可能作为完整的领域模型层存在。
在数据层，我们所需要的是数据对外暴露，因为我们无法预知这些数据的使用方式，
就象是实验数据发表出来以后你无法预知别人如何分析一样，这时信息流是开放的，向外的：信息在这里，放马过来吧。
而在业务逻辑层，复杂的逻辑控制交织在一起，我们需要精细的控制信息通道，通过函数封装，我们反转了信息流的方向：取到什么数据是由调用者提供的信息决定
的。<br>
&nbsp;&nbsp;&nbsp;&nbsp; 实际上，在ORM软件中使用的bean基本上与一个Map类似，只是它具有java
Class所提供的元数据，而访问数据时又必须通过get/set方法，因而在这些方法中能够根据元数据动态的作出响应。在witrix平台的统一数据访
问框架中主要基于Map等通用数据类型，而不是个性化的bean。为了使得Map具有与bean一样的动态响应能力，只需要加入meta的支持即可。<br>
interface IExMap extends Map{<br>
&nbsp;&nbsp;&nbsp;&nbsp; IMapChecker getChecker();<br>
&nbsp; Map getModifications();<br>
&nbsp; ...<br>
}<br>
在get/set之前可以通过IMapChecker来实现动态处理，对Map中数据所作的修改也可以记录下来<img src ="http://www.blogjava.net/canonical/aggbug/21010.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2005-11-22 17:53 <a href="http://www.blogjava.net/canonical/archive/2005/11/22/21010.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>延迟加载的数据集合</title><link>http://www.blogjava.net/canonical/archive/2005/11/19/20545.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Sat, 19 Nov 2005 03:04:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2005/11/19/20545.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/20545.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2005/11/19/20545.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/20545.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/20545.html</trackback:ping><description><![CDATA[  
				      				      &nbsp;&nbsp;&nbsp;
在程序中需要返回一个数据集合的时候, 应该尽量选用标准的Java集合类接口,例如List, Map等.
有时也见到有人选择返回Iterator对象, 一般情况下这不是很好的选择. Iterator对象的功能有限, 而且存在一种即时消费的特点,
我们一般不能把一个Iterator保存起来留待以后使用. 而且JDK提供的集合类也不能从Iterator直接构造出来,例如没有 new
ArrayList(myIterator), 这样为数据复制造成一定的困难. <br>
&nbsp;&nbsp;&nbsp; Iterator在理论上的好处之一是可以支持延迟加载数据, 但是实现起来比较繁琐而且单条加载也是比较低效的. 在witrix平台中如果需要延迟加载数据集合, 我一般选择使用IPageViewer接口<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; interface IPageViewer{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public List getAll();<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public int getTotalCount();<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public List listPage(int startPos, int maxCount);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp;
IPageViewer通过getAll()可以转换到List, 也可以通过 new
Pager(pageViewer,pageSize).itemIterator()得到单条数据的Iterator, 其内部采用分页加载模式.
搜索引擎返回的结果和数据库查询的结果都可以使用这一接口. <img src ="http://www.blogjava.net/canonical/aggbug/20545.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2005-11-19 11:04 <a href="http://www.blogjava.net/canonical/archive/2005/11/19/20545.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]交叉表：平面表的并置</title><link>http://www.blogjava.net/canonical/archive/2005/11/19/20543.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Sat, 19 Nov 2005 03:02:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2005/11/19/20543.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/20543.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2005/11/19/20543.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/20543.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/20543.html</trackback:ping><description><![CDATA[<p>交叉表(Cross Table)的基本特点是具有横纵两个自由延展的维度，而平面表结构只有一个可延展的维度，因为平面表的列名和列数是确定的。例如，地区的产品销售数量，在平面表中表达为<br>
district_id product_id sell_num<br>
如果表现为交叉表，则为<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; productA&nbsp; productB<br>
districtA&nbsp;&nbsp; sellNum&nbsp;&nbsp; sellNum<br>
districtB&nbsp;&nbsp; sellNum&nbsp;&nbsp; sellNum<br>
这种结构上的失配需要通过程序逻辑来进行调整。</p>
<p>注意到平面表结构只具有一个可延展的维度，而join可以看作是该维度上的连接操作。因此我们可以将交叉表看作是多个简单平面表结构并置的结果。即分解为<br>
A:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; productA<br>
districtA&nbsp;&nbsp; sellNum<br>
districtB&nbsp;&nbsp; sellNum</p>
<p>B:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; productB<br>
districtA&nbsp;&nbsp; sellNum<br>
districtB&nbsp;&nbsp; sellNum</p>
<p>横向维度的扩展在程序中表达。</p>
<p>SqlInfo结构封装了这种简单平面表的分解片断。<br>
class SqlInfo{<br>
&nbsp;&nbsp;&nbsp; List fieldNames;<br>
&nbsp;&nbsp;&nbsp; SQL sql;<br>
&nbsp;&nbsp;&nbsp; String keyField;<br>
}</p>
<p>我们在程序中通过JoinBuilder来实现横向维度的构造<br>
JoinBuilder.begin(sqlInfoA)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .join(sqlInfB)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .leftJoin(sqlInfoC)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .end();<br>
生成的sql语句示例如下<br>
select sqlInfoA.fieldNames, sqlInfoB.fieldNames<br>
from sqlInfoA.sql join sqlInfoB.sql<br>
on sqlInfoA.keyField = sqlInfoB.keyField</p>
<img src ="http://www.blogjava.net/canonical/aggbug/20543.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2005-11-19 11:02 <a href="http://www.blogjava.net/canonical/archive/2005/11/19/20543.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]Exportor: Visitor模式</title><link>http://www.blogjava.net/canonical/archive/2005/11/19/20542.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Sat, 19 Nov 2005 03:01:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2005/11/19/20542.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/20542.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2005/11/19/20542.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/20542.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/20542.html</trackback:ping><description><![CDATA[<p>数据导出的功能大致可以分解为三个部分: 1. 从数据源读取一条记录 2. 将一条记录导出为指定格式 3. 循环调用1和2<br>
首
先我们需要一种机制来对外暴露数据源（一种Container）中的数据，Iterator模式恰能满足要求。其次，我们需要一种机制来对一系列数据进行
处理，这对应于Visitor模式。第三，在组合Iterator模式和Visitor模式的处理过程中，我们需要表达出平面表数据集的基本特征。<br>
在witrix平台中，平面表数据导出和转换通过TablePageProcessor对象来完成，<br>
class TablePageProcessor{<br>
&nbsp;IPageViewer viewer;</p>
<p>&nbsp;public Object process(ITableVisitor visitor){<br>
&nbsp;&nbsp;Pager pager = new Pager(viewer, pageSize);<br>
&nbsp;&nbsp;Iterator it = pager.itemIterator();<br>
&nbsp;&nbsp;visitor.visitBegin(headers);<br>
&nbsp;&nbsp;while(it.hasNext()){<br>
&nbsp;&nbsp;&nbsp;Object row = it.next();<br>
&nbsp;&nbsp;&nbsp;if(!visitor.visitRow(row))<br>
&nbsp;&nbsp;&nbsp;&nbsp;break;<br>
&nbsp;&nbsp;}<br>
&nbsp;&nbsp;return visitor.visitEnd();<br>
&nbsp;}<br>
}</p>
<p>interface ITableVisitor{<br>
&nbsp;void visitBegin(List headers);<br>
&nbsp;boolean visitRow(Object row);<br>
&nbsp;Object visitEnd();<br>
}</p>
IPageViewer是暴露平面表数据的标准接口，它通过Pager对象的包装之后可以提供各种Iterator.<br>
ITableVisitor体现了平面表数据的基本特征: header + 一系列row, 存在数据边界(起始与终止)<br>
TablePageProcessor固化了IPageViewer和ITableVisitor的组合过程。<br>
ITableVisitor可以有多种实现，例如CSVBuilder, ExcelBuilder等等。<img src ="http://www.blogjava.net/canonical/aggbug/20542.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2005-11-19 11:01 <a href="http://www.blogjava.net/canonical/archive/2005/11/19/20542.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]Pager: Bridge模式</title><link>http://www.blogjava.net/canonical/archive/2005/11/19/20541.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Sat, 19 Nov 2005 03:00:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2005/11/19/20541.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/20541.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2005/11/19/20541.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/20541.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/20541.html</trackback:ping><description><![CDATA[<p>分页的功能由两部分组成：取数据和计算分页。其中取数据的功能由IPageViewer接口实现<br>
interface IPageViewer{<br>
&nbsp;int getTotalCount();<br>
&nbsp;List getAll();<br>
&nbsp;int listPage(int startPos, int maxCount);<br>
}<br>
Pager是用户调用时的接口<br>
class Pager{<br>
&nbsp;public List getAll(){}<br>
&nbsp;public List listPage(){}<br>
&nbsp;public int getPageCount(){}<br>
&nbsp;public int getPageSize(){}<br>
&nbsp;public int getCurrentPage(){}<br>
&nbsp;...<br>
}<br>
Pager使用IPageViewer作为数据供体，自身仅提供分页计算的功能。在witrix平台中, IPageViewer是表格数据的标准列举方式，因为与List接口相比，IPageViewer容许部分加载。<br>
IPageViewer
可以有多种实现，如ListPageViewer, XmlPageViewer, ExcelPageViewer,
DbTablePageViewer等。一般情况下Pager提供的功能已经足够了，特殊情况下可以通过继承来扩展。例如卡片浏览和分页浏览模式之间的互
相切换通过派生类ItemPager来实现。<br>
</p>
				  
             <img src ="http://www.blogjava.net/canonical/aggbug/20541.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2005-11-19 11:00 <a href="http://www.blogjava.net/canonical/archive/2005/11/19/20541.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]TreeNode:长程关联</title><link>http://www.blogjava.net/canonical/archive/2005/11/19/20540.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Sat, 19 Nov 2005 02:59:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2005/11/19/20540.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/20540.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2005/11/19/20540.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/20540.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/20540.html</trackback:ping><description><![CDATA[<p>java中最常用的数据结构类型是Map和List，
它们也是Container的两种基本模式，一个是根据特征值定位，一个是根据地址定位。
它们共同的一个特征是表达了数据之间的直接的，短程的一种相关性。另一种常见的数据结构Tree则表达了数据之间的一种长程的关联：根节点与其所有层次上
的子节点之间都存在着关联。 文件系统，组织机构, XML文档等都可以对应为Tree数据结构。在描述树形结构的时候，我们经常使用XML文件，
但是XML文件在程序中操纵起来并不方便，这其中的一个重要原因是XML是面向文档的，即操纵XML的API返回的和使用的都只能是文本字符串，而不能直
接使用程序中常见的其他数据结构。在witrix平台中操纵Tree结构的标准接口是TreeNode类，它的设计是面向应用的，即节点的属性值为
Object类型而不是String类型。</p>
<p>Tree由三部分组成: 属性，值， 子节点</p>
<p>class TreeNode implements IVariant{<br>
&nbsp;List getChildren();</p>
<p>&nbsp;int getChildCount();<br>
&nbsp;TreeNode child(int index);</p>
<p>&nbsp;/** 当name对应的节点不存在时将会自动创建该节点 */<br>
&nbsp;TreeNode child(String name);</p>
<p>&nbsp;&nbsp;&nbsp; /** 当name对应的节点不存在时返回null */<br>
&nbsp;TreeNode existingChild(String name);</p>
<p>&nbsp;Map getAttributes();<br>
&nbsp;IVariant attribute(String name);<br>
&nbsp;void setAttribute(String name, Object attrValue);<br>
}</p>
<p>TreeNode.attribute(name)返回的是IVariant接口，例如<br>
boolean defaultValue = true;<br>
boolean b = node.child("subA").attribute("attrB").booleanValue(defaultValue);</p>
<p>TreeNode本身也是IVariant接口的一个实现,例如<br>
int i = ode.intValue();</p>
通过使用IVariant接口，我们实现了强类型的java语言与弱类型的xml文本之间的自然转换，在转换过程中还可以指定缺省值，这些都极大的简化了实际应用时的编码量。<img src ="http://www.blogjava.net/canonical/aggbug/20540.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2005-11-19 10:59 <a href="http://www.blogjava.net/canonical/archive/2005/11/19/20540.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]Query:结构化查询</title><link>http://www.blogjava.net/canonical/archive/2005/11/19/20539.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Sat, 19 Nov 2005 02:58:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2005/11/19/20539.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/20539.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2005/11/19/20539.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/20539.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/20539.html</trackback:ping><description><![CDATA[&nbsp;&nbsp; CRUD(Create, Read,Update, Delete)操作中最难处理的是查询。因为查询总是多样化的，如果每个特定查询调用都编制一个对象方法，则维护量太大且扩展性很差。如果编制一个通用的
查询接口，一般的做法是直接以SQL文本作为参数，但这样就几乎丧失了封装的意义。这里的核心问题是Query本身是复杂的，我们应该将它对象化为一个
类，在程序中控制Query的结构，而一个文本对象与一个复杂的Java结构对象的差异就在于对于文本对象我们很难有什么假定，因而在程序中也很难编制通
用的程序对其进行处理，一般只能对它进行传递。实际上，文本中描述的结构存在于java程序之外!当然，我们可以利用Parser来重新发现这种结构，那
最容易使用的Parser就是xml parser了，所以我们应该将Query的结构建立在xml描述的基础上。<br>
edu.thu.search.Query类直接体现了对主题域的通用查询条件。(对比我对数据仓库模型的描述)<br>
class Query{<br>
&nbsp;&nbsp;&nbsp; List getFields();<br>
&nbsp;TreeNode getCondition();<br>
}<br>
查
询条件主要通过TreeNode进行显式建模，使得程序有可能对它进行进一步的处理。例如，在DataSource处理Query之前，权限配置模块可以
将附加约束直接追加到现有查询条件之后，实现对数据权限的行级控制。因为把Fields明确分离出来，我们也可以做到对权限的列级控制。<br>
Query类的使用示例如下:<br>
Query.begin().fields(TEST_FIELDS)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .condition().eq(ID,"3")<br>
&nbsp;&nbsp; .end().resultType(IQueriable.TYPE_ROW_MAP)<br>
&nbsp;&nbsp; .findOne(dataSource).mapValue();<br>
这里的调用接口的设计基本遵循与SQL类相同的风格，只是面向主题域而不是直接针对SQL语言的封装。<img src ="http://www.blogjava.net/canonical/aggbug/20539.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2005-11-19 10:58 <a href="http://www.blogjava.net/canonical/archive/2005/11/19/20539.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]关于session的使用</title><link>http://www.blogjava.net/canonical/archive/2005/11/16/20144.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Wed, 16 Nov 2005 12:07:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2005/11/16/20144.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/20144.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2005/11/16/20144.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/20144.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/20144.html</trackback:ping><description><![CDATA[有人认为jsplet中使用session是个缺点，关于这一点，我想起一件以前听来的事情。我们都知道Linux的内核是常驻内存，不换页的（不知道最
新的内核是否已经有所改变），Torvalds认为内核换页对系统性能有巨大影响，是愚蠢的想法，所以Linux内核不能换页。据陈榕说，NT内核是可换
页的，而微软内部有一个小组，专门编写工具，对已经编译好的操作系统二机制代码进行优化，调整，最终结果是NT内核可以换页，但几乎不换页，这才是微软可
怕的技术实力。<br>
对于简单的应用，session可以随意使用，而对那些性能要求极高的应用，每一个系统架构师都会如履薄冰，简单的依靠全局Cache不是真正的解决方
案，在每一个细节上我们所需要的是更多的控制权而不是更多的限制。jsplet通过objectScope可以对session进行有效的使用，这是它的
优点而不是缺点。在一个有效的框架下，才能进行真正有序的控制。只有在对系统拥有更多假设的情况下，才能把控制施加在关键点上。<br>
<br>
顺便提一下，现在有些人一提到性能，就对jsp直摇头，而对第三方产品却热情拥抱。目前在java社区内普遍存在着一种对官方标准的漠视或者反感，不知道这是怎么回事。<img src ="http://www.blogjava.net/canonical/aggbug/20144.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2005-11-16 20:07 <a href="http://www.blogjava.net/canonical/archive/2005/11/16/20144.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]tpl:将tag进行到底</title><link>http://www.blogjava.net/canonical/archive/2005/11/16/20140.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Wed, 16 Nov 2005 11:23:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2005/11/16/20140.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/20140.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2005/11/16/20140.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/20140.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/20140.html</trackback:ping><description><![CDATA[<font color="#000000" size="3">tpl是witrix开发平台中的动态xml标签技术，其基本特点表现为如下三个方面:<br>1. 执行能力<br>&nbsp;xml本身只是对数据的描述，而没有循环和判断的能力。<br>&nbsp;在tpl中&lt;c:forEach&gt;和&lt;c:if&gt;标签可以完成程序语言的执行功能，并定义了&lt;c:tile&gt;, &lt;c:iif&gt;等更方便的标签。</font>
<p><font color="#000000" size="3">2. 抽象能力<br>&nbsp;&nbsp; 获得抽象能力，首要的一点是使得创建新概念的成本极低。很少有人大量开发jsp tag，原因就是开发tag过于麻烦，而且调整不方便。另外开发出的tag如果粒度太小，大量使用的时候是对性能的致命伤害。<br>&nbsp;&nbsp; tpl首先需要经过编译，而不是象jsp tag那样完全动态运行，而且tpl编译出的结果是无状态的，因此解决了性能上的问题。<br>&nbsp;&nbsp; 在tpl中导入外部模板的语法非常简单，可以实现基本的分解<br>&nbsp;&nbsp;&nbsp; &lt;c:include src="xxx.tpl" /&gt;<br>&nbsp;&nbsp; 更重要的是，tpl中定义了完善的库机制。<br>使用库<br>&nbsp;&nbsp;&nbsp; &lt;!-- 导入外部包, 导入时指定名字空间为demo。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注意tpl中并没有完整的名字空间支持，只能作为qName使用<br>&nbsp;&nbsp;&nbsp; --&gt;<br>&nbsp;&nbsp;&nbsp; &lt;c:lib src="demo_lib.xml" namespace="demo" /&gt;</font></p>
<p><font color="#000000" size="3">&nbsp;&lt;!-- 对具有cache:timeout属性的节点应用cache修饰，即该节点的运行内容被缓存 --&gt;<br>&nbsp;&lt;c:decorator type="cache" match="</font><a><font color="#000000" size="3">//@[cache:timeout</font></a><font color="#000000" size="3">]" /&gt;</font></p>
<p><font color="#000000" size="3">&nbsp;&nbsp;&nbsp; &lt;div id="blockA"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;p&gt;通过tpl:tag属性可以设定宿主标签的真实标签名。这种做法可以方便可视化设计：&lt;/p&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;input tpl:tag="demo:文本框" value="in block A"
otherValue="${varInJsp}" onclick="clickOnBtn()"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/div&gt;</font></p>
<p><font color="#000000" size="3">&nbsp;&nbsp;&nbsp; &lt;div id="blockB"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;p&gt;分页表格:&lt;/p&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;img tpl:tag="demo:分页表格" headers="fieldA,fieldB" pager="${pager}" /&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/div&gt;</font></p>
<p><font color="#000000" size="3">&nbsp;&nbsp;&nbsp; &lt;div id="blockC"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;p&gt;bodyTag标签的调用:&lt;/p&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;demo:循环data&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;input type="text" value="${row}" /&gt; &lt;br/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/demo:循环data&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/div&gt;</font></p>
<p><font color="#000000" size="3">&nbsp;&nbsp;&nbsp; &lt;div id="blockD" cache:timeout="1000s" &gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;p&gt;条件标签的调用:&lt;/p&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;demo:当Num足够大&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;p&gt;当 thisObj中的变量 'num' 的值大于3的时候显示这句话 &lt;/p&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/demo:当Num足够大&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/div&gt;</font></p>
<p><font color="#000000" size="3">定义库文件demo_lib.xml<br>&lt;!-- <br>&nbsp;&nbsp;&nbsp;&nbsp; 自定义标签的属性：<br>&nbsp; type: simpleTag, bodyTag 或者 conditionTag, 缺省为simpleTag<br>&nbsp; importVars : 每个自定义标签具有自己的变量空间，需要通过importVars来明确指定从外部变量空间中导入的变量。<br>&nbsp; demandArgs: 调用时必须明确给出这些参数的值<br>&nbsp; otherArgs: 如果指定了该参数，则调用自定义标签时能够使用的参数就必须在demandArgs和otherArgs指定的范围之内。<br>--&gt;<br>&lt;demo&gt;<br>&nbsp;&nbsp;&nbsp; &lt;!-- 一个简单的tag示例， 直接输出变量。这里demandArgs指定调用时必须提供的变量。 --&gt;<br>&nbsp;&lt;文本框 demandArgs="value" otherArgs="otherValue,onclick" type="simpleTag" &gt;<br>&nbsp;&nbsp;&lt;p&gt; -----------------------------------&nbsp; &lt;/p&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;!-- 可以使用调用时提供的其他变量，如otherValue--&gt;<br>&nbsp;&nbsp;&lt;input size="40" type="text" value="${value} * ${otherValue}" onclick="${onclick}"/&gt;<br>&nbsp;&nbsp;&lt;p&gt;&nbsp;-----------------------------------&lt;/p&gt;<br>&nbsp;&lt;/文本框&gt;</font></p>
<p><font color="#000000" size="3">&nbsp;&nbsp;&nbsp; &lt;!-- 一个自动分页表格，要求传入headers(List类型)指定表头，pager(Pager类型)提供数据 --&gt;<br>&nbsp;&lt;分页表格 demandArgs="headers,pager"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;!-- 从外部导入tpl文件 --&gt;<br>&nbsp;&nbsp;&lt;c:include src="flex_table.tpl" /&gt;<br>&nbsp;&lt;/分页表格&gt;</font></p>
<p><font color="#000000" size="3">&nbsp;&lt;!-- 一个bodyTag示例: 循环处理thisObj中的变量data中的每一行。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; importVars从调用环境中自动导入变量而不需要通过调用参数直接指定。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当然如果调用参数中指定的变量与importVars中指定的变量重复，则调用参数会覆盖importVars.<br>&nbsp;&nbsp;&nbsp; --&gt;<br>&nbsp;&lt;循环data importVars="thisObj" type="bodyTag"&gt;<br>&nbsp;&nbsp;&lt;c:forEach var="row" items="${data}"&gt;<br>&nbsp;&nbsp;&nbsp;&lt;!-- tagBody为调用时标签的内容 --&gt;<br>&nbsp;&nbsp;&nbsp;&lt;cp:compile src="${tagBody}" /&gt;<br>&nbsp;&nbsp;&lt;/c:forEach&gt;<br>&nbsp;&lt;/循环data&gt;</font></p>
<p><font color="#000000" size="3">&nbsp;&lt;!-- 一个条件标签的示例.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 条件标签一般不应该输出文本，而是返回一个bool值。仅当返回true的时候，调用时标签的内容才会被运行。<br>&nbsp;--&gt;<br>&nbsp;&lt;当Num足够大 importVars="thisObj" type="conditionTag"&gt;<br>&nbsp;&nbsp;&lt;l:gt name="num" value="3" /&gt;<br>&nbsp;&lt;/当Num足够大&gt;<br>&lt;/demo&gt;</font></p>
<p><font color="#000000" size="3">注
意到bodyTag和conditionTag使得我们可以将循环（容器）逻辑和判断逻辑抽象成有意义的概念，而不再是一些执行的指令。这种抽象能力的应
用非常依赖于xml的自描述特性，我们不需要付出太大的努力，就可以在lib中封装出一个有意义的概念来!
而且tag的参数可以指定，可以缺省，存在多种选择，也对应着多种逻辑模型。<br>通过&lt;c:decorator&gt;的用法可以看到，因为xml明确定义了结构，使得我们可以轻易的实施类似AOP的功能。</font></p>
<p><font color="#000000" size="3">3. 集成能力<br>&nbsp;&nbsp; 首先，tpl完全符合xml规范，与xml世界接轨，可以自由的使用xslt。例如，在<br>&nbsp;&nbsp; 任何标签中都可以指定tpl:xdecorator属性, tpl在编译之前首先会应用指定的xslt文件进行变换，对变换后的结果再进行编译。<br>&nbsp;&nbsp; &lt;anyTagName tpl:xdecorator="some_xslt.xslt"&gt;<br>&nbsp;&nbsp;...<br>&nbsp;&nbsp; &lt;/anyTagName&gt;<br>&nbsp;&nbsp; 其次，tpl可以使用属性标记，即tpl标签不仅仅可以通过标签名来标识，而且可以通过tpl:tag属性来标识。例如:<br>&nbsp;&nbsp; &lt;input tpl:decorator="demo:文本框" /&gt;<br>&nbsp;&nbsp; 与 &lt;demo:文本框 /&gt;等效。<br>&nbsp;&nbsp; 这种做法避免了tpl侵入html模型，使得我们可以利用现有工具对tpl进行所见即所得(WISIWIG)的设计。</font></p>
<p><font color="#000000" size="3">&nbsp;&nbsp; 再次, 在tpl中使用EL(Expression Language)语言，集成java数据模型。</font></p>
<p><font color="#000000" size="3">&nbsp;&nbsp; 第四, 在tpl中可以采用如下方式实现强类型化，完成与java的接口。<br>&nbsp;&nbsp; interface ITest{<br>&nbsp;&nbsp;&nbsp;&nbsp; void test(String name, int value);<br>&nbsp;&nbsp; }</font></p>
<p><font color="#000000" size="3">&nbsp;&nbsp; &lt;o:class name="MyClass" implements="ITest"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;test args="name,value" &gt;<br>&nbsp;&nbsp;&nbsp;...<br>&nbsp;&nbsp;&nbsp; &lt;/test&gt;<br>&nbsp;&nbsp; &lt;/o:class&gt;<br>&nbsp;&nbsp; 这种能力可以在数据源的filter中使用。</font></p>
<font color="#000000" size="3">&nbsp;&nbsp; 第五， tpl可以轻易的集成其它xml技术。例如，tpl集成了ant<br>&nbsp;&nbsp; &lt;ant:run&gt;<br>&nbsp;&nbsp;&lt;echo message="xxx" /&gt;<br>&nbsp;&nbsp; &lt;/ant:run&gt;<br>&nbsp;&nbsp; 因此，我们立刻拥有了ant的数千个功能标签。</font><img src ="http://www.blogjava.net/canonical/aggbug/20140.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2005-11-16 19:23 <a href="http://www.blogjava.net/canonical/archive/2005/11/16/20140.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]jsplet与OO</title><link>http://www.blogjava.net/canonical/archive/2005/11/15/19841.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Tue, 15 Nov 2005 04:34:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2005/11/15/19841.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/19841.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2005/11/15/19841.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/19841.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/19841.html</trackback:ping><description><![CDATA[<font color="#000000" size="3">jsplet中的对象化并不是一种巧妙的trick，而是一种设计上的必然。现在大家言
必称OO，可OO到底意味着什么，除了书本上的话语，你能不能用自己的话描述一下，能否体会到那种必然。OO如果是一个有效的概念，它在软件以外的领域是
否有着对应。按照早期教科书的说法,OO是为了模拟现实世界，这种说法只是反映了设计上的一种困境，一种思想上的贫乏。面向对象最直接的意义在于标示了状
态与行为之间的耦合，此后在程序中可以用一种显示的，一致的方式来操纵这个集合体。在界面上，我们看到一个组件，在模型层，我们看到的还是那个对象，在配
置文件里我们还能清晰的辨别出它来。可在webwork这种面向action的框架中，package看起来像对象，在action层却不见了，当我们需
要同时使用两个action的功能的时候（如同时列出role和user)，以前的action不能用了，只能再写一个。想一想，我们最少需要多少概念，
最少需要做多少工作，才能在软件中建立一个合适的概念框架，怎样才能保持这种框架中的张力。</font><img src ="http://www.blogjava.net/canonical/aggbug/19841.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2005-11-15 12:34 <a href="http://www.blogjava.net/canonical/archive/2005/11/15/19841.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]jsplet与IoC</title><link>http://www.blogjava.net/canonical/archive/2005/11/15/19840.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Tue, 15 Nov 2005 04:34:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2005/11/15/19840.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/19840.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2005/11/15/19840.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/19840.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/19840.html</trackback:ping><description><![CDATA[<p><font color="#000000">关于jsplet中的object生命周期的管理以及使用拉模式，如果套用现在流行的设计术语，那就是涉及到所谓的IoC设计（控制反转) <br>IoC
的Container现在很受追捧,
但真正的IoC设计思想并没有引起大家的重视。也许大多数人使用的都是成品吧，以至于把成品的功能等价于其所依赖的设计原理。Spring等所建立的
IoC更准确的说法是Dependency
Injection，只是IoC的一种体现。其基本思想是一个对象并不控制所有与它相关的部分，而是把控制权交给使用对象的人。这里重要的就是控制流（信
息流）的反转。 <br>对象生命周期的管理也是这样，并不是由一个Manager猜测用户是否使用该对象，而是由用户直接标明他的态度，直接发出指令。 <br>参
考一下桌面应用中的资源控制手段，我们打开一个窗口，与系统进行交互，此时占用资源，关闭窗口，则该窗口以及其子窗口所占用的资源都释放。在jsplet
中对象控制策略类似。当用户从某个功能区退出的时候，即当用户访问其它scope中对象而放弃当前objectScope的时候，开始做资源清理工作。即
用户的行为和意向直接驱动着系统的对象管理层。当然，如果用户一直不发出调用，那么系统只能猜测用户的行为，用户是否已断线或者正在思考？在这种情况下，
如果控制资源，则需要通过AOP给thisObj 加上类似EJB的功能。</font></p>
				  
             <img src ="http://www.blogjava.net/canonical/aggbug/19840.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2005-11-15 12:34 <a href="http://www.blogjava.net/canonical/archive/2005/11/15/19840.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]关于jsplet的一些问答</title><link>http://www.blogjava.net/canonical/archive/2005/11/15/19839.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Tue, 15 Nov 2005 04:33:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2005/11/15/19839.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/19839.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2005/11/15/19839.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/19839.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/19839.html</trackback:ping><description><![CDATA[<table align="center" border="0" cellpadding="3" cellspacing="1" width="90%">
<tbody><tr><td><span class="genmed"><b>引用:</b></span></td></tr><tr><td class="quote">如果在Action Centric的框架，要避免两个访问点，可以这么定义。 <br>view.do?&amp;templateName=a &amp;objectName=/@Demo&amp;objectEvent=test </td></tr></tbody>
</table>
<span class="postbody"><br>这
种做法就是程序自己处理而不是框架支持了。我说过，工作就是那么多，只是框架做什么和程序作什么的分工而已。说jsplet是page为中心也不太准确，
jsplet是以对象为中心，只是指定了希望使用的视图页面而已。view.jsp放在前面只是jsp实现上的一个问题， <br><br></span>
<table align="center" border="0" cellpadding="3" cellspacing="1" width="90%">
<tbody><tr><td><span class="genmed"><b>引用:</b></span></td></tr><tr><td class="quote">Action Centric 确实比较麻烦，必须同时传入角色列表 和 用户列表的 分页信息。 JSPLet对于这个问题是怎么处理的？ </td></tr></tbody>
</table>
<span class="postbody"><p><br>很简单包含两个子页面 <br>list_both.jsp <br>&lt;jsp:include page="role_list.jsp?objectName=/@RoleManager" /&gt; <br>&lt;jsp:include page="user_list.jsp?objectName=/@UserManager"/&gt; <br>在
访问的时候通过指定eventTarget参数即可将事件路由到合适的对象，没有响应事件的对象thisObj里的内容不变，因为前台view显示内容也
不变。注意这里role_list.jsp和RoleManager, UserManager对象都是独立开发的。 </p><p><table align="center" border="0" cellpadding="3" cellspacing="1" width="90%"><tbody><tr><td><span class="genmed"><b>引用:</b></span></td></tr><tr><td class="quote">这个时候，role_list.jsp 和 user_list.jsp里面都有一个 thisObj。而且这两个thisObj的scope都是 sesession ?</td></tr></tbody></table></p><span class="postbody"><p><br>注
意所有的对象模型都需要状态保持机制，所以thisObj确实被保持在session中。在webwork2中如果希望在多个action之间协调，则必
须将某个对象保留在session中，否则就是采用无状态模型，所有的状态数据都持久化在数据库中，每次输出的页面都和某个action产生绑定（一种行
为相关），则根本无法实现上述例子中的分解过程，因为在action模型中状态与行为无法抽象到一起并重用！
当页面显示逻辑比较复杂的情况下，页面本身也有一些临时状态需要保持，MVC并不是意味着所有的状态都是需要持久化到数据库中的关键业务数据。在每个层次
上都可能需要保持状态，MVC只是说某些状态变量更加重要，可以驱动其它层次而已。 <br>另外说thisObj的scope是session也是不
准确的。首先注意到jsplet通过对象化实现了将状态和行为抽象到一起，此后程序就拥有了对这个整体的控制权，在jsplet中存在着对象的生命周期控
制，对象的scope是自定义的，对象的生命周期是session的子部分，而不是整个session生命周期范围内都存在。请注意一下这种控制策略带来
的可扩展性。我们拥有了对象生命周期的控制权，依然可以采用无状态设计，但在需要保持状态的时候，可以做到。而在webwork这样的action模型中
是没有这种选择权的。</p><p><table align="center" border="0" cellpadding="3" cellspacing="1" width="90%"><tbody><tr><td><span class="genmed"><b>引用:</b></span></td></tr><tr><td class="quote">session过度使用是不好的 </td></tr></tbody></table></p><span class="postbody"><p><br>thisObj只是允许使用session,是否使用session可以自行决定，这是一种使能技术，而没有object支持，结果是无法有效的使用。另外，请仔细看清楚，objectScope是一种非常精细的资源使用控制手段。 <br></p><p>另
外不要把设计理念和性能混为一谈。设计体现的是对概念的把握，能够达到合适的抽象，而性能是实际实现过程中的限制。在概念能够支持的情况下，可以采用技术
手段解决性能问题，或者退化到较低的层次，这是一种选择权。而概念无法支持的情况下，就需要各种穿墙打洞的方法来实现。 <br><br>thisObj重要的是概念，如果需要，它可以把状态序列化到cookie或者dotNet那种参数中，这只是个实现问题。<br></p><p><table align="center" border="0" cellpadding="3" cellspacing="1" width="90%"><tbody><tr><td><span class="genmed"><b>引用:</b></span></td></tr><tr><td class="quote"><br>JSPLet Action 必须是 JSP ? </td></tr></tbody></table></p><span class="postbody"><p><br>当然可以是任何java类， JSP Action只是IEventListener接口的一个实现 。在jsplet最初的版本中，action只能写在java文件中。稍后改为可以写在jsp中也可以写在java中<br><br></p></span><p><table align="center" border="0" cellpadding="3" cellspacing="1" width="90%"><tbody><tr><td><span class="genmed"><b>引用:</b></span></td></tr><tr><td class="quote">WebWork的Action本身就是模型对象</td></tr></tbody></table></p><span class="postbody"><p><br>这是WebWork弱的地方，它因为是基于action的，没有对象化，所以只有以action作为模型对象的载体，无法捕获多个action之间的状态相关性。 <br>完全无状态的设计正是因为没有合适载体造成的。而jsplet中thisObj可以看作是对session的局域化，是对session的分解。jsplet中的很多概念在webwork这种面向action的框架中都能找到对应，只是加上了很多限制并且变得模糊了。</p><p><span class="gensmall"><table align="center" border="0" cellpadding="3" cellspacing="1" width="90%"><tbody><tr><td><span class="genmed"><b>引用:</b></span></td></tr><tr><td class="quote">没有model1简易(jstl+javabean) 没有struts的"优雅" 定位模糊. </td></tr></tbody></table><span class="postbody"><br>jsplet
是以非常精炼的方式实现对象化。再说一次，不要把jsplet的定位向那些开源框架上靠。jsplet的开发时间大概与那些开源框架同时进行的。仔细看看
设计中的可扩展性。xwork的所有特性jsplet都可以实现，而且jsplet多提供的部分就是对象化。 <br></span></span></p></span><span class="postbody"></span><span class="gensmall"><br></span></span></span><br>可
以使用
并不意味着必须使用，所有无状态设计都可以一样应用。jsplet是一种事件驱动设计，在这一点上，更像是Tapestry或者JSF。基于
actioin的设计是真正的无能使用session，它不敢应用session的原因是它对session没有控制力。而在jsplet中对
session的使用控制是很自然的：当你需要对象的时候，当你需要这个状态的时候，它才存在。它出现是因为需要它存在。在面向action的框架中，你
仍然需要解决状态问题。只是框架无法提供支撑，自己解决而已。 <br>我想，大概大多数人开发的程序都是CURD的堆砌，所以很难理解一个复杂的应用中的状态管理的必要性。有了对象支持之后，才构成整个框架的概念上的完备性。 </span><img src ="http://www.blogjava.net/canonical/aggbug/19839.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2005-11-15 12:33 <a href="http://www.blogjava.net/canonical/archive/2005/11/15/19839.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]jsplet与webwork的概念对比</title><link>http://www.blogjava.net/canonical/archive/2005/11/15/19838.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Tue, 15 Nov 2005 04:32:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2005/11/15/19838.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/19838.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2005/11/15/19838.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/19838.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/19838.html</trackback:ping><description><![CDATA[<font size="3"><font color="#000000"><span class="postbody">jsp本身提供的是一个有限状态机模型(FSM)，Web访问模型直接体现了这一点: action?XXXX。 <br>action对应于方法名，XXX是方法的参数。在这个访问模型中没有指出状态存储在什么地方，因为它假设后台是一个整体，构成一个巨大的状态集。 <br>但
这种模型注定是过分简化的，所以会有很多的发展。发展的方向就是逐渐精细化，识别出相关的部分，把它们组织到一起。其实可以从各个框架的开发过程来看出这
种演化的过程。
Struts最早只有一个全局配置文件，现在多了一个模块的概念。WebWork是在Struts之后设计的，提供了一个所谓的package的概念，将
一堆action和interceptor组织到一起，其设计中package的extends属性看上去是不是有点眼熟。概念多了就要分模块，这一点在
面向对象之前就存在了，也符合Struts的发展历程，只是WebWork的这个extends不再是简单的模块概念了，而是一种面向对象的设计，只是
WebWork中没有实现型与名的分离，每个action名对应唯一的一个action，所以package也可以看作是一种完全静态的对象,只有一个实
例，不是吗? 我们可以做一个对应，包的namespace大概可以对应于Jsplet中的objectScope,
包名大概可以对应于Jsplet中的objectType, action对应于objectEvent,
差别在于objectScope是完全动态的，并参与Web对象管理，而package的namespace被创造出来之后只起了一个名字区别作用，
Webwork的后续发展会不会在这一点上再做些文章? <br>再看另外一个地方。前台页面显示需要从模型中拿到数据，那模型对象是怎么管理的，
Jsp本身提供了几个管理策略application, session, request, page,
几个action需要共享状态信息怎么办？状态与行为的相关就是对象化了。Webwork2没有提供对象化的手段，不知道一般人是怎么做的，将所有相关操
作都塞在一个Action里，然后通过一个扩展参数映射? 还是都从session中存取模型对象?
session中的对象是不是越来越多，有没有人来管一管? <br><br>jsplet的核心是objectManager, 它利用objectFactory来创建对象，利用objectName来管理WebObject，这是与网络无关的, 这里管理的对象也不一定需要响应Web事件。 <br>对
象如果需要响应事件, 实现IEventListener接口，在缺省实现中,
Jsplet用了EventManager来管理objectEvent的响应，大致相当于xwork的工作，只是EventManager是个帮助对
象，由WebObject自己决定是否使用，而且它是每个WebObject自己使用自己的EventManager,
而不是系统全局只有唯一的一个EventManager。 <br><br>整个objectManager层面都是网络无关的，当然可以单元测试。
WebEngine最终实现objectManager与web环境的关联，只是它使用了拉模式。特别是在视图jsp中调用WebEngine,
其最重要的作用是将thisObj这个变量注入到jsp模型中。this指针其实体现了对象化的很重要的特点:使用局部名而不是全局名称。 <br>其实XWork本身也是可以脱离Web环境应用的，特别是它可以脱离View来使用，这是它的扩展性的一个来源。 <br><br>在Webwork
中有一种叫做Model Driven的概念，使用Model
Driven之后在OGNL表达中就可以直接使用model的属性和方法。在jsplet使用我们自己的tpl模板引擎,
其中token解析策略是thisObj的属性和方法可以直接使用，也可以通过thisObj.xx来访问，这就如同this指针的用法。 <br><br><br>再次声明，我无意将jsplet与其它框架在实际使用效果上作对比，所分析的只是Framework整体的概念模型。数据绑定，参数和状态校验等与应用相关的功能在我们的框架中都是有着完整的解决方案的，目前不打算讨论这些。</span></font></font><img src ="http://www.blogjava.net/canonical/aggbug/19838.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2005-11-15 12:32 <a href="http://www.blogjava.net/canonical/archive/2005/11/15/19838.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[导入]jsplet:对Model 2模式的批判</title><link>http://www.blogjava.net/canonical/archive/2005/11/15/19837.html</link><dc:creator>canonical</dc:creator><author>canonical</author><pubDate>Tue, 15 Nov 2005 04:31:00 GMT</pubDate><guid>http://www.blogjava.net/canonical/archive/2005/11/15/19837.html</guid><wfw:comment>http://www.blogjava.net/canonical/comments/19837.html</wfw:comment><comments>http://www.blogjava.net/canonical/archive/2005/11/15/19837.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/canonical/comments/commentRss/19837.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/canonical/services/trackbacks/19837.html</trackback:ping><description><![CDATA[<p><font size="3">&nbsp;&nbsp; 在Jsp Model 2模型中, 用户的所有请求提交给Controller Servlet,
由Controller进行统一分配, 并且采用推的方式将不同的UI显示给用户。
这种推方式在很多人看来是一种优点，因为在Struts等MVC实现中具体推送的UI可以在配置文件中配置，配置完成后还可以通过一些可视化分析工具得到
整个站点地图。在Model2模式中基本的访问格式为:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color="#000000"><strong>action.do?其他参数</strong></font>&nbsp;&nbsp; </font></p>
<p><font size="3">我
本人从未应用过Model2模式，但与我们的jsplet框架对比，我认为这种推送方式在大多数情况下并不是什么优点。如果将一次web访问看作是一次函
数调用，则按照Model2模式，这个函数的返回情况是不确定的，需要由一个额外的配置文件来确定。而我们知道，一个返回情况不确定的函数一般不是什么良
好的设计。在我们的框架设计中，一个基本的观点是尽量将自由度暴露给实际控制它的人。实际上，在大多数情况下，页面编制人员知道应该使用哪个页面来显示数
据，他们并不需要一个额外的配置文件。Jsplet使用如下的url格式:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>视图jsp?objectName=模型对象名&amp;objectEvent=响应事件名&amp;其他参数<br></strong>举一个具体的例子:<br>&nbsp;&nbsp; </font><a href="http://my.com/demo_view.jsp?objectName=/@Demo&amp;objectEvent=test"><font size="3">http://my.com/demo_view.jsp?objectName=/@Demo&amp;objectEvent=test</font></a></p>
<p><font size="3">demo_view.jsp是指定的显示页面, 其代码如下:<br>[code]<br>&lt;%@ include file = "/engine.jsp" %&gt;<br>&lt;!-- 相当于在jsp模型中增加了一个新的变量thisObj，从而实现jsp页面的对象化 --&gt;<br>&lt;c:out&gt;${thisObj.testVar}&lt;/c:out&gt;<br>[/code]<br>objectName被WebEngine映射到session中的一个对象，在demo_view.jsp中成为thisObj这个变量，这就相当于java语言中的this指针，从而实现了jsp页面的对象化。</font></p>
<p><font size="3">WebEngine还将objectEvent映射到一个Action响应函数并自动调用它，具体的Action代码写在一个独立的java文件或者jsp文件中。<br>DemoAction.jsp<br>[code]<br>&lt;%@ include file = "/jsp_action_begin.jsp" %&gt;<br>&lt;%!<br>&nbsp;&nbsp;&nbsp; //<br>&nbsp;// objectName映射为thisObj, objectEvent=test映射对actTest的调用<br>&nbsp;// 在这里增加一个actXXX函数之后，即可通过objectEvent=XXX来访问，不需要任何配置<br>&nbsp;&nbsp;&nbsp; public Object actTest(){<br>&nbsp;&nbsp;// thisObj中的变量可以在视图中使用<br>&nbsp;&nbsp;thisObj.set("testVar","hello");<br>&nbsp;&nbsp;return success();<br>&nbsp;}</font></p>
<p><font size="3">&nbsp;// 如果存在actBeforeAction函数，则该函数在所有action函数之前调用<br>&nbsp;public Object actBeforeAction(){<br>&nbsp;&nbsp;return success();<br>&nbsp;}</font></p>
<p><font size="3">&nbsp;// 如果存在actAfterAction函数，则该函数在所有action函数之后调用<br>&nbsp;public Object actAfterAction(){<br>&nbsp;&nbsp;return success();<br>&nbsp;}<br>%&gt;<br>&lt;%@ include file="/jsp_action_end.jsp" %&gt;<br>[/code]</font></p>
<p><font size="3">在Jsplet框架中只需要注册对象，而不需要单独注册每个action。<br>register.jsp<br>[code]<br>&lt;%<br>&nbsp;&nbsp;&nbsp; WebEngine.registerType("Demo", new WebActionType("/demo/action/DemoAction.jsp"),pageContext);<br>%&gt;<br>[/code]</font></p>
<p><font size="3">与Jsplet
框架对比，Model2是对action的建模而不是对object的建模，即它相当于将objectName,objectEvent和
view.jsp绑定在一起定义为一个访问点action.do,绑定过程中需要一个配置文件来固化view.jsp和action之间的联系。因此,
Model2并没有完全分离view和model，它隐含假定着objectName只具有一个objectEvent,
并且绑定了一个具体的view(出错页面除外)。<br>例如, 我们需要两个不同的view来显示同一个数据，则在Model2程序中可能需要配置两个独立的访问点，而在我们的框架中只需要使用两个不同的url:<br></font><a href="mailto:a_view.jsp?objectName=/@Demo&amp;objectEvent=test"><font size="3">a_view.jsp?objectName=/@Demo&amp;objectEvent=test</font></a><br><a href="mailto:b_view.jsp?objectName=/@Demo&amp;objectEvent=test"><font size="3">b_view.jsp?objectName=/@Demo&amp;objectEvent=test</font></a><br><font size="3">同样的web程序甚至可以在前台通过XMLHTTP方式来调用而不需要额外配置!</font></p>
<p><font size="3">在Jsplet框架中采用的是对象化的方式而不是Action化的方式，因此存在着多种面向对象的扩展,而所有的扩展都直接体现在url格式的细化上，一切都在阳光下。<br>&nbsp; 在Jsplet中objectName是WebObject的名称，在全系统内唯一，其格式定义为: </font><a href="mailto:objectScope@objectType$objectInstanceId"><font size="3">objectScope@objectType$objectInstanceId</font></a><br><font size="3">1. 对象类型objectType<br>&nbsp; 我们需要注册的是对象类型而不是完整的对象名，一个对象类型可以对应于无数个完整的对象名，例如我们注册了Demo类型的WebObject, 则</font><a href="mailto:objectName=/@Demo"><font size="3">objectName=/@Demo</font></a><font size="3">和</font><a href="mailto:objectName=/left/@Demo"><font size="3">objectName=/left/@Demo</font></a><font size="3">对应的处理文件都是DemoAction.jsp。<br>2. 对象生命周期控制objectScope<br>&nbsp;
objectScope为WebObject所在的域，其格式符合Unix路径命名规范。JSP模型本身支持一些预定义的对象域，包括page,
request, session,
application等。但为了能够反映现实世界中的对象组织结构，对象域必须是允许自定义的。objectScope被组织成一个树形结构，这是一个
基本的控制结构，其控制策略为<br>&nbsp;&nbsp;&nbsp;&nbsp; <strong>同时存在的对象域之间必须存在线性序关系(order) <br></strong>&nbsp;
当系统访问某一对象时，如果该对象所在的对象域不能和现有对象的域处在同一"路径"下(即当对象域之间不能建立父子关系时)，系统就会自动销毁不兼容路径
分支下的所有对象。 这种精细的控制策略保证了系统的可扩展性，因为模型上可以保证始终只有一部分对象被创建。<br>对象转移&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
系统动作&nbsp; <br></font><a><font size="3">/main/@MyObject</font></a><font size="3"> ==&gt; </font><a><font size="3">/main/left/@OtherObject</font></a><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
无 <br></font><a><font size="3">/main/left/@OtherObject</font></a><font size="3"> ==&gt; </font><a><font size="3">/main/@MyObject</font></a><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
无 <br></font><a><font size="3">/main/left/@OtherObject</font></a><font size="3"> ==&gt; </font><a><font size="3">/main/left/@MyObject</font></a><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 无 <br></font><a><font size="3">/main/left/@OtherObject</font></a><font size="3"> ==&gt; </font><a><font size="3">/main/right/@MyObject</font></a><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 自动销毁/main/left子域下的对象，如</font><a><font size="3">/main/left/@OtherObject</font></a><br><font size="3">&nbsp;<br>3. 对象实例标识 objectInstanceId<br>&nbsp;如果在某一对象域中需要包含多个同一类型的对象，可以通过objectInstanceId来加以区分，这样在同一个页面上我们可以使用多个同样类型的对象。</font></p>
<p><font size="3">Jsplet中另外一个扩展是通过事件路由来支持jsp子页面的对象化。例如<br></font><a href="http://my.com/demo_main.jsp?objectName=/@Main&amp;eventTarget=/@Sub&amp;objectEvent=test"><font size="3">http://my.com/demo_main.jsp?objectName=/@Main&amp;eventTarget=/@Sub&amp;objectEvent=test</font></a><br><font size="3">如果指定了eventTarget参数，则objectEvent由eventTarget对应的对象来响应。<br>在jsp文件内部我们可以通过include语法来引入子对象，例如<br>&nbsp;&nbsp; &lt;jsp:include page="</font><a href="mailto:sub_view.jsp?objectName=/@Sub"><font size="3">sub_view.jsp?objectName=/@Sub</font></a><font size="3">" /&gt;<br>（注：我不是非常清楚Tapestry具体是如何实现对象化的，熟悉Tapestry的朋友可以介绍一下）</font></p>
<p><font size="3">在Jsplet中可以通过配置文件来支持对Action的interception, 例如<br>[code]<br>&lt;factory&gt;<br>&lt;listener-filter&nbsp; class="global.LogFilter" /&gt;<br>&lt;post-listener class="global.CommonActions"/&gt;</font></p>
<p><font size="3">&lt;type name="Demo"&gt;<br>&nbsp;&lt;!-- 如果未指定object, 则缺省为WebObject类型 --&gt;<br>&nbsp;&lt;object class="demo.MyWebObject" /&gt;<br>&nbsp;&lt;listener&gt;<br>&nbsp;&nbsp;&lt;filter event="query*|select*" class="demo.LogFilter" /&gt;<br>&nbsp;&nbsp;&lt;url-listener url="/demo/DemoAction.jsp" /&gt;<br>&nbsp;&nbsp;&lt;url-listener url="/demo/DemoAction2.jsp" /&gt;<br>&nbsp;&lt;/listener&gt;<br>&lt;/type&gt;</font></p>
<p><font size="3">&lt;/factory&gt;<br>[/code]<br>在上面这个配置文件中，DemoAction.jsp和DemoAction2.jsp是chain关系，即事件响应的传播模型中，如果event没有被标记为stopPropagation,就会传递到下一个listener。</font></p>
<p><font size="3">综上所述，可以看到在目前多变的需求环境下，Model 2已不是一种非常完善的Web程序模式，一些重要的设计需求在Model 2模式的推方式中很难得到合适的表达。</font></p>
<img src ="http://www.blogjava.net/canonical/aggbug/19837.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/canonical/" target="_blank">canonical</a> 2005-11-15 12:31 <a href="http://www.blogjava.net/canonical/archive/2005/11/15/19837.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>