凌华 的个人资料张凌华照片日志列表更多 ![]() | 帮助 |
|
|
12月6日 Frails诞生,JSF果然应该这样走的曾几何时 , 就觉得JSF应该像EJB3那样使用annotations的方式,而抛弃掉那繁冗的像懒婆娘的裹脚那样臭长的xml配置文件 。 如今,终于也有人意识到了这点 ,并且付诸了实现 。(可惜不是官方付诸实现) 。 那就是Frails 。 你可以在Souceforge找到这个项目 https://sourceforge.net/projects/frails 。目前Frails的最新版本是2.0。 ◆Frails 让开发者使用约定(习惯)和注解(annotations)来省略在 faces-config.xml 文件中 managed-bean 和 navigation-rule的配置。 是不是很令人惊喜! 不需要配置faces-config.xml 轻松实现GET
URL是./xxx.jsp?paraName=Yes 那么paVa的值就是Yes。
当然了,Frails提供了让方法在各个声明周期得到调用的Annotations,不仅仅是预渲染。 从烦琐的验证中解脱
这样就使得在setID的时候根据需要进行验证,而不必再写validate方法。 使用简单、功能强大的JSFHelper
如果使用 JSFHelper,代码是如此的简单:
当然,JSFHelper还有很多其他的功能。如导航功能,能将页面之间的数据最大化独立开,之间的联系只需要规定有什么属性就可以了。(以后会详细介绍:Frails下网站快速开发模式) 还有很多特性,帮助你快速开发JSF。当你真正体验过Frails后,你就会发现JSF一统天下的时代的来临! 11月30日 通货膨胀严重威胁稳定中国社会科学院世界经济与政治研究所所长余永定为英国《金融时报》中文网撰写
(2007年10月9日英国《金融时报》首席经济评论员马丁•沃尔夫发表了题目为《通货膨胀不是对稳定的重大威胁》的评论。作为马丁•沃尔夫专栏的特邀评论员,余永定对他的观点进行了点评。马丁•沃尔夫的文章已译成中文,见FT中文网站。下面是余永定的评论:) 可以说,通货膨胀对中国的经济稳定暂时还没有构成严重威胁。但是,中国政府有足够的理由担心通货膨胀将会造成的严重威胁。 首先,2007年中国经济的增长将超过11%。直到前不久,政府和经济学界普遍接受的看法是,中国的潜在增长率在8%至9%之间。在中国的十一五规划中,经济增长目标被隐含地确定为8%以下。在过去,中国GDP只要保持连续两年10%以上增长,都会因此在四至五个季度以后引发严重的通货膨胀。在目前的经济周期中,中国经济已经保持了连续四年10%以上的增速,且通货膨胀率被控制在3%以下,这已经是一个奇迹了。 或许,中国的劳动生产率在最近几年取得了长足的进步。但是,由于固定资产投资约25%的增速,中国的投资率已经超过45%。加上地方政府主导的遍布全国的城市建设运动,以及在这一过程中对能源和原材料极端浪费,很难令人相信中国的生产率(据全要素生产率和/或资本产出比计算得出)已经大幅攀升到如此高的程度,以致中国的潜在经济增长率已从8%增至11%,因而能够保持目前的增长势头而不导致严重的通货膨胀。诚然,中国的内需低于其潜在的供给,但当我们在讨论需求过剩和通货膨胀时,相对的概念应是总需求(内需加外需),而不仅仅是内需。从这点出发,可以认为,中国存在需求过剩并且过剩的需求正在增加,其结果必然是通胀压力的上升。 第二,近期的食品价格上涨并不能完全归咎于一次性的、孤立的、外部的供给冲击。实际上,从饲料到化肥,所有农产品生产所需的投入要素价格都有所上涨,而这至少可以部分归因于需求拉动。举例来说,由于房地产的发展,中国的耕地面积迅速萎缩,几乎达到了政府规定的保护下限。尽管政府能在一段时间内通过行政手段遏制食品价格上涨,但由于许多导致食品价格上涨的因素不会自行消失,通货膨胀也不会自行消失。 第三,通胀预期在百姓中已经形成。据最近一项由中国央行进行的调查显示,公众认为通货膨胀会进一步恶化。人们已开始调整自己的行为:他们取出自己的存款转而购买股票和房地产,他们要求进一步提高工资和薪金。因此,即使通货膨胀在现阶段对稳定不构成威胁,日益严重的通胀预期却能如此。 第四,近年来,工资和薪金已经达到两位数的增长,并且持续加快。但这种增长趋势并没有反映在统计数字上。因为与正规的收入相比,“灰色收入”在中国也许更为重要。我不知道怎样才能让生产力快速上升以抵消迅速增加的劳动力成本从而不会推高产品价格。 第五,价格扭曲仍然广泛蔓延。中国的能源价格是世界上最低的;税收对采矿和开采活动极为慷慨;几乎不需为污染付费;在许多地方,外国直接投资(FDI)用地的租金非常便宜。在一定程度上,低通货膨胀率是以低效率和资源配置不当为代价实现的。如果政府不放弃进一步价格改革的计划,那么很多重要产品的价格上涨将无法避免,这又可能加剧通胀和通胀预期。 第六,中国的货币供应量在很长一段时间持续快速增长,远高于GDP的增长速度。目前,尽管中国央行采取货币紧缩政策,但M2的增长速度在8月份仍高于18%。银行贷款增速也非常高。也就是说,中国的金融环境状况(financial condition)仍然相当宽松,有利于通货膨胀。 第七,2006年末以来,中国的股票价格已经增加了一倍多,股票市值从2005年不足GDP的一半上升到目前超过GDP总额。财富效应的迹象已无处不在。 总之,目前,通货膨胀在中国恶化的所有必要条件都已具备。令人惊讶的不是中国的通货膨胀会恶化,而是通货膨胀率竟仍如此之低。因此,中国政府必须对通货膨胀予以警觉,将其看作是社会稳定的威胁。 马丁是对的,相比快速增长的潜在和实际产量,中国国内需求的增长依然不足,而不是过热。在2006年,净出口对中国的经济增长的贡献率约为3%至4%。除此以外,中国的国内需求或许无法支持经济9%的增长。在2007年,中国的净出口量将超过3000亿美元。如果中国不能保持这一出口势头,中国经济的增长速度可能会由于产量过剩而显著下降。这意味着,尽管此刻中国经济过热,但过热的经济会很快转化为通货紧缩。这是因为高投资率和高固定资产投资增长率是中国经济增长的特点。所以目前过度投资导致的经济过热能够在将来引发由生产能力过剩造成的通货紧缩。 2002年至2004年,中国经历了一段时期的投资热。其结果是在2005年初,经济显现出过剩的迹象。不过,由于第二轮投资热的快速膨胀,预期的经济放缓没有出现。从某种意义上讲,中国实际上是通过创造更多的生产能力来吸收过剩的产能。问题是,这种做法是不可持续的。中国自2005年以来激增的净出口额也有部分是产能过剩的结果。 在我看来,中国同时面临经济过热和产能过剩:当下的经济过热与未来的产能过剩。但不管怎么说,目前的通货膨胀已经对经济稳定形成巨大威胁。首先,它引发通货膨胀预期,从而在成本推动和需求拉动互动的恶性循环中维持通胀。其次,通货膨胀和资产泡沫将会相互强化,并诱发严重的金融不稳定。我认为,当下中国经济形势最危险的特点就是通货膨胀与资产泡沫之间的共生关系。 马丁在他的文章中没有提到中国的资产泡沫,他或许故意回避讨论这一问题。中国股票的平均市盈率在10月14日达到60。中国的两个证券交易所的资本总额已经在不到两年的时间内增加了一倍多,资本总额与GDP的比率已超过100%。毫无疑问,中国的资产泡沫非常严重。然而,股票市场仍然被不断涌入的流动资本所淹没。 在过去的几年中,流动性主要来自中国央行银行对外汇市场的干预。此举是为了在面对日益庞大的经常帐户及资本帐户盈余时,能够控制人民币升值的速度。为了保持物价稳定、遏制资产泡沫,中国央行已经采取了大规模的对冲措施以吸干过多的流动性。 收回流动性的政策已经给商业银行带来了严重的问题,因为它们必须购买越来越多的低收益央行票据,并且必须将更高比例的现金存入央行。尽管如此,这一行动基本上成功地收回了过多的流动资金,使得基础货币和M2的增长率与央行确定的目标大致相符。 然而,尽管对冲流动性相对成功,但中国的金融系统仍充斥着过多的流动资金。否则的话,资产价格不会飙升;通货膨胀应被驯服,投资增长率应该下降。那么,过多的流动资金从哪来?答案就在于流动性过剩不仅是一个货币供给的问题,也是一个货币需求问题。在特殊情况下,资金需求也可以成为流动性过剩的推动力。即使货币供应量保持不变,流动性过剩也能由货币需求的减少而出现。 两大根本原因导致了货币需求的急剧减少。首先,居民持有储蓄存款的偏好开始减弱。资本市场的发育给普通储蓄者提供了分散其资产的机会,很多人现在已经拥有股票、债券和固定资产。2004年至2006年的股市改革,伴随着外国资本的流入,共同点燃了提升股价热气球的火焰。不论其来源,通过股票市场实现较高回报会鼓励居民把自己的存款从银行移至证交所。股票价格的上升,又会进一步加剧上述“存款搬家”过程。其次,即使其他因素未变,由于利率低于物价上涨速度(实际利息率为负),居民通过增加储蓄存款进行储蓄的意愿也会降低。2006年四季度以来开始恶化的通货膨胀正在为“存款搬家”火上浇油。总之,股票价格的上涨和通货膨胀加剧了家庭储蓄的转移(亦既减少了公众的货币需求)。 资产价格飞涨和通货膨胀恶化肯定是由流动性过剩所引发。在过去相当一段时间里,流动性过剩主要是双顺差导致外汇储备急剧增加(这又是因为央行必须不断干预外汇市场以维持人民币汇率稳定),而央行又未能充分冲销的结果。但在最近一段时间,流动性过剩的罪魁祸首是大幅下降的货币需求。在此种情况下,即使中国央行完全对冲了由外汇储备增加产生的过剩流动性,并将M0和M2的增长调整到历史最佳状态,货币供应量仍将超过需求量,流动性过剩仍然会继续存在。(对方因货币需求减少而产生的流动性过剩,需要采取升息、增加资产供应等政策)。 从历史上看,中国货币供应量的涨幅远远超过国内生产总值的增长,并且储蓄存款量巨大。中国的M2与GDP的比例高达160%以上,或许是全世界最高。就38万亿元存款相对于总额8万亿元的流通股而言,储蓄存款由银行转入证交所推高股价的潜力巨大。不断涌入到股票和房地产市场的投资阻碍了对资产的理性估值。股票价格上涨潜力远远没有耗尽。一方面,盛宴可能会持续很多年,直到最愚蠢的买家购买了最昂贵的最后一张股票,股市才会开始崩盘。另一方面,中国目前的(资金推动型的)股价上涨是一个正反馈的过程。换言之,这是一个高度不稳定的的过程。因此,市场可能随时崩溃。这需要政府迅速采取措施以冷却资本市场,而且越快越好。然而,由于种种限制中国政府还不愿或不能这样做。 股市泡沫、通货膨胀、离谱的房价、投资的过度以及巨额的贸易盈余,这些都是对稳定的巨大威胁。中国的收支平衡正处在风口浪尖。作为最坏的一种可能性,如果股市泡沫破裂,房市崩盘,劳动力成本持续快速上涨,以及全球对中国产品的需求出于各种原因而下降,那么中国的经济将会怎样?分析出所有的可能性并制定出综合的应对策略对经济学家来说确实是很大的挑战。 未经英国《金融时报》书面许可,对于英国《金融时报》拥有版权和/或其他知识产权的任何内容,任何人不得复制、转载、摘编或在非FT中文网(或:英国《金融时报》中文网)所属的服务器上做镜像或以其他任何方式进行使用。已经英国《金融时报》授权使用作品的,应在授权范围内使用。 11月27日 关于jsf 生命周期在目前的规范中,jsf还是很不完善的,这也就导致了为什么jsf还是不能成为目前的主流框架。先不去谈论这些弊端,还是先看看一下jsf具体是如何运作的。
揭开J2EE集群的面纱[转]对于理解J2EE集群技术不错的文章,虽然是Sun的技术人员撰写的,基本观点还算客观,内容深浅恰当,非常适合刚刚接触集群的朋友阅读,故此大胆翻译过来,放在这里和大家共享,错误难免,欢迎指正。 Google Android会成为手机领域的微软Windows吗?Google gPhone手机的传言已经沸沸扬扬好几个月了,然而就像Google其他产品那样出人意料,当Android轰轰烈烈推出的时候,原来并非手机产品,而是手机操作系统。Google对无线互联网市场垂涎已久,这已经是尽人皆知的事情。在公众场合,无论是Google全球CEO艾里克施密特博士,还是在中国媒体面前的李开复博士,都毫不掩饰Google对于无线互联网市场的向往。Android的推出就像Google在无线互联网市场亮出的一把利剑,已经是司马昭之心,路人皆知。 Google Android动了谁的奶酪?是Apple,还是微软?Apple刚刚发布了划时代的iPhone手机,而微软已经在手机操作系统领域耕耘了很多年。大家可能忽略了一个简单的事实:Google全球CEO艾里克施密特是Apple公司的董事会成员,Google Android实际上也避开了和iPhone的竞争关系。 互联网时代奇迹般崛起的Google,已经成为微软的心腹大患。然而不论GoogleOS的谣言传得多么活灵活现,业界多么意淫Google直接挑战微软Windows操作系统,然而Google从来都是按兵不动。Google看得很清楚,桌面操作系统时代快要结束了,现在是掌上操作系统时代登场了,谁能够先一步占领消费者的手掌,谁才是真正的赢家。这一次,Google终于亮剑了,亮出来的绝对是一把无坚不摧的利剑 - Android。 Android对于Google未来的无线互联网战略为什么那样重要?Android比其他手机操作系统有什么更牛的地方? 一、Android是开源的 开源社区对于软件行业的推动力已经没有人可以否认了,纵观整个手机操作系统产品,也只有Android的开源力度是如此之大,之强。 二、Android不单纯只是操作系统 1、经过Google剪裁和调优的Linux Kernel,对于掌上设备的硬件提供了优秀的支持。Google在Linux方面的应用能力不容置疑,Google公司所有的几十万台服务器全部都是自己修改过的Linux操作系统。 2、经过Google修改的Java虚拟机Dalvik,请注意这个虚拟机并不是Sun的Hotspot,而是基于Apache Harmony虚拟机版本进行改良而来,能够提供比Hotspot高得多的执行性能。有了Java虚拟机,大部分Java核心类库都已经可以直接运行。 3、大量立即可用的类库和应用软件,例如浏览器WebKit,数据库SQLite,让你可用轻易开发出来媲美桌面应用复杂度的手机软件。 4、Google已经开发好的大量现成的应用软件,同时可以直接使用Google很多的在线服务。 5、Google提供了基于Eclipse的完整开发环境,模拟器,文档,帮助,示例,当然,还有悬赏1000万美元的花红。 三、围绕Android形成了一个移动手机联盟,主要的手机厂商几乎全部在列,对于已经形成的一个庞大的产业联盟的推动力来说,影响力是非常惊人的。 事实上,通过Android战略,Google已经开始抢占未来互联网领域的制高点。对于我们程序员来说,有几个非常值得关心的问题: 一、Android是用Java来开发应用的 二、Java ME前景如何? 三、我应该现在开始学习Android吗? 无线互联网已经成为未来时代争夺的制高点,Apple iPhone上市,紧接着Google Android一出,你会发现很多传统的无线互联网技术,例如Java ME,WAP,都将成为过眼云烟,而站在未来时代最前沿的是Google和Apple两个身影。 11月26日 Ruby on Rails 和 J2EE:两者能否共存?两个 Web 应用程序框架的比较
文档选项
级别: 初级 Aaron Rustad (arustad@gmail.com), 技术架构师, Anassina, Inc. 2005 年 8 月 11 日 Ruby on Rails 是一个相对较新的 Web 应用程序框架,构建在 Ruby 语言之上。它被宣传为现有企业框架的一个替代,而它的目标,简而言之,就是让生活,至少是 Web 开发方面的生活,变得更轻松。在本文中,Aaron Rustad 将对 Rails 和传统的 J2EE 框架在架构上的一些关键特性进行比较。 Ruby on Rails 是一个 Web 应用程序框架,它的目标是为应用程序开发提供一条易行道。实际上,框架的支持者们声称 Ruby on Rails 开发人员的生产率最多是使用传统 J2EE 框架的 10 倍。(请阅读“Rolling with Ruby on Rails”一文,以获得关于这一声明的更多内容;请参阅 参考资料)。虽然这句话造成了 Rails 和 J2EE 社区相当大的争议,但争论中却很少谈及如何比较 Rails 和 J2EE 架构。本文将使用企业应用程序中常见的开源工具,对 Rails 框架和典型的 J2EE 实现进行比较。 要想找到用一句话描述 Rails 的简单说明,只需查看项目的 主页 即可: Rails 是一个用 Ruby 编写的全栈的(full-stack)、开源的 Web 框架,可以使用它来轻松编写实际的应用程序,所需的代码也要比大多数框架花在处理 XML 上的代码少。 虽然我不能保证框架确实会提供它所承诺的轻松快乐,但是上面这句话确实很好地总结了 Rails 的品质。全栈包括:Web 服务器、处理 HTTP 请求和响应的框架,以及方便地把数据持久存储到关系数据库的框架。Rails 通过消除复杂的 XML 配置文件,使用 Ruby 语言的动态性质,帮助把静态类型语言中常见的许多重复代码减少到最少,努力使开发工作变得更容易。 图 1 比较了 Rails 堆栈和典型的 J2EE Web 堆栈(包括 Tomcat servlet 容器、Struts Web 应用程序框架和 Hibernate 持久性框架)。 可以看到,Rails 堆栈和构成普通的基于 J2EE 的 Web 应用程序的组件之间的基本区别很小。两者都有用来执行应用程序代码的容器;都有帮助分离应用程序的模型、视图和控件的 MVC 框架;以及持久存储数据的机制。
模型-视图-控制器(MVC)是应用时间相当长、应用面相当广的一个设计模式。它源自 Smalltalk;如今,几乎所有的 GUI 框架,包括 Web 和胖客户端,都以该框架为基础。MVC 有三个部分:模型,负责业务逻辑,包括应用程序状态和将在这个状态上执行的动作;视图,用来渲染和向用户呈现模型(在 Web 应用程序中,视图一般渲染为 HTML);控制器,定义应用程序的行为。有关 MVC 模式的详细解释,请参阅 参考资料。 Struts 的 使用 Struts,开发人员需要把特定请求的映射外部化到 XML 配置文件中的 Rails 采用了不同的方式。它没有依赖配置文件把请求映射到某一个动作,而是根据请求的 URL 发现适当的动作。从图 2 可以看到,URL 在 Rails 和 Struts 中,动作用来充当前端控制器和模型之间的桥梁。开发人员提供动作的现实,从而提供特定于应用程序的请求处理。前端控制器负责接受请求,并把请求传递到特定动作。图 3 演示了 Rails 和 Struts 基本的动作层次结构。
Struts 要求开发人员扩展
在 Rails 中,必须扩展 清单 1 和 清单 2 分别演示了典型的 Struts 动作和典型的 Rails 动作 表 1 提供了对两种方法的逻辑流程的比较,并演示了清单 1 和清单 2 的特定行中发生的事情。研究 步骤 框架调用动作 从请求中检索到的 ID 从数据库删除订单记录 重定向到列出剩余订单
持久性框架 用来在应用程序层和数据库之间来回移动数据。Hibernate 和 Rails 的持久性框架可以归类为对象/关系映射(ORM)工具,这意味着它们接受数据的对象视图,并将该视图映射到关系数据库内的表中。使用两种框架的目的都是为了减少与关系数据库有关的开发时间。但是,图 4 演示了两者在设计和配置上的一些根本区别。 图 4. Active Record 和 Hibernate 持久性框架的比较 Hibernate 基于 Data Mapper 模式,在这种模式中,特定的映射器类 清单 3 显示了 Hibernate 映射文件的一个实例。 ... 01 <hibernate-mapping> 02 <class name="models.Order" table="ORDERS" 03 dynamic-update="true" dynamic-insert="false" 04 discriminator-value="null"> 05 06 <id name="id" column="id" type="java.lang.Long" 07 unsaved-value="null"> 08 <generator class="identity"/> 09 </id> 10 11 <set name="items" lazy="false" inverse="false" 12 cascade="none" sort="unsorted"> 13 <key column="id"/> 14 <one-to-many class="models.Item"/> 15 </set> 16 17 <property name="name" type="java.lang.String" 18 update="true" insert="true" 19 access="property" column="name"/> 20 </class> 21 </hibernate-mapping> 01 public class Order {
02 private Set items;
03 private String name;
04 private Long id;
05
06 public Long getId() { return id;}
07
08 public void setId(Long id) { this.id = id;}
09
10 public Set getItems() { return items;}
11
12 public void setItems(Set items) { this.items = items; }
13
14 public String getName() { return name; }
15
16 public void setName(String name) { this.name = name; }
17 }
Wikipedia 中(请参阅 参考资料)简要地把 反射 定义为“程序在运行的时候检查和修改其高级结构的能力”。在那里,还将 元编程 定义为“编写那些能够编写和操作其他其他程序(或它们自己),将其他程序作为自己的数据的程序,或者编写那些完成其他程序在运行时所做的部分工作的程序。” 以下代码将实现反射: 01 obj = "some_string"
02 if obj.respond_to?('length'):
03 puts "obj length = #{obj.length}"
03 end
>> obj length = 5
这个代码将实现元编程: 01 class SomeClass
02 end
03 newMethod = %q{def msg() puts "A message!" end}
04 SomeClass.class_eval(newMethod)
05 aClass = SomeClass.new
06 aClass.msg
>> A message!
Rails 的 ORM 框架叫作 Active Record,它基于同名的设计模式。Martin Fowler 将 Active Record 描述为“包装数据库表或视图中数据行的对象,封装数据库访问,在数据上添加域逻辑”。在 Rails 中,每个域对象都将扩展提供 CRUD 操作的 与 Hibernate 一样,Active Record 不需要映射文件;实际上,使用 Active Record 的开发人员不需要对 getter 或 setter、甚至类的属性进行编码。通过一些漂亮的词汇分析,Active Record 能够判断出, 清单 5 显示了 01 class Order < ActiveRecord::Base 02 has_many :items 03 end 像 清单 5 那样编码的 类方法
虽然 Ruby on Rails 是一个非常新、令人兴奋的框架,并且在 Web 社区中已经引起了人们相当的兴趣,但是它的核心架构仍然遵循在 J2EE 中发现的基本模式。开发把两个框架分开的 Web 应用程序是一种合理的方法。Rails 更喜欢清楚的代码而不是配置文件,而 Ruby 语言的动态性质在运行时生成了大部分管道 代码。大多数 Rails 框架都是作为独立项目创建的,而且应用程序开发能够从一组同类组件受益。相比之下,典型的 J2EE 堆栈倾向于构建在通常独立开发的最好的组件之上,常常用 XML 进行配置并将组件组合在一起。 那么,是否应该考虑对下一个 Web 应用程序使用 Rails 呢?嗯,为什么不呢?它是编写得很好的组件堆栈,它们彼此之间工作得很好,并且基于行业接受的企业模式。Ruby 语言支持快速开发,并通过生产大多数应用程序管道来添加到框架。熟悉 Java 世界中的 MVC 和 ORM 框架的人们在用 Rails 表达自己的思想时没有任何困难。 与 J2EE 一起分发会不会有利于 Rails?绝对不要。J2EE 是一个已经设置好的标准,有许多固定的实现,而且,最重要的是,它是一个经过验证的技术。我建议您下载一份 Rails 的副本,并开始自己钻研它。许多可用的教程都是介绍性的,这些教程可以让您立即开始使用 Rails。再次声明,我并不能保证您会通过使用 Rails 得到快乐,但是我敢打赌您会感到满意。 使用 Ruby on Rails 快速开发 Web 应用程序基于 Ruby 的框架用于快速开发,使用的是模型-视图-控制器模式
文档选项
级别: 初级 David Mertz, Ph.D. (mertz@gnosis.cx), 开发人员, Gnosis Software 2005 年 7 月 04 日 虽然还是测试版本,但 Ruby on Rails 已经成为进行 Web 应用程序开发的一个新途径。Rails 的成功之处在于能够自动化大部分常见类型的 Web 应用程序的创建,而且在您希望添加定制或者有特殊要求时,并不会受到限制。不仅如此,同那些只能完成 Web 应用程序单方面需求的自由软件(Free Software)库相比,Rails 包含了非常完整的一套工具。 Ruby on Rails 正在令整个 Web 开发领域受到震憾。让我们首先了解底层的技术:
作为一个完整的框架,这意味着 Rails 中的所有的层都是为协同工作而构造的,所以您不必自己再重复,可以完全只使用一门单一的语言。在 Rails 中,所有内容(从模板到控制流再到业务逻辑)都是用 Ruby 编写的。Rails 支持基于配置文件和注释的反射(reflection)和运行时扩展。 本文详细介绍了 Rails 的组成部分,并介绍了它的工作原理。 关于 Rails,首先需要理解的是它的模型/视图/控制器(model/view/controller,MVC)架构。虽然这种技术不是 Rails 所特有的 —— 甚至不是 Web 应用程序所特有的(相对于其他程序),但是 Rails 具有非常清晰而专一的 MVC 思维方式。如果您并不使用 MVC 方法,那么 Rails 的用处将大为降低(与遵循其模式的情况相比)。 Rails 应用程序的模型部分主要是它所使用的底层数据库。实际上,在很多情形中 Rails 应用程序正是以一种受管理的方式对关系型数据库管理系统(RDBMS)中的数据执行操作的一个途径。
如果您愿意,您可以添加 Ruby 代码来在应用程序模型中执行额外的验证,加强数据关联,或者触发其他操作。应用程序的 app/models/ 目录中的 Ruby 文件能够调用 清单 1. 骨架模型 app/models/contact.rb class Contact < ActiveRecord::Base end 控制器以其抽象形式执行应用程序的逻辑。也就是说,应用程序的 app/controllers/ 目录中的 Ruby 脚本能把模型数据导入为变量,保存回去,或对其进行修改和处理。不过,控制器不关心用户如何适当地显示或者输入数据。在通常的 MVC 模型中,这可以让用户能够以多种方式与同一控制器进行交互:本地 GUI, Web 界面,以及视力较弱的人使用的语音界面都可以与相同的控制器进行交互。 不过,Rails 不像那样非常通用;相反,它仅局限于在 Web 页中提供和收集数据。虽然如此,但是您可以修改那些 Web 页的布局 —— 颜色、字体、表格、样式表单,等等 —— 与控制器代码无关。 Rails 视图是我们编写 Ruby 代码的地方。Rails 包含有一门用于 .rhtml 的非常好的模板语言,它将纯粹的 HTML 与嵌入的 Ruby 代码组合起来。 Rails 应用程序界面的最表层外观通常是由 CSS 样式表单控制的。.rhtml 格式是一种增强的 HTML。实际上,一个简单的 HTML 文件本身也是一个合法的 RHTML 模板,不过,不应该忽略 RHTML 为您提供的脚本控制。 RHTML 是真正的模板格式 —— 不仅是在 HTML 中嵌入代码的方式 —— 这是一种更为有效的方法。如果您熟悉 PHP,那么可以考虑 PHP 本身与 Smarty 模板之间的对照。也就是说,嵌入的脚本只是将代码与未被解释的 HTML 混合在一起;当需要向客户机输出某些内容时,代码部分仍要负责执行 与之不同的是,模板引擎向 HTML 添加了一组定制的标签,让您能够将条件、循环以及其他逻辑作为增强的 HTML 标记的一部分来表示。
Rails 所提供的工具主要是一组代码生成器。相对于那些强迫我使用严格的工作空间和 IDE 的开发环境,我更喜欢这种方法。 Rails 不会妨碍您,但是却会为您省去大部分手工编程的工作 —— 或者,通过提供“可自由获得的”初步(first-pass)支架(scaffolding),至少帮助您轻松将需要手工编码的工作分为多个部分。 支架 概念是 Rails 中的核心概念。非常简单的应用程序可能完全不用编码,让 Rails 在运行时动态地生成客户机 HTML 页面。第一遍生成代码时创建的只是粗略的支架;接下来您可以生成更详细的能够定制的控制器、视图和模型。不过在开始时不需要生成太多。 Rails 对其文件的组织是固定的而且非常普通的,不过这种组织相对严格。如果您试图强行使用其他文件和代码组织方式,那么您可能得付出努力去修改 Rails 环境。再者说,我找不到不使用 Rails 所提供的组织方式的理由;在大部分情况下,它“fits your brain”(Ruby 的支持者喜欢这样讲)。例如,如果您从头开始设计一个框架(至少如果您以“Ruby 方式”思考),那么这些目录名称及其组织可能与您的选择非常接近。
在 Ruby on Rails Web 站点上有一些教程,可以完整地引导您创建一个简单的 Rails 应用程序(见 参考资料)。这里的示例程序与之类似,因为正确开始构建 Rails 应用程序的方式是确定的。由于此介绍的长度相对较短,所以我 极力 推荐那些较长的教程中的一篇,以使得您能够打好更为全面的基础。 示例应用程序是一个基本的通讯录。它演示了创建应用程序的一般步骤:
我们将详细研究每一个步骤。 对于任何应用程序,您需要做的第一件事情是为它创建一个存放数据的数据库。技术上这个步骤不必最先进行,不过需要在早期完成;应该在编写任何应用程序代码(甚至是自动生成的代码)之前创建数据库,这应该是显然的。所以,让我们在 MySQL 数据库中创建一个数据库,并在此数据库中创建第一张表。(阅读其他文档以了解如何安装运行 MySQL 或其他 RDBMS。) 我们假定 MySQL 已经安装并且可用。 [~/Sites]$ cat AddressBook.sql CREATE DATABASE IF NOT EXISTS AddressBook; USE AddressBook; CREATE TABLE IF NOT EXISTS contacts ( id smallint(5) unsigned NOT NULL auto_increment, name varchar(30) NOT NULL default '', created_on timestamp(14) NOT NULL, updated_on timestamp(14) NOT NULL, PRIMARY KEY (id), UNIQUE KEY name_key (name) ) TYPE=MyISAM COMMENT='List of Contacts'; [~/Sites]$ cat AddressBook.sql | mysql 在这第一张表中有些地方需要注意。最重要的是每一张表都必须拥有一个 另一个稍微古怪的方面是,Rails 为不同的内容使用单数和复数的名称。根据上下文,各种条目会被重命名为单数或复数形式。表的名称应该使用复数格式。我没有使用不规则复数单词的经验; 既然已经拥有了一个能够交互的数据库,就可以创建 [~/Sites]$ rails AddressBook create create app/apis create app/controllers create app/helpers create app/models create app/views/layouts create config/environments create components [...] create public/images create public/javascripts create public/stylesheets create script [...] create README create script/generate create script/server [...] 我删减了运行 创建了 AddressBook/ 目录和所需要的子目录后,您需要执行一次惟一的初始配置。首先,通过修改 YAML 配置文件来设置数据库,如下: [~/Sites]$ cd AddressBook [~/Sites/AddressBook]$ head -6 config/database.yml # after editing development: adapter: mysql database: AddressBook host: localhost username: some_user password: password_if_needed 最后,您需要提供数据。Rails 附带了它自己的单一功能的 Web 服务器,即 WEBrick,非常适用于我们的试验。您可能也会遵循 Ruby on Rails Web 站点上的说明来配置 Apache 或者其他服务器,以通过 FCGI(或者普通的 CGI,但是普通的 CGI 将会较慢)向 Rails 应用程序提供服务。 [~/Sites/AddressBook]$ ruby script/server -d => Rails application started on http://0.0.0.0:3000 [2005-03-21 17:57:38] INFO WEBrick 1.3.1 [2005-03-21 17:57:38] INFO ruby 1.8.2 (2004-12-25) [powerpc-darwin7.8.0] 要在 WEBrick 端口上看到一个欢迎页面,先前的步骤就足够了。例如,在我的本地系统中,现在可以访问 [~/Sites/AddressBook]$ ruby script/generate model contact
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/contact.rb
create test/unit/contact_test.rb
create test/fixtures/contacts.yml
[~/Sites/AddressBook]$ ruby script/generate controller contact
exists app/controllers/
exists app/helpers/
create app/views/contact
exists test/functional/
create app/controllers/contact_controller.rb
create test/functional/contact_controller_test.rb
create app/helpers/contact_helper.rb
注意,在相应的表名中,这里应该使用单数的 现在需要编辑一个或多个生成的文件(只需稍加编辑)来让控制器去使用支架: [~/Sites/AddressBook]$ cat app/controllers/contact_controller.rb class ContactController < ApplicationController model :contact scaffold :contact end 现在可以通过类似于
前面的代码创建了一个查看和修改数据库的功能完全的界面,不过,所有格式化、显示以及业务逻辑(比如本来就有的)都由 Rails 动态完成,没有任何重大修改。为了创建一些更为定制的内容,需要生成更多一些代码。现在我们所需要的是让 Rails 显式地写出它在运行时隐式地生成的所有支架,以使得我们能够修改它。 [~/Sites/AddressBook]$ ruby script/generate scaffold Contact
dependency model
[...]
create app/views/contacts
exists test/functional/
create app/controllers/contacts_controller.rb
create test/functional/contacts_controller_test.rb
create app/helpers/contacts_helper.rb
create app/views/layouts/contacts.rhtml
create public/stylesheets/scaffold.css
create app/views/contacts/list.rhtml
create app/views/contacts/show.rhtml
create app/views/contacts/new.rhtml
create app/views/contacts/edit.rhtml
现在有了更多一些要做的,所以尝试去修改一些内容。(注意此代码已经重新使用了复数格式 [~/Sites/AddressBook]$ head -8 public/stylesheets/scaffold.css
body { background-color: #ffe; color: #338; }
body, p, ol, ul, td {
font-family: verdana, arial, helvetica, sans-serif;
font-size: 13px;
}
td { border: 1px solid; }
a { color: #eef; background-color: #446; }
a:hover { color: #fff; background-color:#000; }
您已经拥有了这段代码,那么 清单 10. 控制器 app/controllers/contacts_controller.rb class ContactsController < ApplicationController
def list
@contacts = Contact.find_all
end
def show
@contact = Contact.find(@params['id'])
end
def create
@contact = Contact.new(@params['contact'])
if @contact.save
flash['notice'] = 'Contact was successfully created.'
redirect_to :action => 'list'
else
render_action 'new'
end
end
如前所述,控制器的主要任务是将数据导入到变量之中。对象 此示例中的控制器最终连接到了视图,即 RHTML 文件,它们使用的是控制器导入到变量中的数据值。例如,这里是 清单 11. 列出视图 app/views/contacts/list.rhtml [...]
<% for contact in @contacts %>
<tr>
<% for column in Contact.content_columns %>
<td><%=h contact.send(column.name) %></td>
<% end %>
<td><%= link_to 'Show', :action => 'show', :id => contact.id %></td>
<td><%= link_to 'Edit', :action => 'edit', :id => contact.id %></td>
<td><%= link_to 'Destroy', :action => 'destroy', :id => contact.id %></td>
</tr>
<% end %>
[...]
方法
初始的模型只包含联系人的名字。不幸的是,本文中我已经没有余地扩展这个模型以使其包含实际的联系人数据,比如电话号码、地址、电子邮件等等。通常,那些数据应该存放在一张子表中,子表的外部关键字关联到表 清单 12. 定制代码 app\models\phone.rb class Phone < ActiveRecord::Base belongs_to :contact end 在结束之前,让我们来对数据模型稍加修改,以查看它如何影响应用程序。首先,添加一列: $ cat add-contact-date.sql USE AddressBook; ALTER TABLE contacts ADD first_met date; $ cat add-contact-date.sql | mysql 既然已经修改了底层的模型,
清单 14. 编辑视图 app/views/contacts/edit.rhtml <h1>Editing contact</h1> <%= error_messages_for 'contact' %> <%= start_form_tag :action => 'update' %> <%= hidden_field 'contact', 'id' %> <p><label for="contact_name">Name</label><br/> <%= text_field 'contact', 'name' %></p> <p><label for="first_met">Known Since</label><br/> <%= date_select "contact", "first_met", :use_month_numbers => false %></p> <input type="submit" value="Update" /> <%= end_form_tag %> <%= link_to 'Show', :action => 'show', :id => @contact.id %> | <%= link_to 'Back', :action => 'list' %> 那么您手工修改的应用程序看起来如何了呢?与默认的区别不太大,不过在图 3 和 4 中可以看到修改已经生效了:
Rails 为您提供了开发灵活的 Web 应用程序的一种极其快速的途径;本篇介绍只是肤浅地涉及了如何使用 Rails。完整的框架包含很多实用的类和方法,能够完成基于 Web 的应用程序使用最多的操作。 Rails 的最大价值在于它孕育了一个成体系的“Rails 思维方式”,因为您所需要的所有支持代码令它变得完整。相对于只是给出要使用的原始材料的其他工具包和框架而言,这是一个巨大的优势。Rails 开发为您提供了将半成形(half-formed)的想法实现为功能完全的 Web 应用程序的一条坦途。 11月25日 IT业结构失衡 软件外包人才奇缺来源:凤凰财经 日期: 2007-11-20 目前,我国的互联网发展似乎进入了一个发展的平稳期。据了解,it业急需的人才主要集中在项目管理和研发方面的中、高级人才。不过职位虽然诱人,入行的门槛却不低,基本的"硬件"条件是:科班出身,至少具备5年以上相关工作经验。 有关专家指出,我国的it队伍存在严重的结构失衡,既缺乏高级it人才,更缺乏技能型、应用型信息技术人才,和一大批能从事基础性工作的技术人员。目前,软件开发主要分两类,一类是通用软件开发,另一类是软件定制服务。未来国内it企业将需要大量的通用软件开发人才。同时,针对各行各业的软件定制服务,也将对软件人才提出更高的要求,当然最好是该行业的从业人员能够进入it业设计适合行业特征的软件。 此外,对日软件外包人才也成为时下的紧缺人才。据悉,在过去的一年,软件外包进入到一个疯狂扩张的阶段,掌握日语的软件项目经理更是奇缺。虽然我国对日软件外包每年以50%以上的速度增长,但目前在中国进行的软件开发只占日本业务总量的1%以上,不到2%。现在对日软件外包企业之间的竞争很少,但真正的竞争就是在人才方面,人才能不能到你的企业中来是决定企业发展的核心因素。 日前,专业人才相关机构发布了最新一期的it职场人气排行榜。据了解,"十大it职场人气企业"选自求职者投递简历最多的企业;而"十大it职场人气职位"则是统计自企业发布需求最多的it职位,透过榜单看职场,对于准备找工作、跳槽的人来说,这就是一个最及时的职场风向标。 十大it职场人气企业 1、大唐电信科技产业集团 2、中国惠普有限公司 3、京信通信系统(广州)有限公司 4、方正集团 5、三星(中国)投资有限公司 6、中信大东宽带网络技术有限公司 7、英顺达科技有限公司 8、亚信科技(中国)有限公司 9、中国长城计算机深圳股份有限公司 10、朝华科技有限责任公司 十大it职场人气职位 1、区域销售经理 2、软件工程师 3、nc销售工程师(兼职) 4、夜间数据处理文员(兼职) 5、executivesecretary 6、高级技术支持 7、测试人员 8、嵌入式软件工程师 9、issc-webtechnologydeveloper 10、对日软件开发初级程序员 11月24日 Java集合对象的比较Java 集合对象的区分和比较
11月22日 Java Servlet中HttpSession , HttpSessionListener , HttpSessionBindingListener , HttpSessionAtributeListener的正确理解[转]关于HttpSession的误解实在是太多了,本来是一个很简单的问题,怎会搞的如此的复杂呢?下面说说我的理解吧: 一个session就是一系列某用户和服务器间的通讯。服务器有能力分辨出不同的用户。一个session的建立是从一个用户向服务器发第一个请求开始,而以用户显式结束或session超时为结束。 HttpSession的使用 在请求中有两个重载的方法用来获取HttpSession对象。 获取到HttpSession对象后,我们就需要使用HttpSession的某些方法去设置和更改某些参数了。如: 在javax.servlet.http包里一共定义了四个session监听器接口和与之关联的两个session事件。分别是: 他们的继承关系是: 以下分别详述: HttpSessionBindingListener 我们可以看到前两个接口都对HttpSessionBindingEvent事件做出反应,但机理不同。 HttpSessionListener 也许我们会简单的考虑使用sessionDestroyed方法来在session结束后做一些清理工作。但是,请注意,当这个方法被调用的时候,session已经结束了,你不能从中提取到任何信息了。因此,我们要另辟蹊径。 一种通常采用的方法是使用HttpSessionBindingListener接口。在session创建时增加一个属性,而在session结束前最后一件事将这个属性删除,这样就会触发valueUnbound方法,所有对session的清理工作可以在这个方法中实现。 HttpSessionActivationListener 1、HTTP协议本身是“连接-请求-应答-关闭连接”模式的,是一种无状态协议(HTTP只是一个传输协议); 2、Cookie规范是为了给HTTP增加状态跟踪用的(如果要精确把握,建议仔细阅读一下相关的RFC),但不是唯一的手段; 3、所谓Session,指的是客户端和服务端之间的一段交互过程的状态信息(数据);这个状态如何界定,生命期有多长,这是应用本身的事情; 4、由于B/S计算模型中计算是在服务器端完成的,客户端只有简单的显示逻辑,所以,Session数据对客户端应该是透明的不可理解的并且应该受控于服务端;Session数据要么保存到服务端(HttpSession),要么在客户端和服务端之间传递(Cookie或url rewritting或Hidden input); 5、由于HTTP本身的无状态性,服务端无法知道客户端相继发来的请求是来自一个客户的,所以,当使用服务端HttpSession存储会话数据的时候客户端的每个请求都应该包含一个session的标识(sid, jsessionid 等等)来告诉服务端; 7、客户端Session存储只有一个办法:cookie(url rewritting和hidden input因为无法做到持久化,不算,只能作为交换session id的方式,即a method of session tracking),而服务端做法大致也是一个道理:容器有个session管理器(如tomcat的org.apache.catalina.session包里面的类),提供session的生命周期和持久化管理并提供访问session数据的api; 8、使用服务端还是客户端session存储要看应用的实际情况的。一般来说不要求用户注册登录的公共服务系统(如google)采用cookie做客户端session存储(如google的用户偏好设置),而有用户管理的系统则使用服务端存储。原因很显然:无需用户登录的系统唯一能够标识用户的就是用户的电脑,换一台机器就不知道谁是谁了,服务端session存储根本不管用;而有用户管理的系统则可以通过用户id来管理用户个人数据,从而提供任意复杂的个性化服务; 9、客户端和服务端的session存储在性能、安全性、跨站能力、编程方便性等方面都有一定的区别,而且优劣并非绝对(譬如TheServerSide号称不使用HttpSession,所以性能好,这很显然:一个具有上亿的访问用户的系统,要在服务端数据库中检索出用户的偏好信息显然是低效的,Session管理器不管用什么数据结构和算法都要耗费大量内存和CPU时间;而用cookie,则根本不用检索和维护session数据,服务器可以做成无状态的,当然高效); 10、所谓的“会话cookie”简单的说就是没有明确指明有效期的cookie,仅在浏览器当前进程生命期内有效,可以被后继的Set-Cookie操作清除掉 11月21日 我们的.Net培训进入第一个项目练手阶段了(实验)一周时间完成如下课题:
一,简要: 用Asp.net2.0 开发一套有用户管理的建议博客系统 。 二,要求: 1,使用三层架构 三,功能模块: 前台: 后台: 四,可参考原型 五,页面描述 前台: Default.aspx (首页) 后台: 自行设计页面 六,提示 如何来区分用户? 七,周期 八,指导讲师:林立峰 ,张凌华
实验场景 (场地简陋,不过我们也不计较这个了): (四个角落缩影) 最近一直没有心情上来发点儿文章,太忙了。今日终于有个阶段性的进展最近一直没有发点儿文章,包括我的NDSL L键坏了,也一直没有好好的去理会它 。原因无他。因为最近项目中设计一个消息集群处理中心 。 发现了不少问题 。 在做这个设计的时候,发现JMS & MDB怎么都不能满足我的需求 。 似乎没有一个商业化的可行性架构 。 而且我查遍了网上的文章 , 看了5,6本中英文的书籍的相关部分 。 居然就没有人通过JMS &MDB商业化过。 都是些简单的Send , Receive 。 就好像他们都只是写一个HelloWorld用在自己的项目中似的 。
我真是表示怀疑 。 全国就没有人深入理解何接触这块儿么 ?其实主题很明确,就是在JSF (Web架构)中实现消息集群的调用 , 处理和呈现 。 但是确实有些难度。主要原因还是在于JMS 设计的还不够完善 。 与MSMQ Server差远了 。 不过最后通过我一个多礼拜的不断实验(大概实验了十几个Case),研究了两套方案来解决这样的问题并且可以支持商用 。 将来有时间,我会写出文章分享这些经验 。
天下文章一大抄 , 网上的文章大多都是道听途说,人云亦云 。 从开始做J2EE起,我就写了不少网站没有的原创经验了,也喜欢能为其它程序员的商用之路提供一些帮助!
11月17日 WebSphere Application Server 处理有害消息的方法(转)最近在开发中开发消息中心 。 遇到一些有害消息的处理 。 特此,摘抄一篇文章对有害消息处理的简单描述,也算是共享一些经验了。 虽然是针对WebSphere 的,但对于JBoss思路都是一致的。 WebSphere Application Server V5.x 提供基于 Java™ Messaging Service(JMS)规范的异步消息传递支持。通过使用消息侦听器服务和嵌入式 JMS 服务器或外部消息提供程序(比如 WebSphere MQ),应用程序开发人员可以编写消息驱动 Bean(MDB),它们在 JMS 目的地(一个消息队列,或者一个主题)上侦听,并且在消息到达那个特定的目的地时被调用。如果消息侦听器服务将一个“有害”消息传递给 MDB 应用程序,则该应用程序可以选择拒绝此消息。此时,应用程序服务器怎样处理此消息? 本文假定您具备 JMS 基础知识。
有害消息就是接收该消息的 MDB 应用程序无法处理的消息。该消息可能是已损坏的或是以非预期格式表示的消息。例如,假设您有一个处理 TextMessage 类型 JMS 消息的 MDB。如果消息侦听器服务传递了一个具有不同消息类型的消息,则该消息被认为是有害消息。
如果 MDB 发现一个有害消息,它可以做以下三件事之一:
请牢记 MDB 应用程序的职责是确定它是否收到一个有害消息。JMS 提供程序或消息侦听器服务无法检测一个消息是否已损坏或是否应用程序所期望的 JMS 消息类型。
如果在一个事务中运行,则 MDB 可以回滚有害消息。这将导致消息被返回到它来源的队列。JMS 服务器在这种情况下的缺省行为是关闭与 MDB 相关的侦听器端口,主要为防止可能出现的失控状态。 如果侦听器端口没有被关闭,则消息侦听器服务将检测消息到达的受监控的目的地,并且将再次把消息发送给 MDB。然而,该消息仍然是有害消息,所以 MDB 将第二次回滚该消息。 有害消息第一次被回滚时关闭侦听器端口,这会强制应用程序开发人员仔细考虑应用程序和 JMS 服务器应如何处理它们。
JMS 服务器的确切行为依赖于三个属性:
嵌入式 JMS 服务器首先把消息返回到它源自的目的地。此时,服务器将比较消息 Redelivery count 和为目的地而定义的 Backout threshold 属性值。 如果 Redelivery count 少于 Backout threshold,则把消息留在队列中。然而,如果 Redelivery count 等于 Backout threshold,则把消息移出队列并置于队列 SYSTEM.DEAD.LETTER.QUEUE 中。 何时设置侦听器端口的 Maximum retries 属性?如上所述,该属性指定了在侦听器端口被停止之前消息侦听器服务向 MDB 尝试发送一个消息的次数。该属性的缺省值是 0,这表示当第一次出现某个消息无法发送时,侦听器端口被关闭,并且消息被返回到队列。此时,该消息的 Redelivery count 属性值加 1。 当侦听器端口被重启,它将再次尝试传送消息到 MDB。如果传送失败或消息被再次回滚,则该消息的 Redelivery count 加 1(将该属性设置为 2),然后将关闭侦听器端口。 此行为将持续到消息传送失败或回滚五次,此时该消息被置于 SYSTEM.DEAD.LETTER.QUEUE。这样做的原因是该消息的 Redelivery count 等于队列的 Backout threshold。 如果 Maximum retries 属性值被设置为 6 或更多,则当侦听器端口无法传送一个消息到 MDB 时,它将永远不会自关闭。当侦听器端口第五次发送同一个消息失败时,嵌入式 JMS 服务器将把消息置于 SYSTEM.DEAD.LETTER.QUEUE,而不是消息的原始队列,这是因为 Redelivery count 等于该队列的 Backout threshold 值。
缺省地,WebSphere MQ 创建的队列将 Backout threshold 属性(在 WebSphere MQ 用术语 BOTHRESH 表示)设置为 0。因此,WebSphere MQ 的缺省行为是从不收回有害消息。这意味着什么? 当 MDB 将有害消息回滚时,该消息被返回到它源自的队列。因为该队列未定义 Backout threshold,有害消息将被留在那里。然而,因为它现在存在于队列中,消息侦听器服务会检测到它并重新发送回 MDB。但这是一个有害消息,所以 MDB 将拒绝该消息,导致它被回滚被返回队列! 这一系列事件将持续直到有害消息被回滚的次数等于侦听器端口的 Maximum retries 属性值。此时,侦听器端口将自关闭。防止这种事情发生的方法是将队列的 Backout threshold 属性值设置为大于 0 而小于侦听器端口的 Maximum retries 属性值。这将确保在侦听器关闭之前回滚并删除任何有害消息。 在使用 WebSphere MQ 时,您可以通过修改队列的 Backout Requeue Name 属性(在 WebSphere MQ 术语中是 BOQNAME)来指定退回消息的目的地。缺省未设置该属性,这表示任何已被回滚的次数等于队列 Backout threshold 属性值的消息将丢失。需要培养的好习惯是将此属性值设置为系统的死信(dead letter)队列(缺省死信队列是 SYSTEM.DEAD.LETTER.QUEUE),因为有害消息实际上是“死信”。 可以使用 WebSphere MQ 提供的命令程序来设置 Backout threshold 和 Backout Requeue Name 属性。 图 2 来自 WebSphere MQ Explorer 实用程序,运行于 Windows® 平台。在此,将称为 CSINPUT_QUEUE 的队列的 Backout threshold 属性值设置为 1,并把 Backout Requeue Name 属性设置为 SYSTEM.DEAD.LETTER.QUEUE。当有害消息第一次被 MDB 检测到并回滚,MDB 将把该消息从 CSINPUT_QUEUE 移至队列 SYSTEM.DEAD.LETTER.QUEUE。 当然,您应该周期性地监控死信队列以检查应用程序接收了多少有害消息。如果死信队列包含了大量的消息,您应该调查这些消息出现的原因。 11月15日 Windows Server 2008 将与 Visual Studio 2008 以及 SQL Server 2008 共同发布微软公司刚刚在全球伙伴大会 (Worldwide Partner Conference) 上郑重宣布,Windows Server 2008 将与 Visual Studio 2008 和 SQL Server 2008 于 2008 年 2 月 27 日在洛杉矶共同发布。作为微软公司历史上最重要的企业平台发布,这一发布将为接下来的几百个世界性活动所构成的微软“发布浪潮”正式拉开序幕。了解更多
Windows Server 2008 内置的 Web 和虚拟化技术,可助您增强服务器基础结构的可靠性和灵活性。新的虚拟化工具、Web 资源和增强的安全性可助您节省时间、降低成本,并且向您提供了一个动态而优化的数据中心平台。强大的新工具,如 IIS7、Windows Server Manager 和 Windows PowerShell,能够使您加强对服务器的控制,并可助您简化 Web、配置和管理任务。先进的安全性和可靠性增强功能,如 Network Access Protection 和 Read-Only Domain Controller,可助您加强服务器操作系统安全并保护服务器环境,确保您拥有坚实的业务基础。
产品亮点• 针对 Web 而建 Internet Information Services 7.0 是一个强大的应用程序和 Web 服务平台,可助您简化 Web 服务器管理,这个模块化的平台提供了简化的、基于任务的管理界面,更好的跨站点控制,增强的安全功能,以及集成的 Web 服务运行状态管理。 Internet Information Server (IIS) 7 和 .NET Framework 3.0 提供了一个综合性平台,用于建立连接用户与用户、用户与数据之间的应用程序,以使他们能够可视化、共享和处理信息。 • 虚拟化 Windows Server 2008 的虚拟化技术,可助您在一个服务器上虚拟化多种操作系统,如Windows、Linux 等等。服务器操作系统内置的虚拟化技术和更加简单灵活的授权策略,可助您获得前所未有的易用性优势并降低成本。 Windows Server 2008 可助您灵活地创建敏捷、动态的数据中心,以满足您不断变化的业务需求。 借助Terminal Services Gateway 和 Terminal Services RemoteApp ,您可以轻松进行远程访问并与本地桌面应用程序进行集成,还可实现在无需 VPN 的情况下,安全无缝地部署应用程序。 • 安全性 Windows Server 2008 是迄今为止最可靠的 Windows Server,它加强了操作系统安全性并进行了突破安全创新,包括 Network Access Protection、Federated Rights Management、Read-Only Domain Controller,可为您的网络、数据和业务提供最高水平的安全保护。 Windows Server 2008 可帮助您保护服务器、网络、数据和用户帐户安全,以免发生故障或遭到入侵。 Network Access Protection 能够帮助您隔离不符合组织安全策略的计算机,并提供网络限制、更正和实时符合性检查功能。 Federated Rights Management Services 提供了一个综合性信息保护平台,可对敏感数据提供持续性保护,同时帮助降低风险并保证符合性。 Read-Only Domain Controller 可支持部署 Active Directory Domain Services,同时限制整个 Active Directory 数据库的复制,以便更好地防止服务器的信息泄露或被窃取。 • 坚实的业务基础 Windows Server 2008 是迄今为止最灵活、最稳定的 Windows Server 操作系统,借助其新技术和新功能,如 Server Core、PowerShell、Windows Deployment Services 和加强的网络和群集技术,为您提供了性能最全面、最可靠的 Windows 平台,可以满足您所有的业务负载和应用程序要求。 Server Manager 可以加速服务器的安装和配置,并能通过统一的管理控制台,简化进行中的服务器角色管理。 Windows PowerShell 是一个全新的命令行 Shell, 提供130 多种工具,以及集成的脚本语言,帮助管理员实现例行系统管理任务自动化,尤其是针对跨多个服务器的任务自动化。 Server Core 是一个全新的安装选项,仅包含必要的组件和子系统,而没有图形用户界面,以提供一个具有高可用性,且较少需要进行更新和服务的服务器。 谷歌发布“Android”的软件开发工具包 采用Java编写应用程序【日经BP社报道】 Android的结构 美国谷歌及手机平台促进团体美国开放手机联盟(Open Handset Alliance)(参阅本站报道1,本站报道2)于2007年11月13日发布了OSA推广的软件平台——“Android”的软件开发工具包(SDK)试用版。该工具包可通过OSA的网站下载(Android网站)。谷歌发布的Android SDK可分别在Windows、OS X、Linux系统上使用。综合开发环境利用了开源的“Eclipse”。 冬天到了,换个主题,加点儿温度今日终于感受到冬季的寒冷 。 坐在电脑上敲着赶工的代码 ,感觉手脚都有些不听使唤了 。 可惜我这个小区的供暖日期延后了,因此还要把这3000大洋的供暖费用揣在怀里 ,过几日才能贡献出去 。 给博客换个皮肤吧,暖色调的,以免把耳朵冻掉 ! 11月9日 Web开发就是组件开发 , JSF复合组件开发实例最近一直在研究JSF的应用 , 还买了一本书(JSF in Action 中文名:JSF实战),却发现“原来无耻的还是中国人”这个道理,原因在于几点:
闲话休谈, 下面是我为了研究JSF组件开发在IBM官方网站看到的内容 , 很不错,权且当作那300页的补充吧 。
在下一个示例中,我将介绍如何创建这样一个组件(和标记),它可以记住最后一个人离开的位置。Field 组件把多个组件的工作组合到一个组件中。复合组件是 JSF 组件开发的重点,会节约大量时间! Field 组件把标签、文本输入和消息功能组合到一个组件。Field 的文本输入功能允许用户输入文本。如果有问题(例如输入不正确),它的标签功能会显示红色,还会显示星号(*)表示必需的字段。它的消息功能允许它在必要的时候写出出错消息。 Field 组件示例演示了以下内容:
与 Label 组件不同,Field 组件使用独立渲染器。如果为一个基于 HTML 的应用程序开发组件,那么不要费力使用独立渲染器。这么做是额外的无用功。如果正在开发许多 JSF 组件,打算卖给客户,而针对的客户又不止一个,那么就需要独立的渲染器了。简而言之,渲染器适用于商业框架的开发人员,不适用于开发内部 Web 应用程序的应用程序开发人员。 由于我已经介绍了创建组件、定义渲染器以及创建定制标记的基本步骤,所以这次我让代码自己说话,我只点出几个重要的细节。在清单 5 中,可以看到在典型的应用程序示例中如何使用 Field 标记的:
<f:view>
<h2>CD Form</h2>
<h:form id="cdForm">
<h:inputHidden id="rowIndex" value="#{CDManagerBean.rowIndex}" />
<arcmind:field id="title"
value="#{CDManagerBean.title}"
label="Title:"
errorStyleClass="errorText"
required="true" /> <br />
<arcmind:field id="artist"
value="#{CDManagerBean.artist}"
label="Artist:"
errorStyleClass="errorText"
required="true" /> <br />
<arcmind:field id="price"
value="#{CDManagerBean.price}"
label="CD Price:"
errorStyleClass="errorText"
required="true">
<f:validateDoubleRange maximum="1000.0" minimum="1.0"/>
</arcmind:field>
以上标记输出以下 HTML: <label style="" class="errorText">Artist*</label>
<input type="text" id="cdForm:artist "
name=" cdForm:artist " />
Artist is blank, it must contain characters
图 5 显示了浏览器中这些内容可能显示的效果。 清单 6 显示了创建 Field 组件的代码。因为这个组件负责输入文本而不仅仅是输出它(像 Label 那样),所以要从继承
package com.arcmind.jsfquickstart;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
/**
* @author Richard Hightower
*
*/
public class FieldComponent extends UIInput {
private String label;
@Override
public Object saveState(FacesContext context) {
Object values[] = new Object[2];
values[0] = super.saveState(context);
values[1] = label;
return ((Object) (values));
}
@Override
public void restoreState(FacesContext context, Object state) {
Object values[] = (Object[])state;
super.restoreState(context, values[0]);
label = (String)values[1];
}
public FieldComponent (){
this.setRendererType("arcmind.Field");
}
/**
* @return Returns the label.
*/
public String getLabel() {
return label;
}
/**
* @param label
* The label to set.
*/
public void setLabel(String label) {
this.label = label;
}
@Override
public String getFamily() {
return "arcmind.Field";
}
public boolean isError() {
return !this.isValid();
}
}
可以注意到,代表片段中遗漏了编码方法。这是因为编码和解码发生在独立的渲染器中。我稍后会介绍它。 虽然 Label 组件只有一个属性(JSP 属性),可是 Field 组件却有多个属性,即 像使用 Label 组件时一样,需要用定制标记把 Field 组件绑定到 JSP,如清单 7 所示:
/*
* Created on Jul 19, 2004
*
*/
package com.arcmind.jsfquickstart;
import javax.faces.application.Application;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
import javax.faces.webapp.UIComponentTag;
/**
* @author Richard Hightower
*
*/
public class FieldTag extends UIComponentTag {
private String label;
private String errorStyleClass="";
private String errorStyle="";
private boolean required;
private String value="";
/**
* @return Returns the label.
*/
public String getLabel() {
return label;
}
/**
* @param label The label to set.
*/
public void setLabel(String label) {
this.label = label;
}
/**
* @see javax.faces.webapp.UIComponentTag#setProperties
* (javax.faces.component.UIComponent)
*/
@Override
protected void setProperties(UIComponent component) {
/* You have to call the super class */
super.setProperties(component);
((FieldComponent)component).setLabel(label);
component.getAttributes().put("errorStyleClass",
errorStyleClass);
component.getAttributes().put("errorStyle",errorStyle);
((FieldComponent)component).setRequired(required);
FacesContext context = FacesContext.getCurrentInstance();
Application application = context.getApplication();
ValueBinding binding = application.createValueBinding(value);
component.setValueBinding("value", binding);
}
/**
* @see javax.faces.webapp.UIComponentTag#getComponentType()
*/
@Override
public String getComponentType() {
return "arcmind.Field";
}
/**
* @see javax.faces.webapp.UIComponentTag#getRendererType()
*/
@Override
public String getRendererType() {
return "arcmind.Field";
}
/**
* @return Returns the errorStyleClass.
*/
public String getErrorStyleClass() {
return errorStyleClass;
}
/**
* @param errorStyleClass The errorStyleClass to set.
*/
public void setErrorStyleClass(String errorStyleClass) {
this.errorStyleClass = errorStyleClass;
}
/**
* @return Returns the errorStyle.
*/
public String getErrorStyle() {
return errorStyle;
}
/**
* @param errorStyle The errorStyle to set.
*/
public void setErrorStyle(String errorStyle) {
this.errorStyle = errorStyle;
}
/**
* @return Returns the required.
*/
public boolean isRequired() {
return required;
}
/**
* @param required The required to set.
*/
public void setRequired(boolean required) {
this.required = required;
}
/**
* @return Returns the value.
*/
public String getValue() {
return value;
}
/**
* @param value The value to set.
*/
public void setValue(String value) {
this.value = value;
}
}
从概念上说,在上面的代码和 Label 组件之间找不出太大区别。但是,在这个示例中, protected void setProperties(UIComponent component) {
/* You have to call the super class */
super.setProperties(component);
((FieldComponent)component).setLabel(label);
component.getAttributes().put("errorStyleClass",
errorStyleClass);
component.getAttributes().put("errorStyle",errorStyle);
((FieldComponent)component).setRequired(required);
虽然 这个修订后的 protected void setProperties(UIComponent component) {
...
FacesContext context = FacesContext.getCurrentInstance();
Application application = context.getApplication();
ValueBinding binding = application.createValueBinding(value);
component.setValueBinding("value", binding);
这个代码允许 Field 组件的 最后介绍渲染器,但并不是说它不重要。独立渲染器必须考虑的主要问题是解码(输入) 和编码(输出)。Field 组件做的编码比解码多得多,所以它的渲染器有许多编码方法,而只有一个解码方法。在清单 8 中,可以看到 Field 组件的渲染器: 清单 8. FieldRenderer 扩展自 Renderer
package com.arcmind.jsfquickstart;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import javax.faces.el.ValueBinding;
import javax.faces.render.Renderer;
/**
* @author Richard Hightower
*
*/
public class FieldRenderer extends Renderer {
@Override
public Object getConvertedValue(FacesContext facesContext, UIComponent component,
Object submittedValue) throws ConverterException {
//Try to find out by value binding
ValueBinding valueBinding = component.getValueBinding("value");
if (valueBinding == null) return null;
Class valueType = valueBinding.getType(facesContext);
if (valueType == null) return null;
if (String.class.equals(valueType)) return submittedValue;
if (Object.class.equals(valueType)) return submittedValue;
Converter converter = ((UIInput) component).getConverter();
converter = facesContext.getApplication().createConverter(valueType);
if (converter != null ) {
return converter.getAsObject(facesContext, component, (String) submittedValue);
}else {
return submittedValue;
}
}
@Override
public void decode(FacesContext context, UIComponent component) {
/* Grab the request map from the external context */
Map requestMap = context.getExternalContext().getRequestParameterMap();
/* Get client ID, use client ID to grab value from parameters */
String clientId = component.getClientId(context);
String value = (String) requestMap.get(clientId);
FieldComponent fieldComponent = (FieldComponent)component;
/* Set the submitted value */
((UIInput)component).setSubmittedValue(value);
}
@Override
public void encodeBegin(FacesContext context, UIComponent component)
throws IOException {
FieldComponent fieldComponent = (FieldComponent) component;
ResponseWriter writer = context.getResponseWriter();
encodeLabel(writer,fieldComponent);
encodeInput(writer,fieldComponent);
encodeMessage(context, writer, fieldComponent);
writer.flush();
}
private void encodeMessage(FacesContext context, ResponseWriter writer,
FieldComponent fieldComponent) throws IOException {
Iterator iter = context.getMessages(fieldComponent.getClientId(context));
while (iter.hasNext()){
FacesMessage message = (FacesMessage) iter.next();
writer.write(message.getDetail());
}
}
private void encodeLabel(ResponseWriter writer, FieldComponent
fieldComponent) throws IOException{
writer.startElement("label", fieldComponent);
if (fieldComponent.isError()) {
String errorStyleClass = (String) fieldComponent.getAttributes().get("errorStyleClass");
String errorStyle = (String) fieldComponent.getAttributes().get("errorStyle");
writer.writeAttribute("style", errorStyle, "style");
writer.writeAttribute("class", errorStyleClass, "class");
}
writer.write("" + fieldComponent.getLabel());
if (fieldComponent.isRequired()) {
writer.write("*");
}
writer.endElement("label");
}
private void encodeInput(ResponseWriter writer, FieldComponent
fieldComponent) throws IOException{
FacesContext currentInstance = FacesContext.getCurrentInstance();
writer.startElement("input", fieldComponent);
writer.writeAttribute("type", "text", "type");
writer.writeAttribute("id", fieldComponent.getClientId(currentInstance), "id");
writer.writeAttribute("name", fieldComponent.getClientId(currentInstance), "name");
if(fieldComponent.getValue()!=null)
writer.writeAttribute("value", fieldComponent.getValue().toString(), "value");
writer.endElement("input");
}
}
正如前面提到的,渲染器做的主要工作就是解码输入和编码输出。我先从解码开始,因为它是最容易的。 @Override
public void decode(FacesContext context, UIComponent component) {
/* Grab the request map from the external context */
Map requestMap = context.getExternalContext().getRequestParameterMap();
/* Get client ID, use client ID to grab value from parameters */
String clientId = component.getClientId(context);
String value = (String) requestMap.get(clientId);
FieldComponent fieldComponent = (FieldComponent)component;
/* Set the submitted value */
((UIInput)component).setSubmittedValue(value);
}
Label 组件不需要进行解码,因为它是一个 编码方法没什么惊讶的。它们与 Label 组件中看到的类似。第一个方法 @Override
public void encodeBegin(FacesContext context, UIComponent component)
throws IOException {
FieldComponent fieldComponent = (FieldComponent) component;
ResponseWriter writer = context.getResponseWriter();
encodeLabel(writer,fieldComponent);
encodeInput(writer,fieldComponent);
encodeMessage(context, writer, fieldComponent);
writer.flush();
}
private void encodeLabel(ResponseWriter writer, FieldComponent fieldComponent) throws IOException{
writer.startElement("label", fieldComponent);
if (fieldComponent.isError()) {
String errorStyleClass = (String) fieldComponent.getAttributes().get("errorStyleClass");
String errorStyle = (String) fieldComponent.getAttributes().get("errorStyle");
writer.writeAttribute("style", errorStyle, "style");
writer.writeAttribute("class", errorStyleClass, "class");
}
writer.write("" + fieldComponent.getLabel());
if (fieldComponent.isRequired()) {
writer.write("*");
}
writer.endElement("label");
}
首先, 您可能已经注意到,有一个方法我还没有介绍。这个方法就是这个类中的“黑马”方法。如果您阅读 实际上,基类 相反,需要使用方法
@Override
public Object getConvertedValue(FacesContext facesContext,
UIComponent component, Object submittedValue) throws ConverterException {
//Try to find out by value binding
ValueBinding valueBinding = component.getValueBinding("value");
if (valueBinding == null) return null;
Class valueType = valueBinding.getType(facesContext);
if (valueType == null) return null;
if (String.class.equals(valueType)) return submittedValue;
if (Object.class.equals(valueType)) return submittedValue;
Converter converter = ((UIInput) component).getConverter();
converter = facesContext.getApplication().createConverter(valueType);
if (converter != null ) {
return converter.getAsObject(facesContext, component, (String) submittedValue);
}else {
return submittedValue;
}
}
清单 9 的代码添加了 如果想知道如何把组件和渲染器关联,那么只要看看图 6 即可。 定制标记有两个方法,分别返回组件类型和渲染器类型。这些方法用于查找配置在 faces-config.xml 中的正确的渲染器和组件。请注意(虽然图中没有)组件必须返回正确的 family 类型。 Web的开发就是组件的开发 ,JSF的组件开发实例最近一直在研究JSF的应用 , 还买了一本书(JSF in Action 中文名:JSF实战),却发现“原来无耻的还是中国人”这个道理,原因在于几点:
闲话休谈, 下面是我为了研究JSF组件开发在IBM官方网站看到的内容 , 很不错,权且当作那300页的补充吧 。
JSF 组件模型与 AWT GUI 组件模型类似。它有事件和属性,就像 Swing 组件模型一样。它也有包含组件的容器,容器也是组件,也可以由其他容器包含。从理论上说,JSF 组件模型分离自 HTML 和 JSP。JSF 自带的标准组件集里面有 JSP 绑定,可以生成 HTML 渲染。 JSF 组件的示例包括日历输入组件和 HTML 富文本输入组件。您可能从来没时间去编写这样的组件,但是如果它们已经存在,那会如何呢?通过把常用功能变成商品,组件模型降低了向 Web 应用程序添加更多功能的门槛。 组件的功能通常围绕着两个动作:解码和编码数据。解码 是把进入的请求参数转换成组件的值的过程。编码 是把组件的当前值转换成对应的标记(也就是 HTML)的过程。 JSF 框架提供了两个选项用于编码和解码数据。使用直接实现 方式,组件自己实现解码和编码。使用委托实现 方式,组件委托渲染器进行编码和解码。如果选择委托实现,可以把组件与不同的渲染器关联,会在页面上以不同的方式渲染组件;例如多选列表框和一列复选框。 因此,JSF 组件由两部分构成:组件和渲染器。JSF 组件 类定义 UI 组件的状态和行为;渲染器 定义如何从请求读取组件、如何显示组件 —— 通常通过 HTML 渲染。渲染器把组件的值转换成适当的标记。事件排队和性能验证发生在组件内部。 在图 1 中可以看到数据编码和解码出现在 JSF 生命周期中的什么阶段(到现在,我希望您已经熟悉 JSF 生命周期了)。
在许多情况下,可以在保持组件本身不变的情况下,通过改变渲染而简化开发过程。在这些情况下,可以编写定制渲染器而不是定制组件。 所有 JSF 组件的基类是 组件拥有双亲和标识符。每个组件都关联着一个组件类型,组件类型用于在 face 的上下文配置文件(faces-config.xml)中登记组件。可以用 JSF-EL (表达式语言)把 JSF 组件绑定到受管理的 bean 属性。可以把表达式关联到组件上的任何属性,这样就允许用 JSF-EL 设置组件的属性值。在创建使用 JSF-EL 绑定的组件属性时,需要创建值绑定表达式。在调用绑定属性的 getter 方法时,除非 setter 方法已经设置了值,否则 getter 方法必须用值绑定获得值。 组件可以作为 像表单字段组件这样的组件拥有一个 这里描述的许多组件的概念将会是接下来展示的示例的一部分,所以请记住它们!
我们用一个又好又容易的示例来开始 JSF 组件的开发:我将展示如何渲染 Label 标记(示例: 下面是我要采取的步骤:
Label 示例将演示 JSF 组件开发的以下方面:
返回 图 1,可以看到在这个示例中会有两个生命周期属性在活动。它们是 Apply Request Value 和 Render Response。 在图 2 中,可以看到在 JSP 中如何使用 Label 标记的( 第一步是创建一个组件,继承 清单 1. 继承 UIComponent 并添加 label
import java.io.IOException;
import javax.faces.component.UIOutput;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
public class LabelComponent extends UIOutput{
private String label;
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
...
接下来要做的是保存组件状态。JSF 通常通过会话、隐藏表单字段、cookies 等进行实际的存储和状态管理。(这通常是用户配置的设置)。要保存组件状态,需要覆盖组件的
@Override
public Object saveState(FacesContext context) {
Object values[] = new Object[2];
values[0] = super.saveState(context);
values[1] = label;
return ((Object) (values));
}
@Override
public void restoreState(FacesContext context, Object state) {
Object values[] = (Object[])state;
super.restoreState(context, values[0]);
label = (String)values[1];
}
可以注意到,我使用的是 JDK 1.5。我对编译器进行了设置,所以我必须指定 override 注释,以便指明哪些方法要覆盖基类的方法。这样做可以更容易地标识出 JSF 的钩子在哪。 创建组件的最后一步是用 faces-config.xml 登记它,如下所示: <faces-config>
<component>
<component-type>simple.Label</component-type>
<component-class>
arcmind.simple.LabelComponent
</component-class>
</component>
...
下面要做的是内联地定义渲染器的功能。稍后我会介绍如何创建独立的渲染器。现在,先从编码 Label 组件的输出、显示 label 开始,如清单 3 所示:
public class LabelComponent extends UIOutput{
...
public void encodeBegin(FacesContext context)
throws IOException {
ResponseWriter writer =
context.getResponseWriter();
writer.startElement("label", this);
writer.write(label);
writer.endElement("label");
writer.flush();
}
...
}
注意,响应写入器( 下面显示的 family 属性用来把 Label 组件与渲染器关联。虽然目前 Label 组件还不需要这个属性(因为还没有独立的渲染器),但是在这篇文章后面,在介绍如何创建独立渲染器的时候,会需要它。 public class LabelComponent extends UIOutput{
...
public String getFamily(){
return "simple.Label";
}
...
}
如果正在使用来自 Sun Microsystems 的 JSF 参考实现(不是 MyFaces 实现),那么就不得不在组件创建代码中添加下面一段: public void encodeEnd(FacesContext context)
throws IOException {
return;
}
public void decode(FacesContext context) {
return;
}
Sun 的 JSF RI 期望,在组件没有渲染器的时候,渲染器会发送一个空指针异常。MyFaces 实现不要求处理这个需求,但是在代码中包含以上方法依然是个好主意,这样组件既可以在 MyFaces 环境中工作也可以在 JSF RI 环境中工作了。
如果正在使用 Sun JSF RI 或其他替代品,那么请帮自己一个忙,转到 MyFaces。虽然 MyFaces 不总是 更好的实现,但是目前它是。它的错误消息要比 Sun JSF RI 的好,而这个框架相比之下更严格。 JSF 组件不是天生绑定到 JSP 上的。要连接起 JSP 世界和 JSF 世界,需要能够返回组件类型的定制标记(然后在 faces-context 文件中登记)和渲染器,如图 3 所示。 注意,由于没有独立的渲染器,所以可以给 [LabelTag.java]
public class LabelTag extends UIComponentTag {
…
protected void setProperties(UIComponent component) {
/* you have to call the super class */
super.setProperties(component);
((LabelComponent)component).setLabel(label);
}
记住, 现在要做的全部工作就是创建一个 TLD(标记库描述符)文件,以登记定制标记,如清单 4 所示:
[arcmind.tld]
<taglib>
<tlib-version>0.03</tlib-version>
<jsp-version>1.2</jsp-version>
<short-name>arcmind</short-name>
<uri>http://arcmind.com/jsf/component/tags</uri>
<description>ArcMind tags</description>
<tag>
<name>slabel</name>
<tag-class>arcmind.simple.LabelTag</tag-class>
<attribute>
<name>label</name>
<description>The value of the label</description>
</attribute>
</tag>
...
一旦定义了 TLD 文件,就可以开始在 JSP 中使用标记了,如下面示例所示: [test.jsp]
<%@ taglib prefix="arcmind"
uri="http://arcmind.com/jsf/component/tags" %>
...
<arcmind:slabel label="Form Test"/>
现在就可以了 —— 开发一个简单的 JSP 组件不需要更多了。但是如果想创建稍微复杂一些的组件,针对更复杂的使用场景时该怎么办?请继续往下看。 |
|||||||||||||||||||||||||||||||||||||||
|
|