<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>liulang&#039;s blog &#187; J2EE</title>
	<atom:link href="http://www.liulang.cq.cn/category/j2ee/feed" rel="self" type="application/rss+xml" />
	<link>http://www.liulang.cq.cn</link>
	<description>既然选择了远方，便只顾风雨兼程……</description>
	<lastBuildDate>Thu, 10 Jun 2010 11:41:08 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>J2EE项目10大风险</title>
		<link>http://www.liulang.cq.cn/20090811/j2ee10risk.html</link>
		<comments>http://www.liulang.cq.cn/20090811/j2ee10risk.html#comments</comments>
		<pubDate>Tue, 11 Aug 2009 03:33:28 +0000</pubDate>
		<dc:creator>刘 浪</dc:creator>
				<category><![CDATA[J2EE]]></category>

		<guid isPermaLink="false">http://www.liulang.cq.cn/?p=105</guid>
		<description><![CDATA[本文采用了以下结构来描述风险：　 
风险名称：风险的标题（使用粗体） 
项目阶段：在哪个项目阶段会发生风险情况 
影响阶段：会影响到以后的哪些阶段 
症状：    风险产生时的症状 
规避方案：如何规避风险或者把其对项目的影响降低到最小程度 
备注：    风险相关的补充说明和提示 
通过对企业级Java项目的仔细考察，本文将J2EE项目过程分解为以下几个阶段：  
提供商选择: 在开始你的J2EE项目之前，要选择最合适的提供商，从应用服务器到开发工具组合，一直至工作期间享用的咖啡的厂商。:)　 
设计： 在遵照一系列严格的规范和软件工程方法的前提下，可以开始进行足够充分的设计，然后再很自然地进入开发阶段。在开发之前，要周全地考虑好正在做什么，以及如何往下做的问题。另外，我使用了一些设计模板来确信在进入开发之前，已经想到了所有的问题和可能的解决方案。但是，我有时也在该阶段做一些编码，有时候这样做可以回答一些问题，有效地判断出性能上和模块划分上的问题。　 
开发: 也就是程序开发阶段，选择一些好的开发工具，进行精良的设计等等，在这个阶段将显示其优越性，并且可以给开发带来很大的帮助。　 
稳定性/负载测试：在该阶段，系统架构师和项目经理应该冻结住产品特性，并把焦点放在质量以及产品参数（允许的并发用户数量，故障恢复情况，等等）上。质量和性能在该阶段应得到足够的重视。当然，最好应该避免在前阶段写出不良的运行缓慢的代码而到本阶段来作很多的修改。 
成熟期：这不是一个真正的项目阶段，而是一个固定的准备阶段。过去潜伏的错误（来自于糟糕的设计和开发、错误的厂商选择）可能出现并影响你的系统。 
OK，以下让我们进入 top 10 项目风险！ 
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;
风险1:没有真正理解 Java, EJB, 和J2EE
这个问题可以分解为3个部分，以便于分析。 
描述: 没有真正理解Java 
项目阶段:开发 
影响阶段：设计、稳定性测试、成熟期 
对系统性能的影响：可维护性、可扩展性、性能 
症状： 
重复开发了JDK核心API中的功能或类 
不懂得以下列表中的某些项（这只是一些主题或者实际例子而已）： 
垃圾收集器 (train, generational, incremental, synchronous, asynchronous) 
对象在何时能被进行垃圾收集 &#8211; dangling references 
使用的继承机制及其权衡 
over-riding和over-loading方法 
为什么java.lang.String (在这里用你所中意的类代替) 提供的性能不好 
Java中的pass-by参考语义和EJB中pass-by值的语义的比较 
使用 == 或者使用equals() 方法 for nonprimitives 
在不同平台上Java线程的运行顺序方式(例如是否是抢先方式的) 
新线程和本地线程的比较 
Hotspot技术(以及为什么旧的性能调整技术降低了Hotspot 的优化效果) 
JIT，以及什么时候好的JIT变得不好(未安装的JAVA编译器，以及你的代码运行得刚够良好) 
API搜集 
RMI 
规避方案：
你需要不断改进Java方面的知识，尤其是深入了解Java的优势和不足之处。Java的存在价值已经远不止是一种语言，理解平台(JDK及工具等)也是同样重要的。具体地说，你应该是经过认证的Java程序员，如果你不是的话，也许你有时会为还有那么多不知道的内容而感到惊讶。另外，你可以加入Java的邮件列表。以前我曾加盟过的每一个公司都加入了这样的邮件列表，从同行中学到技术，这将是你最好的资源。 
备注:
如果你或者你的团队中的成员不真正了解编程语言和平台，怎么还能保持成功的希望呢？强干的Java程序员之于EJB和J2EE，就象是鸭子之于水一样。与此相反，比较弱的、没有经验的程序员只能开发出质量低劣的J2EE应用程序。 
描述: 没有真正理解EJB 
项目阶段:
设计 
影响阶段:
开发、稳定化 
对系统的影响:
维护 
症状: 
EJB在第一次被调用后没有再被使用到(尤其是stateless session bean) 
没有重复利用价值的EJB 
不理解开发者要做什么，容器提供什么 
EJB没有依照规范定义(fire线程, 加载了本地库，试图执行I/O，等等) 
解决方案:
要改进关于EJB方面的知识，可以找一个周末来阅读EJB规范 (1.1版有314页)，然后阅读2.0规范(524页!)，这样可以了解到1.1没有定义到的而在2.0规范中补充的内容。EJB开发者从18.1及18.2章节开始阅读是比较合适的。 
备注:
不要从提供商的角度去看EJB，要确切地知道规范所支持的标准EJB模型和基于这些模型的特殊应用之间的区别。这也会有助于你迁移到别的提供商的时候所用。 
描述: 没有真正理解J2EE 
项目阶段:
设计 
影响阶段:
开发 
对系统的影响:
维护、扩展性、性能 
症状: 
&#8220;Everything is an EJB&#8221;的设计方式 
用手工事务管理取代了容器-提供的机制 
自定义方式的安全处理 &#8211; J2EE平台在企业级计算中，从表示逻辑到后台处理，已具有最完整的集成安全架构；但很少用到其全部功能。 
解决方案:
学习J2EE的关键组件，并且了解它们的优缺点，依次用它们替代每一个服务；“知识就是力量”在这里是行之有效的。 
备注:
只有知识能够弥补这些问题。好的Java开发者会成为好的EJB开发者，此后也应逐渐成为J2EE得道高手。Java和J2EE知识掌握得越多，设计和开发工作就会越出色。在设计阶段一切都会有条不紊。
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;
风险2: 过度设计(Over-engineering) (采用 EJB或者不采用EJB) 
项目阶段：
设计 
影响的项目阶段:
开发 
对系统的影响:
维护、扩展性、性能 
症状: 
过于庞大的EJB 
开发者无法解释EJB做什么，以及其间的联系 
无法重复使用的EJB、组件或者服务 
EJB启动了新的事务，而该事务本该由一个已存在的EJB启动 
为了安全，把数据分离级别定得太高 
解决方案:
过度工程化的解决之道直接来自于极限编程 (XP)方法：用最小的设计和编程来满足需求，除此之外别无它干。除非你需要明确知道今后可能的需求，如将来的负载要求，或者系统在最高负载下的表现，否则大可不必为系统将来的情况做太多考虑或猜测。另外，J2EE平台已经定义了可伸缩性及出错恢复等特性，可以让服务器系统为你进行处理。
在最小的系统中，只包含一个个小组件，这些组件只做一件事，只要把这些要求做到的进行实现，系统稳定性就已经得到了提高，而且，你的系统的可维护性会变得很强，在未来要增加功能以满足新的需求也将变得容易。 
备注:
除了上面所列方案之外，可以推行设计模式 &#8211; 它们可以显著地改进你的系统设计。EJB模型本身也广泛使用了设计模式。例如，每个EJB所带的Home 接口就是Finder和Factory模式的实例。EJB的remote接口扮演了一种实际bean实现的代理，并且对于提供容器的能力也是至关重要的，这些容器截取调用信号并提供诸如透明（transparent）负载均衡的服务。忽视设计模式也是危险的一部分。 
我常提到要反对的另外一种危险是：仅仅是为了使用EJB而使用EJB。在你的应用中的某一部分可能并不需要EJB，甚至你的整个应用都不需要。这是过度工程化所走的极端，而且我确实也目睹了一些良好的servlet和JavaBean应用被重构为EJB，而这样做并没有很好的技术上的理由。
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;
风险3: 没有将业务规则和逻辑表现形式相分离
项目阶段：
设计 
影响的项目阶段：
开发 
对系统的影响:
维护、扩展性、性能 
症状: 
过于庞大、没有边际的JSP程序 
在业务逻辑改变的时候必须修改JSP 
在要求改变界面显示的时候需要修改并重新配置EJB和其它后台组件 
规避方案:
J2EE平台使你有机会将表示逻辑和导航控制相分离，进而与业务规则相分离。这被称为模式2结构。 
备注:
可以使用具有一致性的设计来进行用户界面框架的连接。(例如可以使用taglib)，这将帮助你避免逻辑分离的问题。有许多现成的好的方法可供选择。对每一个分别进行评估，然后采用最合适的框架。
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;
风险4: 没有在开发环境中进行适当的配置
项目阶段：
开发 
影响的项目阶段:
稳定化、并发、成熟期 
对系统的影响:
你的权衡 
症状: 
经过多日或数周的时间才能过渡到成熟系统 
风险存在与过渡期，带有很多不确定性，有些主要的功能场景没有被测试到 
实际系统中的数据和开发、测试中的数据不同 
无法在开发者机器上进行组建 
应用行为在开发、稳定化及产品环境中各不相同 
规避方案:
解决之道是忠实地在开发环境中配置实际的环境，让开发所用环境接近于要实施产品的环境。如果未来环境是JDK 1.2.2及Solaris 7，那么不要在JDK 1.3及Red Hat Linux上进行开发。对于所用的应用服务器也是如此。同样，要快速地看一下产品数据库中的数据，并将这样的数据用于测试。不要依赖于人工创建的数据。如果产品数据很敏感，则要使之变得不敏感，然后把它配置起来。开发中未能预期到的产品数据将对以下过程产生破坏： 
数据检验规则 
系统测试行为 
系统组件构建(特别地包括：EJB-EJB以及EJB-数据库) 
最为糟糕的是，这样还可能产生异常、空指针，以及你从没见过的问题。 
备注:
开发人员常把安全性问题放到稳定化阶段才开始解决。要防止这样的陷阱产生，你也可以花费同样多的时间在业务逻辑中改进安全性。 
成熟期是一个复杂的过程，其中充满了技术性问题和非技术性问题。你可能会陷于想不到的一大堆问题中，这就是成熟化所意味的一切。开发及稳定化环境过程为你提供了制造更多这样的问题，以及发现这样的问题的地方，不断去做，就可以大大减少风险。 
你做的工程越多，你就越能了解什么是可行的，什么是不可行的。你可以对工程问题进行记录，以避免同样的错误重复发生。
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;
风险5: 选择了错误的提供商
项目阶段：
提供商选择 
影响阶段：
设计、开发、稳定化/负载测试，成熟化 
对系统的影响:
可伸缩性、性能、可维护性及稳定性 
症状:
开发人员要使用更多的时间来处理工具方面的问题，而不是很有成效地使用这些工具 
为了应付已知的和未知的问题，而不得不进行显著的系统重新设计 
在不同的工具之间很难进行集成（应用服务器与IDE工具，IDE工具与调试器，源码控制与合成工具，等等） 
对于IDE工具和调试器等，开发人员往往排斥它们，而推崇自己所喜欢的工具 
规避方案:
为了避免风险5，你需要一个很好的提供商选择过程，风险10的规避也适用于此。 
要真正衡量一种IDE工具是否最合适的方法是真正地进行使用。而唯一来评估一种J2EE应用的方法是建立一种概念试验来进行证明，在试验中要包含你的应用框架。事实上，你也不希望在花费了3个月时间进行了培训和开发后，在使用时又发现一些bug。 
假设在开发到一半的时候，突然发现你的工具集有问题，那么你早应该知道，有些工具确实比另一些更重要。如果你所选的应用服务器不能充分满足你的需要，你只好修改原先的设定。如果IDE不好，则需要设置最低限度的代码标准，并让开发人员任意选择他们认为最为有效的工具。 
备注:
要真正了解到哪一个供应商对一项特殊的任务来说最合适，其实并不是一件一次性决定的事情。你需要不断地跟踪与评估这个市场。例如，在过去的一年里我用过4种不同的IDE工具，这取决于我使用了什么样的应用服务器、平台，是否使用EJB等。
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;

风险6: 不了解你的提供商
项目阶段：
提供商选择 
影响阶段:
提供商选择阶段后面的所有阶段：设计、开发、稳定化/负载测试、成熟化 
对系统的影响:
可维护性、可伸缩性、性能 
症状: 
开发所用周期超过了最坏预测的周期1/3以上 
提供商已经提供了某项功能，但开发者在不知道的情况下重新进行了该项功能的开发 
规避方案:
为了规避这样的风险，你可以尽可能地订阅提供商的网上资源，例如邮件列表、新闻组、版本信息（尤其是其中的bug修复补丁的说明等），你能从中得到无法估量之多的收获。 
一旦你已经选定了提供商，那么立即就要投资进行培训，并且尽可能赶在项目启动以前。然后，逐渐在团队中建立起对此提供商的认识及信任。试着建立几个EJB并部署一下，再用你的表示层技术 (Swing GUI, JSP等)来调用它们。如果你既要搭建开发环境，又要同时在实现项目目标，就会产生一些不必要的冲突。实际上，我也见到过一直没有进行构建过程的情况：“我们没有时间。”因此，这些工作必须提早进行。有些人会说：“我们的计划中没有为我们提供这些时间。”我的回答是：“你的计划中并没有不给你时间使你不这么做啊。” 
备注:
在J2EE世界里，各提供商产品的技术兼容性究竟如何？让我们看一下IBM和BEA的具体分析吧。两者都分别在各自的应用服务器中支持EJB 1.1。那么，实际上BEA WebLogic 5.1和IBM WebSphere 3.5究竟有多少相似之处呢? 
BEA WebLogic和IBM WebSphere的系统配置和管理方式几乎完全不同。 
IBM在WebSphere中采用了全面的GUI环境，而与之相对的是，BEA 在WebLogic中提供一整套命令行。 
IBM WebSphere使用IIOP来和CORBA异常进行通讯，这些异常对程序员来说是可见的；WebLogic根本没有CORBA构造，而缺省使用t3协议。 
WebSphere和Visual Age衔接紧密，而WebLogic是IDE无关的，实际上，你几乎可以使用任何的开发工具。 
由此可见，差异还是相当多。如果你是一种应用服务器的专家，并不意味着你就是所有应用服务器的专家。这种区别体现在IDE，debugger，build工具，配置管理等等方面。具备某提供商的某项特殊工具的使用经验，可以在评估该提供商的竞争对手产品时具有一些便利。但是，不要奢望在不同产品之间进行无缝的转移或衔接。因此，你不得不花费足够多的时间在熟练掌握这些工具上。
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;
风险7: 设计中没有充分考虑到可伸缩性和产品性能
项目阶段：
设计 
受影响的项目阶段:
开发、负载测试及成熟化 
对系统的影响:
可伸缩性、性能、可维护性 
症状: 
无法忍受的速度缓慢 
系统给服务器端增加的沉重负担，而无法利用到一些聚簇技术。 
规避方案:
把精力集中于性能和可伸缩性方面的需求，明确开发中要达到的性能指标。如果你需要每秒50个事务，而你的EJB设计只能提供40个，那么你就需要考虑替代方案，诸如存储过程，批处理，或者重新考虑OLTP的设计。 
尽可能让你的提供商加入进来，他们应该非常清楚其产品的强项和弱处在哪里，然后给你提供最直接的帮助。 
备注:
本风险与风险2 (over-engineering)似乎有些冲突。实际上，两者相互影响。 我对风险2给出的解决方案是，只在绝对必要的情况下才进行构建。而对与性能和可伸缩性，你要预先划分好什么是必须要做的。 
如果你实现就识别出系统需要非常强的可伸缩性，并把它作为一个比较关键的需求，那么你首先需要选择一个带有很强的簇支持及事务型缓存的应用服务器。另外，你应把业务对象设计为EJB，从而可以充分利用服务器架构的优势。 XP也没有问题，你仍然是只做绝对必要的工作。 
我把这样的观点看作是一种检查和平衡的方法。我们只需要最简单可能性的系统，该系统只提供客户所需要的功能与行为即可。
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;
风险8: 陈旧的开发过程
项目阶段：
开发 
影响阶段:
稳定化，成熟化 
对系统的影响:
可维护性、代码质量 
症状: 
项目计划看上去似乎类似于瀑布模型: “首先草构设计，然后在一个很长的周期里进行开发。” 
由于不存在构建（build）过程，每次构建都象是噩梦 
构建的日期等于损失开发的日期，因为什么也没有做成 
在集成以前组件没有分别被充分地测试过，而集成测试意味着将2个不稳定的组件放在一起，然后查看堆栈里的跟踪结果。 
规避方案:
好的软件方法学将提高你的软件生命期。此前我已经提到XP方法，你可以在网上找到很多这方面的资料。 
备注:
JUnit可以用来进行单元测试，Ant工具可以进行编译与构建，这2种工具都对XP方法有很好的支持。
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;
风险9: 没有好的架构方式
项目阶段：
开发 
影响阶段:
开发、稳定化、成熟期 
对系统的影响：
可维护性、可伸缩性、代码质量 
症状: 
在代码中使用了很多次的核心库中发现Bug。 
没有建立日志标准 &#8211; 于是系统的输出很难读取或者解析。 
不良的不一致的异常处理。在有些站点中我们甚至可以看到，出错信息直接暴露给了最终用户，例如在用户在他的购物车核帐时发送一条SQLException堆栈跟踪信息，用户接着会怎么做？打电话给数据库管理员要求对primary key约束进行修补吗？ 
以下任务已经被开发者以各种方式处理了无数次了，这些都有必要放在任何构架设计的第一批目标中。　 
日志 
异常处理 
与资源的连接(数据库，名字服务等) 
构建JSP页 
数据合法性检查 
规避方案:
我是一个轻方法学的信徒和实践者。我在JavaWorld 上的第一篇文章 &#8211; &#8221;Frameworks Save the Day&#8221; &#8211; 就是研讨在企业Java环境中的架构。即使你已经开始开发了，此时考虑一下架构仍然是值得的。可能你不得不忍受一下重构带来的异常处理和日志处理，但从长远来看还是值得的，这样即省时间又省钱。 
备注:
让我们想一下在构架中基于组件开发的可重用性的不同等级。第一级别是plumbing，具有0.9以上的可重用比例，也就是说，有90%的项目可以对它重复利用。 服务定义得越详细，重用比例就越低。换句话说，我需要构建一个会计服务，但要提供这些资源与用法的管理，以便于其它50%项目中可以对它们进行重复利用。但是对那些项目来说，能得到这些资源，那真是太好了！
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;

风险10: 项目计划和设计基于市场效应，而脱离了技术现实 
备注: 不断有新人加入到Java/EJB的开发领域中来，不理解Java的人数一般比想象中还要多。 
项目阶段：
所有阶段都会受到影响，包括提供商的选择 
影响阶段:
所有阶段都会受到影响 
对系统的影响:
可维护性、可扩展性、设计质量、代码质量 
症状: 
轻率地进行技术决策，认为EJB只是为了便携式处理的方便 
选择提供商的时候没有随即进行产品的试用 
在项目的生命周期内还需要更换工具 
规避方案:
不要轻易相信项目外部的任何人的看法，这些人可能已经有一些既得利益，不要相信提供商的说法（除非你早已经了解），也不要相信白皮书。如果你要取得来自真实世界的关于应用服务器的建议，可以在网上取得。你还可以下载这些工具进行评估，用它们做一些原型，并运行一下其中的样例。(好的提供商都有这样的样例)。 
总的来说，为你的项目选择最好的提供商及工具需要时间，而你可能没有太多的时间。你可以把选择范围限制在3-4个对象，然后用一周时间进行比较和检验。最后从中选出比较满意的工具和产品。 
备注:
如果你缺少J2EE经验，则可能会在项目前期就产生问题。在前期所确定的决策会影响整个过程，并进而影响项目的成功。好的J2EE咨询专家将能够帮助你选择好的提供商，并为设计和开发刻划出一个好的构形。
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;
仅仅只有这10项风险吗？
10只是一个特定的数字，显然，还有更多更多的风险会存在。只是我可以保证的是，如果你克服了所列的各项风险，那么你的项目会有出色的表现并已打好了成功的基础。 
还有一项需要注意，即没有任何东西可以代替经验和计划。如果你没有经验，那么一定要想办法取得并积累。千万不要一边做项目一边进行培训。在开发之前要预先做好充分的准备，最好是在设计以前就进行准备。可以让你的团队接受Java/J2EE顾问的指导，并确保这样的指导能够传递到整个其他的团队成员。 
最后，还有必要提到以下几点： 
软件工程的外界影响 
什么时候进行单元测试，什么时候进行集成测试？ 
设计模式 
异常处理 
结论
总的说来，以上10大风险是你在企业级Java项目开发过程中将面对的主要困难。我也相信在你的旅程中一定还有更多的陷阱，但我比较确信的是我所提到的风险已经涵盖了主要的问题。最后让我们按照优先级重新列举一下10大风险：　 
没有真正理解Java, 没有真正理解EJB, 没有真正理解J2EE 
过度设计(Over-engineering)  
没有将业务规则和逻辑表现形式相分离 
没有在开发环境中进行适当的配置 
选择了错误的提供商 
不了解你的提供商 
设计中没有充分考虑到可伸缩性和产品性能 
陈旧的开发过程 
没有好的架构方式 
项目计划和设计基于市场效应，而脱离了技术现实 
最后，让我祝你好运！　
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;
译后记：
我基本上没有做过J2EE项目，但仍有足够勇气翻译这样的文章。在国内软件公司里，极端情况下也许到处都是风险，这样也就无所谓风险了。对于选择J2EE技术路线，自然会有J2EE特有的风险，因此本文中的风险往往也是特别针对J2EE项目的。另外，对于J2EE项目，我们不应该忽视的一点是，其技术上的风险会更大一些。
]]></description>
			<content:encoded><![CDATA[<p>本文采用了以下结构来描述风险：　 </p>
<p><strong>风险名称：风险的标题（使用粗体） </strong><br />
项目阶段：在哪个项目阶段会发生风险情况 <br />
影响阶段：会影响到以后的哪些阶段 <br />
症状：    风险产生时的症状 <br />
规避方案：如何规避风险或者把其对项目的影响降低到最小程度 <br />
备注：    风险相关的补充说明和提示 <br />
通过对企业级Java项目的仔细考察，本文将J2EE项目过程分解为以下几个阶段：  <span id="more-105"></span></p>
<p>提供商选择: 在开始你的J2EE项目之前，要选择最合适的提供商，从应用服务器到开发工具组合，一直至工作期间享用的咖啡的厂商。:)　 <br />
设计： 在遵照一系列严格的规范和软件工程方法的前提下，可以开始进行足够充分的设计，然后再很自然地进入开发阶段。在开发之前，要周全地考虑好正在做什么，以及如何往下做的问题。另外，我使用了一些设计模板来确信在进入开发之前，已经想到了所有的问题和可能的解决方案。但是，我有时也在该阶段做一些编码，有时候这样做可以回答一些问题，有效地判断出性能上和模块划分上的问题。　 <br />
开发: 也就是程序开发阶段，选择一些好的开发工具，进行精良的设计等等，在这个阶段将显示其优越性，并且可以给开发带来很大的帮助。　 <br />
稳定性/负载测试：在该阶段，系统架构师和项目经理应该冻结住产品特性，并把焦点放在质量以及产品参数（允许的并发用户数量，故障恢复情况，等等）上。质量和性能在该阶段应得到足够的重视。当然，最好应该避免在前阶段写出不良的运行缓慢的代码而到本阶段来作很多的修改。 <br />
成熟期：这不是一个真正的项目阶段，而是一个固定的准备阶段。过去潜伏的错误（来自于糟糕的设计和开发、错误的厂商选择）可能出现并影响你的系统。 </p>
<p>OK，以下让我们进入 top 10 项目风险！ <br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<p><strong>风险1:没有真正理解 Java, EJB, 和J2EE</strong></p>
<p>这个问题可以分解为3个部分，以便于分析。 </p>
<p>描述: 没有真正理解Java </p>
<p>项目阶段:开发 </p>
<p>影响阶段：设计、稳定性测试、成熟期 </p>
<p>对系统性能的影响：可维护性、可扩展性、性能 </p>
<p>症状： </p>
<p>重复开发了JDK核心API中的功能或类 <br />
不懂得以下列表中的某些项（这只是一些主题或者实际例子而已）： <br />
垃圾收集器 (train, generational, incremental, synchronous, asynchronous) <br />
对象在何时能被进行垃圾收集 &#8211; dangling references <br />
使用的继承机制及其权衡 <br />
over-riding和over-loading方法 <br />
为什么java.lang.String (在这里用你所中意的类代替) 提供的性能不好 <br />
Java中的pass-by参考语义和EJB中pass-by值的语义的比较 <br />
使用 == 或者使用equals() 方法 for nonprimitives <br />
在不同平台上Java线程的运行顺序方式(例如是否是抢先方式的) <br />
新线程和本地线程的比较 <br />
Hotspot技术(以及为什么旧的性能调整技术降低了Hotspot 的优化效果) <br />
JIT，以及什么时候好的JIT变得不好(未安装的JAVA编译器，以及你的代码运行得刚够良好) <br />
API搜集 <br />
RMI <br />
规避方案：<br />
你需要不断改进Java方面的知识，尤其是深入了解Java的优势和不足之处。Java的存在价值已经远不止是一种语言，理解平台(JDK及工具等)也是同样重要的。具体地说，你应该是经过认证的Java程序员，如果你不是的话，也许你有时会为还有那么多不知道的内容而感到惊讶。另外，你可以加入Java的邮件列表。以前我曾加盟过的每一个公司都加入了这样的邮件列表，从同行中学到技术，这将是你最好的资源。 </p>
<p>备注:<br />
如果你或者你的团队中的成员不真正了解编程语言和平台，怎么还能保持成功的希望呢？强干的Java程序员之于EJB和J2EE，就象是鸭子之于水一样。与此相反，比较弱的、没有经验的程序员只能开发出质量低劣的J2EE应用程序。 </p>
<p>描述: 没有真正理解EJB </p>
<p>项目阶段:<br />
设计 </p>
<p>影响阶段:<br />
开发、稳定化 </p>
<p>对系统的影响:<br />
维护 </p>
<p>症状: </p>
<p>EJB在第一次被调用后没有再被使用到(尤其是stateless session bean) <br />
没有重复利用价值的EJB <br />
不理解开发者要做什么，容器提供什么 <br />
EJB没有依照规范定义(fire线程, 加载了本地库，试图执行I/O，等等) <br />
解决方案:<br />
要改进关于EJB方面的知识，可以找一个周末来阅读EJB规范 (1.1版有314页)，然后阅读2.0规范(524页!)，这样可以了解到1.1没有定义到的而在2.0规范中补充的内容。EJB开发者从18.1及18.2章节开始阅读是比较合适的。 </p>
<p>备注:<br />
不要从提供商的角度去看EJB，要确切地知道规范所支持的标准EJB模型和基于这些模型的特殊应用之间的区别。这也会有助于你迁移到别的提供商的时候所用。 </p>
<p>描述: 没有真正理解J2EE </p>
<p>项目阶段:<br />
设计 </p>
<p>影响阶段:<br />
开发 </p>
<p>对系统的影响:<br />
维护、扩展性、性能 </p>
<p>症状: </p>
<p>&#8220;Everything is an EJB&#8221;的设计方式 <br />
用手工事务管理取代了容器-提供的机制 <br />
自定义方式的安全处理 &#8211; J2EE平台在企业级计算中，从表示逻辑到后台处理，已具有最完整的集成安全架构；但很少用到其全部功能。 <br />
解决方案:<br />
学习J2EE的关键组件，并且了解它们的优缺点，依次用它们替代每一个服务；“知识就是力量”在这里是行之有效的。 </p>
<p>备注:<br />
只有知识能够弥补这些问题。好的Java开发者会成为好的EJB开发者，此后也应逐渐成为J2EE得道高手。Java和J2EE知识掌握得越多，设计和开发工作就会越出色。在设计阶段一切都会有条不紊。</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<p><strong>风险2: 过度设计(Over-engineering) (采用 EJB或者不采用EJB) </strong><br />
项目阶段：<br />
设计 </p>
<p>影响的项目阶段:<br />
开发 </p>
<p>对系统的影响:<br />
维护、扩展性、性能 </p>
<p>症状: </p>
<p>过于庞大的EJB <br />
开发者无法解释EJB做什么，以及其间的联系 <br />
无法重复使用的EJB、组件或者服务 <br />
EJB启动了新的事务，而该事务本该由一个已存在的EJB启动 <br />
为了安全，把数据分离级别定得太高 <br />
解决方案:<br />
过度工程化的解决之道直接来自于极限编程 (XP)方法：用最小的设计和编程来满足需求，除此之外别无它干。除非你需要明确知道今后可能的需求，如将来的负载要求，或者系统在最高负载下的表现，否则大可不必为系统将来的情况做太多考虑或猜测。另外，J2EE平台已经定义了可伸缩性及出错恢复等特性，可以让服务器系统为你进行处理。<br />
在最小的系统中，只包含一个个小组件，这些组件只做一件事，只要把这些要求做到的进行实现，系统稳定性就已经得到了提高，而且，你的系统的可维护性会变得很强，在未来要增加功能以满足新的需求也将变得容易。 </p>
<p>备注:<br />
除了上面所列方案之外，可以推行设计模式 &#8211; 它们可以显著地改进你的系统设计。EJB模型本身也广泛使用了设计模式。例如，每个EJB所带的Home 接口就是Finder和Factory模式的实例。EJB的remote接口扮演了一种实际bean实现的代理，并且对于提供容器的能力也是至关重要的，这些容器截取调用信号并提供诸如透明（transparent）负载均衡的服务。忽视设计模式也是危险的一部分。 </p>
<p>我常提到要反对的另外一种危险是：仅仅是为了使用EJB而使用EJB。在你的应用中的某一部分可能并不需要EJB，甚至你的整个应用都不需要。这是过度工程化所走的极端，而且我确实也目睹了一些良好的servlet和JavaBean应用被重构为EJB，而这样做并没有很好的技术上的理由。</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<p><strong>风险3: 没有将业务规则和逻辑表现形式相分离</strong><br />
项目阶段：<br />
设计 </p>
<p>影响的项目阶段：<br />
开发 </p>
<p>对系统的影响:<br />
维护、扩展性、性能 </p>
<p>症状: </p>
<p>过于庞大、没有边际的JSP程序 <br />
在业务逻辑改变的时候必须修改JSP <br />
在要求改变界面显示的时候需要修改并重新配置EJB和其它后台组件 <br />
规避方案:<br />
J2EE平台使你有机会将表示逻辑和导航控制相分离，进而与业务规则相分离。这被称为模式2结构。 </p>
<p>备注:<br />
可以使用具有一致性的设计来进行用户界面框架的连接。(例如可以使用taglib)，这将帮助你避免逻辑分离的问题。有许多现成的好的方法可供选择。对每一个分别进行评估，然后采用最合适的框架。</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<p><strong>风险4: 没有在开发环境中进行适当的配置</strong><br />
项目阶段：<br />
开发 </p>
<p>影响的项目阶段:<br />
稳定化、并发、成熟期 </p>
<p>对系统的影响:<br />
你的权衡 </p>
<p>症状: </p>
<p>经过多日或数周的时间才能过渡到成熟系统 <br />
风险存在与过渡期，带有很多不确定性，有些主要的功能场景没有被测试到 <br />
实际系统中的数据和开发、测试中的数据不同 <br />
无法在开发者机器上进行组建 <br />
应用行为在开发、稳定化及产品环境中各不相同 <br />
规避方案:<br />
解决之道是忠实地在开发环境中配置实际的环境，让开发所用环境接近于要实施产品的环境。如果未来环境是JDK 1.2.2及Solaris 7，那么不要在JDK 1.3及Red Hat Linux上进行开发。对于所用的应用服务器也是如此。同样，要快速地看一下产品数据库中的数据，并将这样的数据用于测试。不要依赖于人工创建的数据。如果产品数据很敏感，则要使之变得不敏感，然后把它配置起来。开发中未能预期到的产品数据将对以下过程产生破坏： </p>
<p>数据检验规则 <br />
系统测试行为 <br />
系统组件构建(特别地包括：EJB-EJB以及EJB-数据库) <br />
最为糟糕的是，这样还可能产生异常、空指针，以及你从没见过的问题。 </p>
<p>备注:<br />
开发人员常把安全性问题放到稳定化阶段才开始解决。要防止这样的陷阱产生，你也可以花费同样多的时间在业务逻辑中改进安全性。 </p>
<p>成熟期是一个复杂的过程，其中充满了技术性问题和非技术性问题。你可能会陷于想不到的一大堆问题中，这就是成熟化所意味的一切。开发及稳定化环境过程为你提供了制造更多这样的问题，以及发现这样的问题的地方，不断去做，就可以大大减少风险。 </p>
<p>你做的工程越多，你就越能了解什么是可行的，什么是不可行的。你可以对工程问题进行记录，以避免同样的错误重复发生。</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<p><strong>风险5: 选择了错误的提供商</strong><br />
项目阶段：<br />
提供商选择 </p>
<p>影响阶段：<br />
设计、开发、稳定化/负载测试，成熟化 </p>
<p>对系统的影响:<br />
可伸缩性、性能、可维护性及稳定性 </p>
<p>症状:</p>
<p>开发人员要使用更多的时间来处理工具方面的问题，而不是很有成效地使用这些工具 <br />
为了应付已知的和未知的问题，而不得不进行显著的系统重新设计 <br />
在不同的工具之间很难进行集成（应用服务器与IDE工具，IDE工具与调试器，源码控制与合成工具，等等） <br />
对于IDE工具和调试器等，开发人员往往排斥它们，而推崇自己所喜欢的工具 <br />
规避方案:<br />
为了避免风险5，你需要一个很好的提供商选择过程，风险10的规避也适用于此。 </p>
<p>要真正衡量一种IDE工具是否最合适的方法是真正地进行使用。而唯一来评估一种J2EE应用的方法是建立一种概念试验来进行证明，在试验中要包含你的应用框架。事实上，你也不希望在花费了3个月时间进行了培训和开发后，在使用时又发现一些bug。 </p>
<p>假设在开发到一半的时候，突然发现你的工具集有问题，那么你早应该知道，有些工具确实比另一些更重要。如果你所选的应用服务器不能充分满足你的需要，你只好修改原先的设定。如果IDE不好，则需要设置最低限度的代码标准，并让开发人员任意选择他们认为最为有效的工具。 </p>
<p>备注:<br />
要真正了解到哪一个供应商对一项特殊的任务来说最合适，其实并不是一件一次性决定的事情。你需要不断地跟踪与评估这个市场。例如，在过去的一年里我用过4种不同的IDE工具，这取决于我使用了什么样的应用服务器、平台，是否使用EJB等。</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
<strong><br />
风险6: 不了解你的提供商</strong><br />
项目阶段：<br />
提供商选择 </p>
<p>影响阶段:<br />
提供商选择阶段后面的所有阶段：设计、开发、稳定化/负载测试、成熟化 </p>
<p>对系统的影响:<br />
可维护性、可伸缩性、性能 </p>
<p>症状: </p>
<p>开发所用周期超过了最坏预测的周期1/3以上 <br />
提供商已经提供了某项功能，但开发者在不知道的情况下重新进行了该项功能的开发 <br />
规避方案:<br />
为了规避这样的风险，你可以尽可能地订阅提供商的网上资源，例如邮件列表、新闻组、版本信息（尤其是其中的bug修复补丁的说明等），你能从中得到无法估量之多的收获。 </p>
<p>一旦你已经选定了提供商，那么立即就要投资进行培训，并且尽可能赶在项目启动以前。然后，逐渐在团队中建立起对此提供商的认识及信任。试着建立几个EJB并部署一下，再用你的表示层技术 (Swing GUI, JSP等)来调用它们。如果你既要搭建开发环境，又要同时在实现项目目标，就会产生一些不必要的冲突。实际上，我也见到过一直没有进行构建过程的情况：“我们没有时间。”因此，这些工作必须提早进行。有些人会说：“我们的计划中没有为我们提供这些时间。”我的回答是：“你的计划中并没有不给你时间使你不这么做啊。” </p>
<p>备注:<br />
在J2EE世界里，各提供商产品的技术兼容性究竟如何？让我们看一下IBM和BEA的具体分析吧。两者都分别在各自的应用服务器中支持EJB 1.1。那么，实际上BEA WebLogic 5.1和IBM WebSphere 3.5究竟有多少相似之处呢? </p>
<p>BEA WebLogic和IBM WebSphere的系统配置和管理方式几乎完全不同。 <br />
IBM在WebSphere中采用了全面的GUI环境，而与之相对的是，BEA 在WebLogic中提供一整套命令行。 <br />
IBM WebSphere使用IIOP来和CORBA异常进行通讯，这些异常对程序员来说是可见的；WebLogic根本没有CORBA构造，而缺省使用t3协议。 <br />
WebSphere和Visual Age衔接紧密，而WebLogic是IDE无关的，实际上，你几乎可以使用任何的开发工具。 <br />
由此可见，差异还是相当多。如果你是一种应用服务器的专家，并不意味着你就是所有应用服务器的专家。这种区别体现在IDE，debugger，build工具，配置管理等等方面。具备某提供商的某项特殊工具的使用经验，可以在评估该提供商的竞争对手产品时具有一些便利。但是，不要奢望在不同产品之间进行无缝的转移或衔接。因此，你不得不花费足够多的时间在熟练掌握这些工具上。</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<p><strong>风险7: 设计中没有充分考虑到可伸缩性和产品性能</strong><br />
项目阶段：<br />
设计 </p>
<p>受影响的项目阶段:<br />
开发、负载测试及成熟化 </p>
<p>对系统的影响:<br />
可伸缩性、性能、可维护性 </p>
<p>症状: </p>
<p>无法忍受的速度缓慢 <br />
系统给服务器端增加的沉重负担，而无法利用到一些聚簇技术。 <br />
规避方案:<br />
把精力集中于性能和可伸缩性方面的需求，明确开发中要达到的性能指标。如果你需要每秒50个事务，而你的EJB设计只能提供40个，那么你就需要考虑替代方案，诸如存储过程，批处理，或者重新考虑OLTP的设计。 </p>
<p>尽可能让你的提供商加入进来，他们应该非常清楚其产品的强项和弱处在哪里，然后给你提供最直接的帮助。 </p>
<p>备注:<br />
本风险与风险2 (over-engineering)似乎有些冲突。实际上，两者相互影响。 我对风险2给出的解决方案是，只在绝对必要的情况下才进行构建。而对与性能和可伸缩性，你要预先划分好什么是必须要做的。 </p>
<p>如果你实现就识别出系统需要非常强的可伸缩性，并把它作为一个比较关键的需求，那么你首先需要选择一个带有很强的簇支持及事务型缓存的应用服务器。另外，你应把业务对象设计为EJB，从而可以充分利用服务器架构的优势。 XP也没有问题，你仍然是只做绝对必要的工作。 </p>
<p>我把这样的观点看作是一种检查和平衡的方法。我们只需要最简单可能性的系统，该系统只提供客户所需要的功能与行为即可。</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<p><strong>风险8: 陈旧的开发过程</strong><br />
项目阶段：<br />
开发 </p>
<p>影响阶段:<br />
稳定化，成熟化 </p>
<p>对系统的影响:<br />
可维护性、代码质量 </p>
<p>症状: </p>
<p>项目计划看上去似乎类似于瀑布模型: “首先草构设计，然后在一个很长的周期里进行开发。” <br />
由于不存在构建（build）过程，每次构建都象是噩梦 <br />
构建的日期等于损失开发的日期，因为什么也没有做成 <br />
在集成以前组件没有分别被充分地测试过，而集成测试意味着将2个不稳定的组件放在一起，然后查看堆栈里的跟踪结果。 <br />
规避方案:<br />
好的软件方法学将提高你的软件生命期。此前我已经提到XP方法，你可以在网上找到很多这方面的资料。 </p>
<p>备注:<br />
JUnit可以用来进行单元测试，Ant工具可以进行编译与构建，这2种工具都对XP方法有很好的支持。</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<p><strong>风险9: 没有好的架构方式</strong><br />
项目阶段：<br />
开发 </p>
<p>影响阶段:<br />
开发、稳定化、成熟期 </p>
<p>对系统的影响：<br />
可维护性、可伸缩性、代码质量 </p>
<p>症状: </p>
<p>在代码中使用了很多次的核心库中发现Bug。 <br />
没有建立日志标准 &#8211; 于是系统的输出很难读取或者解析。 <br />
不良的不一致的异常处理。在有些站点中我们甚至可以看到，出错信息直接暴露给了最终用户，例如在用户在他的购物车核帐时发送一条SQLException堆栈跟踪信息，用户接着会怎么做？打电话给数据库管理员要求对primary key约束进行修补吗？ <br />
以下任务已经被开发者以各种方式处理了无数次了，这些都有必要放在任何构架设计的第一批目标中。　 </p>
<p>日志 <br />
异常处理 <br />
与资源的连接(数据库，名字服务等) <br />
构建JSP页 <br />
数据合法性检查 <br />
规避方案:<br />
我是一个轻方法学的信徒和实践者。我在JavaWorld 上的第一篇文章 &#8211; &#8221;Frameworks Save the Day&#8221; &#8211; 就是研讨在企业Java环境中的架构。即使你已经开始开发了，此时考虑一下架构仍然是值得的。可能你不得不忍受一下重构带来的异常处理和日志处理，但从长远来看还是值得的，这样即省时间又省钱。 </p>
<p>备注:<br />
让我们想一下在构架中基于组件开发的可重用性的不同等级。第一级别是plumbing，具有0.9以上的可重用比例，也就是说，有90%的项目可以对它重复利用。 服务定义得越详细，重用比例就越低。换句话说，我需要构建一个会计服务，但要提供这些资源与用法的管理，以便于其它50%项目中可以对它们进行重复利用。但是对那些项目来说，能得到这些资源，那真是太好了！</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
<strong><br />
风险10: 项目计划和设计基于市场效应，而脱离了技术现实 </strong></p>
<p>备注: 不断有新人加入到Java/EJB的开发领域中来，不理解Java的人数一般比想象中还要多。 </p>
<p>项目阶段：<br />
所有阶段都会受到影响，包括提供商的选择 </p>
<p>影响阶段:<br />
所有阶段都会受到影响 </p>
<p>对系统的影响:<br />
可维护性、可扩展性、设计质量、代码质量 </p>
<p>症状: </p>
<p>轻率地进行技术决策，认为EJB只是为了便携式处理的方便 <br />
选择提供商的时候没有随即进行产品的试用 <br />
在项目的生命周期内还需要更换工具 <br />
规避方案:<br />
不要轻易相信项目外部的任何人的看法，这些人可能已经有一些既得利益，不要相信提供商的说法（除非你早已经了解），也不要相信白皮书。如果你要取得来自真实世界的关于应用服务器的建议，可以在网上取得。你还可以下载这些工具进行评估，用它们做一些原型，并运行一下其中的样例。(好的提供商都有这样的样例)。 </p>
<p>总的来说，为你的项目选择最好的提供商及工具需要时间，而你可能没有太多的时间。你可以把选择范围限制在3-4个对象，然后用一周时间进行比较和检验。最后从中选出比较满意的工具和产品。 </p>
<p>备注:<br />
如果你缺少J2EE经验，则可能会在项目前期就产生问题。在前期所确定的决策会影响整个过程，并进而影响项目的成功。好的J2EE咨询专家将能够帮助你选择好的提供商，并为设计和开发刻划出一个好的构形。</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<p>仅仅只有这10项风险吗？</p>
<p>10只是一个特定的数字，显然，还有更多更多的风险会存在。只是我可以保证的是，如果你克服了所列的各项风险，那么你的项目会有出色的表现并已打好了成功的基础。 </p>
<p>还有一项需要注意，即没有任何东西可以代替经验和计划。如果你没有经验，那么一定要想办法取得并积累。千万不要一边做项目一边进行培训。在开发之前要预先做好充分的准备，最好是在设计以前就进行准备。可以让你的团队接受Java/J2EE顾问的指导，并确保这样的指导能够传递到整个其他的团队成员。 </p>
<p>最后，还有必要提到以下几点： </p>
<p>软件工程的外界影响 <br />
什么时候进行单元测试，什么时候进行集成测试？ <br />
设计模式 <br />
异常处理 <br />
结论<br />
总的说来，以上10大风险是你在企业级Java项目开发过程中将面对的主要困难。我也相信在你的旅程中一定还有更多的陷阱，但我比较确信的是我所提到的风险已经涵盖了主要的问题。最后让我们按照优先级重新列举一下10大风险：　 </p>
<p>没有真正理解Java, 没有真正理解EJB, 没有真正理解J2EE <br />
过度设计(Over-engineering)  <br />
没有将业务规则和逻辑表现形式相分离 <br />
没有在开发环境中进行适当的配置 <br />
选择了错误的提供商 <br />
不了解你的提供商 <br />
设计中没有充分考虑到可伸缩性和产品性能 <br />
陈旧的开发过程 <br />
没有好的架构方式 <br />
项目计划和设计基于市场效应，而脱离了技术现实 <br />
最后，让我祝你好运！　</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<p>译后记：</p>
<p>我基本上没有做过J2EE项目，但仍有足够勇气翻译这样的文章。在国内软件公司里，极端情况下也许到处都是风险，这样也就无所谓风险了。对于选择J2EE技术路线，自然会有J2EE特有的风险，因此本文中的风险往往也是特别针对J2EE项目的。另外，对于J2EE项目，我们不应该忽视的一点是，其技术上的风险会更大一些。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.liulang.cq.cn/20090811/j2ee10risk.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Velocity学习笔记</title>
		<link>http://www.liulang.cq.cn/20090810/velocitybak.html</link>
		<comments>http://www.liulang.cq.cn/20090810/velocitybak.html#comments</comments>
		<pubDate>Mon, 10 Aug 2009 14:55:08 +0000</pubDate>
		<dc:creator>刘 浪</dc:creator>
				<category><![CDATA[J2EE]]></category>

		<guid isPermaLink="false">http://www.liulang.cq.cn/?p=98</guid>
		<description><![CDATA[1.Velocity:是一个模板语言的引擎，可以对模板进行解析和处理，然后输出结果。
2.Velocity使用的模板语言为Velocity Template Language(VTL)

3.单行注释：##
   多行注释：#*  *#
4.VTL中有三种类型的引用：变量，属性，方法，所有的引用都将被做为String对象进行处理。
5.变量： $作为起始符，第一个字符必须是字母
  属性：$作为起始符，后跟一个&#8221;.&#8221;和另一个VTL标识符
          $customer.Address
          可以表示查找Hashtable对象customer中以Address为关键字的值
          也可以表示customer对象的getAddress()方法
  方法：方法是被定义在Java中的一段代码
          $customer.getAddress()
  以上的都是简化写法，正式写法为${customer.getAddress}
6.当Velocity遇到没有定义的引用的时候，通常会将引用本身输出到页面上。
    如果还没初始化，可以使用隐藏写法达到目的$!email
7.#set($email=&#8221;foo&#8221;)
8.指令
    #set：用于设置一个引用的值
    条件判断语句：#if/#elseif/#else/#end
    关系运算符：==
    逻辑运算符：AND OR NOT
    循环：foreach
9.#include指令：允许引入本地文件，被引入文件的内容将不会通过模板引擎被翻译，本地文件只能在        template_root目录下。
    #include(&#8220;one.txt&#8221;)
10.#parse指令：允许引用一个包含VTL的本地文件，Velocity将解析其中的VTL并插入到模板中。
    #parse(&#8220;me.vm&#8221;)
11.#stop指令：允许停止执行模板引擎并返回，用户模板的调试过程。
12.#macro指令：允许定义一段可重用的VTL模板，也叫做宏。
    #macro(d)
      &#60;tr&#62;&#60;td&#62;&#60;/td&#62;&#60;/tr&#62;
    #end
     使用#d()可以调用宏
]]></description>
			<content:encoded><![CDATA[<p>1.Velocity:是一个模板语言的引擎，可以对模板进行解析和处理，然后输出结果。<br />
2.Velocity使用的模板语言为Velocity Template Language(VTL)</p>
<p><span id="more-98"></span><br />
3.单行注释：##<br />
   多行注释：#*  *#<br />
4.VTL中有三种类型的引用：变量，属性，方法，所有的引用都将被做为String对象进行处理。<br />
5.变量： $作为起始符，第一个字符必须是字母<br />
  属性：$作为起始符，后跟一个&#8221;.&#8221;和另一个VTL标识符<br />
          $customer.Address<br />
          可以表示查找Hashtable对象customer中以Address为关键字的值<br />
          也可以表示customer对象的getAddress()方法<br />
  方法：方法是被定义在Java中的一段代码<br />
          $customer.getAddress()<br />
  以上的都是简化写法，正式写法为${customer.getAddress}<br />
6.当Velocity遇到没有定义的引用的时候，通常会将引用本身输出到页面上。<br />
    如果还没初始化，可以使用隐藏写法达到目的$!email<br />
7.#set($email=&#8221;foo&#8221;)<br />
8.指令<br />
    #set：用于设置一个引用的值<br />
    条件判断语句：#if/#elseif/#else/#end<br />
    关系运算符：==<br />
    逻辑运算符：AND OR NOT<br />
    循环：foreach<br />
9.#include指令：允许引入本地文件，被引入文件的内容将不会通过模板引擎被翻译，本地文件只能在        template_root目录下。<br />
    #include(&#8220;one.txt&#8221;)<br />
10.#parse指令：允许引用一个包含VTL的本地文件，Velocity将解析其中的VTL并插入到模板中。<br />
    #parse(&#8220;me.vm&#8221;)<br />
11.#stop指令：允许停止执行模板引擎并返回，用户模板的调试过程。<br />
12.#macro指令：允许定义一段可重用的VTL模板，也叫做宏。<br />
    #macro(d)<br />
      &lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;<br />
    #end<br />
     使用#d()可以调用宏</p>
]]></content:encoded>
			<wfw:commentRss>http://www.liulang.cq.cn/20090810/velocitybak.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Struts与Velocity 的集成</title>
		<link>http://www.liulang.cq.cn/20090810/strutsandvelocity.html</link>
		<comments>http://www.liulang.cq.cn/20090810/strutsandvelocity.html#comments</comments>
		<pubDate>Mon, 10 Aug 2009 14:44:11 +0000</pubDate>
		<dc:creator>刘 浪</dc:creator>
				<category><![CDATA[J2EE]]></category>

		<guid isPermaLink="false">http://www.liulang.cq.cn/?p=93</guid>
		<description><![CDATA[Velocity 的五步把 Struts 与 Velocity 模板引擎组合起来很简单，也很直接；实际上，只要用五步就可以实现：

把 Velocity JAR 放在类路径中。
修改 web.xml 文件让它识别 Velocity servlet。
把 Velocity toolbox.xml 放在应用程序的 WEB-INF 目录下。
修改 struts-config，把它的视图指向 Velocity 模板而不是 JSP。
为每个需要显示的页面创建 Velocity 模板。


我将用一个熟悉的搜索用例来演示 Struts 与 Velocity 的集成。
在这个示例中，一个简单的应用程序允许用户按照图书的 ISBN 编号搜索图书。应用程序的结果页面显示与 ISBN 编号匹配的图书。








放弃 Struts 标记 —— 不！
现在，您可能会想，是不是需要放弃那些过去让您节约了许多编码时间的很好的 Struts 标记。如果不使用 JSP，那么肯定没有使用 Struts 的 JSP 标记！幸运的是，您可以使用 Velocity 工具。Velocity 的 Struts 工具提供了所有您熟悉的 Struts 方便特性，但是添加了 Velocity 的灵活性。







第 1 步：把 Velocity [...]]]></description>
			<content:encoded><![CDATA[<p><a name="N100AA"><span>Velocity 的五步</span></a>把 Struts 与 Velocity 模板引擎组合起来很简单，也很直接；实际上，只要用五步就可以实现：</p>
<ol>
<li>把 Velocity JAR 放在类路径中。</li>
<li>修改 web.xml 文件让它识别 Velocity servlet。</li>
<li>把 Velocity toolbox.xml 放在应用程序的 WEB-INF 目录下。</li>
<li>修改 struts-config，把它的视图指向 Velocity 模板而不是 JSP。</li>
<li>为每个需要显示的页面创建 Velocity 模板。</li>
</ol>
<p><span id="more-93"></span></p>
<p>我将用一个熟悉的搜索用例来演示 Struts 与 Velocity 的集成。</p>
<p>在这个示例中，一个简单的应用程序允许用户按照图书的 ISBN 编号搜索图书。应用程序的结果页面显示与 ISBN 编号匹配的图书。</p>
<table border="0" cellspacing="0" cellpadding="0" width="40%" align="right">
<tbody>
<tr>
<td width="10"><img src="http://www.ibm.com/i/c.gif" alt="" width="10" height="1" /></td>
<td>
<table border="1" cellspacing="0" cellpadding="5" width="100%">
<tbody>
<tr>
<td bgcolor="#eeeeee"><a name="N100CB"><strong>放弃 Struts 标记 —— 不！</strong></a><br />
现在，您可能会想，是不是需要放弃那些过去让您节约了许多编码时间的很好的 Struts 标记。如果不使用 JSP，那么肯定没有使用 Struts 的 JSP 标记！幸运的是，您可以使用 Velocity 工具。Velocity 的 Struts 工具提供了所有您熟悉的 Struts 方便特性，但是添加了 Velocity 的灵活性。</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<p><a name="N100D5"><span>第 1 步：把 Velocity JAR 放在 WEB-INF/lib 下</span></a></p>
<p>如果您还没下载 Velocity，那么现在需要下载它。Velocity 本身是很棒的，但是它的工具包可以帮助您把工作做得更好更快。特</p>
<p>别是 Struts 工具模拟了您以前熟悉的 Struts 标记。请参阅 <a href="http://www.ibm.com/developerworks/cn/java/j-sr1.html#download">下载</a> 一节下载 Velocity 模板引擎和 Velocity 工具。</p>
<p>请注意不同时候，需要的 jar 也会略有不同。在这里我不想列出一个 JAR 列表，只是想建议您访问 Velocity 的主页（请参阅 <a href="http://www.ibm.com/developerworks/cn/java/j-sr1.html#resources">参考资料</a>）并阅读那里的安装指南。一旦得到了需要的 JAR，只需把它们放在 WEB-INF\lib 下面即可。</p>
<p><a name="N100EA"><span>第 2 步：修改 web.xml，让它识别 Velocity 的 servlet</span></a></p>
<p>下一步是修改 Struts 的 web.xml 文件，让它识别 Velocity 的 servlet 并把所有以 <em>.vm</em> 结尾的资源请求定向到 Velocity servlet，如清单 1 所示。<br />
<a name="N100FB"><strong>清单 1. 修改 web.xml，声明 Velocity servlet</strong></a></p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<pre>&lt;servlet&gt;
  &lt;servlet-name&gt;velocity&lt;/servlet-name&gt; <span>|(1)</span>
  &lt;servlet-class&gt; <span>|(2)</span>
         org.apache.velocity.tools.view.servlet.VelocityViewServlet
  &lt;/servlet-class&gt;
  &lt;init-param&gt; <span>|(3)</span>
    &lt;param-name&gt;org.apache.velocity.toolbox&lt;/param-name&gt;
    &lt;param-value&gt;/WEB-INF/toolbox.xml&lt;/param-value&gt;
 &lt;/init-param&gt;
 &lt;load-on-startup&gt;10&lt;/load-on-startup&gt; <span>|(4)</span>
&lt;/servlet&gt;
&lt;!-- Map *.vm files to Velocity --&gt;
&lt;servlet-mapping&gt; <span>|(5)</span>
  &lt;servlet-name&gt;velocity&lt;/servlet-name&gt;
  &lt;url-pattern&gt;*.vm&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;</pre>
</tr>
</tbody>
</table>
<p> </p>
<p>让我们来看看清单 1 中发生了什么：</p>
<ul>
<li>（1）声明了 Velocity servlet 并给了它一个 <em>velocity</em> 句柄。</li>
<li>（2）声明了 Velocity servlet 的类名。</li>
</ul>
<p>Velocity servlet 接受“toolbox”参数。toolbox 是声明应用程序的可用工具的位置。因此，在清单 1 中，我还做了以下工作：</p>
<ul>
<li>（3）告诉 <code>VelocityServlet</code> 在哪里可以找到 toolbox 的配置。</li>
<li>（4）设置了 <code>load-on-startup</code> 标记，确保在正确的时间装入 Velocity servlet。任何大于或等于 0 的值都会迫使容器通过调用 servlet 的 <code>init()</code> 方法来装入它。放在 <code>load-on-startup</code> 标记体中的值决定了不同的 servlet 的 <code>init</code> 方法调用的次序。例如，0 在 1 之前调用，而 1 在 2 之前调用。缺少的标记或负值允许 servlet 容器根据自己的选择装入 servlet。</li>
<li>（5）声明了 servlet 映射，强迫所有用 <em>.vm</em> 结尾的资源请求定向到 Velocity servlet。请注意（5）中的&lt;servlet-name&gt; 必须与（1）中的&lt;servlet-name&gt; 匹配。交错的声明和映射会在日志中生成错误。</li>
</ul>
<p><a name="N1014E"><span>第 3 步：把 toolbox.xml 放在 WEB-INF 下</span></a></p>
<p>利用 Velocity，可以使用（或创建）包含许多工具的工具箱。用来登记类的工具箱中包含有用的函数，常常会用到。幸运的是，Velocity 提供了许多预先构建好的工具。还创建了许多 Struts 工具来模拟原始的 Struts 标记。如果发现需要构建自己的工具，也可以自由地构建。在清单 2 中显示的 toolbox.xml 可以在 Velocity 工具下载中找到。这个文件应当随 Velocity JAR 一起放在 WEB-INF 下。<br />
<a name="N1015C"><strong>清单 2. toolbox.xml</strong></a></p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<pre>&lt;?xml version="1.0"?&gt;
&lt;toolbox&gt;
  &lt;tool&gt;
     &lt;key&gt;link&lt;/key&gt;
     &lt;scope&gt;request&lt;/scope&gt;
     &lt;class&gt;
       org.apache.velocity.tools.struts.StrutsLinkTool
     &lt;/class&gt;
  &lt;/tool&gt;
  &lt;tool&gt;
     &lt;key&gt;msg&lt;/key&gt;
     &lt;scope&gt;request&lt;/scope&gt;
     &lt;class&gt;
       org.apache.velocity.tools.struts.MessageTool
     &lt;/class&gt;
  &lt;/tool&gt;
  &lt;tool&gt;
     &lt;key&gt;errors&lt;/key&gt;
     &lt;scope&gt;request&lt;/scope&gt;
     &lt;class&gt;
       org.apache.velocity.tools.struts.ErrorsTool
     &lt;/class&gt;
  &lt;/tool&gt;
  &lt;tool&gt;
     &lt;key&gt;form&lt;/key&gt;
     &lt;scope&gt;request&lt;/scope&gt;
     &lt;class&gt;
       org.apache.velocity.tools.struts.FormTool
     &lt;/class&gt;
  &lt;/tool&gt;
  &lt;tool&gt;
     &lt;key&gt;tiles&lt;/key&gt;
     &lt;scope&gt;request&lt;/scope&gt;
     &lt;class&gt;
       org.apache.velocity.tools.struts.TilesTool
     &lt;/class&gt;
  &lt;/tool&gt;
  &lt;tool&gt;
     &lt;key&gt;validator&lt;/key&gt;
     &lt;scope&gt;request&lt;/scope&gt;
     &lt;class&gt;
       org.apache.velocity.tools.struts.ValidatorTool
     &lt;/class&gt;
  &lt;/tool&gt;
&lt;/toolbox&gt;</pre>
</tr>
</tbody>
</table>
<p> </p>
<p><a name="N10163"><span>第 4 步：修改 struts-config</span></a></p>
<p>下一步是修改 struts-config.xml，指向 Velocity 视图而不是 JSP。新的配置文件如清单 3 所示。<br />
<a name="N10171"><strong>清单 3. 针对 Velocity 视图修改后的 struts-config.xml </strong></a></p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<pre>&lt;?xml version="1.0" encoding="ISO-8859-1" ?&gt;
&lt;!DOCTYPE struts-config PUBLIC
          "-//Apache Software Foundation//DTD Struts Configuration 1.0//EN"
          "http://jakarta.apache.org/struts/dtds/struts-config_1_0.dtd"&gt;
&lt;struts-config&gt;
    &lt;form-beans&gt;
        &lt;form-bean name="searchForm" type="app.SearchForm"/&gt;
    &lt;/form-beans&gt;
    &lt;global-forwards&gt;
        &lt;forward name="welcome" path="/welcome.do"/&gt;
    &lt;/global-forwards&gt;

   &lt;action-mappings&gt;
        &lt;action
            path="/welcome"
            type="org.apache.struts.actions.ForwardAction"
            parameter="/pages/search.vm"/&gt; <span>|(1)</span>
        &lt;action
            path="/search"
            type="app.SearchAction"
            name="searchForm"
            scope="request"
            input="/pages/search.vm"&gt; <span>|(2)</span>
            &lt;forward name="success"
              path="/pages/results.vm"/&gt; <span>|(3)</span>
        &lt;/action&gt;
    &lt;/action-mappings&gt;
&lt;/struts-config&gt;</pre>
</tr>
</tbody>
</table>
<p> </p>
<p>清单 3 看起来就像一个非常典型的 Struts 应用程序，只有一个小小的不同。响应没有把客户转向到 JSP，而直接转向到 <em>.vm</em> 文件（请参阅清单 3 中的引用 1、2 和 3）。在大多数情况下，把 Struts 应用程序从 JSP 迁移到 Velocity 视图，需要做的仅仅是全局搜索，把 <em>.jsp</em> 替换成 <em>.vm</em>。其他所有东西都可以保持不变！模板可以同样保存在以前保存 JSP 的位置；所需要做的只是用 Velocity 命令代替 JSP 标记。</p>
<p><a name="N1018D"><span>第 5 步：创建 Velocity 模板</span></a></p>
<p>在清单 4 中，可以看到示例应用程序搜索页面的 Velocity 模板。<br />
<a name="N1019B"><strong>清单 4. 搜索页面的 Velocity 模板</strong></a></p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<pre>&lt;HTML&gt;
  &lt;HEAD&gt;
    &lt;TITLE&gt;Search&lt;/TITLE&gt;
  &lt;/HEAD&gt;
  &lt;BODY&gt;
    $!errors.msgs()|<span>|(1)</span>
    &lt;FORM method="POST"
      action="$link.setAction('/search')"&gt; <span>|(2)</span>
      &lt;h2&gt;Book Search&lt;/h2&gt;
      ISBN:&lt;INPUT type="text" name="isbn"&gt;
      &lt;INPUT type="submit" value="Submit" name="submit"&gt;
    &lt;/FORM&gt;
  &lt;/BODY&gt;
&lt;/HTML&gt;</pre>
</tr>
</tbody>
</table>
<p> </p>
<p>清单 4 是一个没有 JSP 或 Struts 标记的典型的 HTML 页面。但是，以下元素看起来可能不是那么熟悉：</p>
<ul>
<li>（1）用 <code>$!errors.msgs()</code> 得到错误消息队列中的错误消息。</li>
<li>（2）用 <code>$link.setAction('/search')</code> 获得搜索转发的 URL。</li>
</ul>
<p>这就成功了 —— 模板剩下的部分看起来几乎与以前熟悉的 HTML 文件相同。清单 5 显示了应用程序结果页面的模板。<br />
<a name="N101C3"><strong>清单 5. 结果页面的 Velocity 模板</strong></a></p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<pre>&lt;html&gt;
  &lt;body&gt;
  &lt;h1&gt;Book Details&lt;/h1&gt;
  &lt;a href="$link.setForward("searchEntry")"&gt;Search again&lt;/a&gt; <span>|(1)</span>
  &lt;h3&gt;$book.title&lt;/h3&gt; <span>|(2)</span>
    &lt;b&gt;ISBN:&lt;/b&gt;$book.isbn&lt;br&gt;<span>|(3)</span>
    &lt;b&gt;Title:&lt;/b&gt;$book.title&lt;br&gt;<span>|(4)</span>
    &lt;b&gt;Author:&lt;/b&gt;$book.author&lt;br&gt;<span>|(5)</span>
    &lt;b&gt;Price:&lt;/b&gt;$book.price&lt;br&gt;<span>|(6)</span>
    &lt;b&gt;No Pages:&lt;/b&gt;$book.pages&lt;br&gt;<span>|(7)</span>
    &lt;b&gt;Description:&lt;/b&gt;$book.description&lt;br&gt;<span>|(8)</span>
    &lt;b&gt;Publisher:&lt;/b&gt;$book.publisher&lt;br&gt;<span>|(9)</span>
  &lt;/body&gt;
&lt;html&gt;</pre>
</tr>
</tbody>
</table>
<p> </p>
<p>可以注意到，清单 5 中不包含 JSP 标记或 Struts 标记。我们来详细看看它：</p>
<ul>
<li>（1）用 Struts 的链接工具把 <code>&lt;a&gt;</code> 标记的 <em>href</em> 设置为 Struts 转发。</li>
<li>（2）访问 <code>$book title</code> 属性。</li>
<li>（3）访问 <code>$book isbn</code> 属性。</li>
<li>（4）再次访问 <code>$book title</code> 属性。</li>
<li>（5）访问 <code>$book author</code> 属性。</li>
<li>（6）访问 <code>$book price</code> 属性。</li>
<li>（7）访问 <code>$book pages</code> 属性。</li>
<li>（8）访问 <code>$book description</code> 属性。</li>
<li>（9）访问 <code>$book publisher</code> 属性。</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.liulang.cq.cn/20090810/strutsandvelocity.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
