<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>muRain</title>
    <description></description>
    <link>http://murain.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
      <item>
        <title>[转]ERP与SCM到底有什么区别</title>
        <author>muRain</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://murain.javaeye.com">muRain</a>&nbsp;
          链接：<a href="http://murain.javaeye.com/blog/50807" style="color:red;">http://murain.javaeye.com/blog/50807</a>&nbsp;
          发表时间: 2007年01月27日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p><font face="Verdana">SCM与ERP，这两个时髦名词背后，到底有何不同？ </font></p>
<p>从20世纪60年代起，企业就开始了管理信息化的应用，从MRP到ERP，逐步地实现了对采购、库存、生产、销售、财务和人力资源等业务的管理，<!-- cio 正文页网画中画广告  -->
<table cellspacing="0" border="0" align="left" cellpadding="0" style="MARGIN: 10px 7px 3px 4px">
    <tbody>
        <tr>
            <td>
            <div id="ad_position_roller_38">&nbsp;</div>
            </td>
        </tr>
    </tbody>
</table>
使其内部业务流程和处理实现了自动化，为企业内部纵向一体化管理创造了不可磨灭的功绩。但是，在经济全球化蓬勃发展的今天，ERP在供应链的跨企业横向一体化管理方面显得力不从心。因此，全球500强企业在经过若干年的ERP应用后又纷纷引入了SCM（供应链管理）。 </p>
<p>那么，ERP与SCM到底有什么区别？</p>
<p>一、在理论模型和方法上：SCM Vs ERP&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>多年来，人们一直在坚持不懈地将数学规划等方法用于企业安排计划和管理上，渐渐形成了MRP法和数学规划法这两种主要的计划编制方法，后者由于受到如建模困难、运算量大等客观条件的限制，发展比较迟缓，而MRP法则由于省略了一些实际存在、但又难以求解的因素，变得简单易算，获得了较好的成效和发展。但是，人们逐渐发现那些因素往往是不容忽视的，必须加以重视，加之计算机计算速度的飞速发展使得采用数学规划方法不再困难，人们开始以常驻计算机内存的方式采用数学规划方法编制计划，这种全新的计划方法就是SCM的的开端，人们发现这种方法无论在计划的准确性方面，还是编制速度方面以及优化性方面等都远远优于被ERP沿用至今的MRP法。&nbsp;&nbsp; </p>
<p>SCM采用了多种数学解析的优化模型和规则，是基于约束理论进行计划的，它考虑了物料、设备、人员、场所、时间和技术等所有的约束因素，对不同的目标可以通过不同的规制进行优化。它具有更坚实的理论基础和更科学的指导方法，作出的计划更具优化性、准确性和可行性。此外，SCM借助于各种模型和算法，能够对企业和供应链进行决策和优化。而ERP的理论模型过于简单和陈旧，它的计划模型和提前期的计算方法等都无法模拟今日复杂多变的业务过程。例如，它作计划的前提假设是企业具有无限的物料和能力，计划模型是无约束的；又如，ERP一般采用简单的线性公式T=A+BX计算产品的提前期，但每种物品每一次的采购提前期都可能存在较大差别，无法用这种简单公式来准确计算；而且由于缺少理论模型的支持，无法实现业务优化和科学决策。</p>
<p>二、在管理范围上：SCM Vs ERP </p>
<p>ERP是面向企业内部的管理，只能对其内部资源进行管理。然而，单靠企业内部的业务改进所获得的收效已变得越来越有限。随着经济全球化和市场竞争加剧，形成了产品定制化生产和交货期不断缩短的趋势，企业面对越来越复杂多变的经营环境，逐渐将管理焦点转移到超越企业之外的供应链管理和上下游的业务协同上，以适应环境的变化。而ERP在管理范围和功能上都不具备协调多个企业间资源的能力，无法实现供应链上信息的共享。然而，SCM则能够满足供应链横向一体化运作的要求，它帮助企业更好地参与新环境的竞争，在考虑了资源约束、优化和决策的技术支持下，有效利用和整合外部资源，与上下游企业建立合作伙伴关系以实现信息共享和业务集成、共同协调制定兼顾各方利益的联合计划，实现协同运作和供应链整体价值的最大化。&nbsp; </p>
<p>SCM另一个优于ERP之处是它能够模拟和改善财务指标，特别是收入、成本和资产利用率指标。它不仅仅是简单地降低成本，而是利用不同的方式来满足市场和客户的需求，使企业乃至整个供应链实现盈利最大化，它通过改善收益表和资产负债表中关键因素来实现股东权益最大化。因此，ERP仅仅是实现了单个的企业价值最大化，而SCM则实现了整个供应链乃至社会价值最大化。</p>
<p>三、在编制计划上：SCM Vs ERP&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>首先，SCM是基于某些规则进行计划的。它是通过不同的规则对不同业务进行计划，并可对单一目标和多目标进行优化的计划，它扩大了计划范围。而ERP则难以实现优化；其次，它的计划是并发的、计划时段是连续的，它综合、完整地考虑了约束问题，生成的提前期是弹性的，计划的能见度可达全球范围。它可以对供应链和企业的各项业务进行计划，一次性地考虑业务流程的纵向和横向的协调，无需一个个地依次制定计划。而ERP编制计划是按顺序进行的、计划时段是离散的，生成的提前期是固定的，仅面向某一功能的计划，计划的能见度只限于局部，几乎不考虑约束；再次，SCM的计划覆盖了所有的业务，计划模型可以做得足够的详细，覆盖了长、中、短周期，可以实现倒排、顺排和中间排，其精细程度可从年、月、周一直到天、小时和分钟。同时，SCM能够随时根据生产和客户需求的变化进行重排计划，量化地反映甚至超前于市场的需求，例如变化的资源和约束，用户的优先权等。它可以实现一个可持续转变的流程，使得重排计划能够对每一次意外变化进行随时处理。而ERP有时按天做计划都很困难，更无法精确到小时和分钟，也很难做到快速地重排计划；最后，在生成计划后，SCM可以根据&ldquo;评价计划成本&rdquo;标准评价计划成本，并与企业的财务指标进行对比和衡量，进一步核实其可行性。但ERP无法对做出的计划成本进行评价。</p>
<p>四、在业务管理上：SCM Vs ERP </p>
<p>SCM在业务管理上具有比ERP更好、更多的功能。首先，SCM具有极强的实时承诺性，它的承诺标准能为客户提供准确的交货日期。虽然ERP的可用量检查ATP也具有某种承诺能力，但仅是建立在对现有库存检查的基础上，而SCM在ATP的基础上，还通过对需求承诺能力和对订单承诺能力的检查、扩展的生产可用性检查和对获利能力的检查等功能，对包括供应商和服务商在内的资源进行动态地分析和物理检查，以对客户作出准确的交货承诺，并在商谈订单的第一时间就能确定该订单是否能够获利，是否需要接受订单；第二，SCM能对供应链上的资源进行优化调配，将供应链上的某种稀缺资源预先分派给具有较高优先级别的客户或渠道的需求，以避免其它客户或渠道与它们争夺该资源，实现了资源平衡和优化利用。而ERP不具备这种能力；第三，SCM的计划范围扩展到了企业之外，能生成跨企业的协同计划，实时了解伙伴们的业务变化情况，及时进行重排计划，保持高度的灵活性和预见性，以快速响应市场需求。而ERP则无法满足这种需求；第四，SCM可以动态计算提前期，它的提前期标准提供了一个优于ERP的特性，ERP逻辑使用固定的提前期进行计划，这对整个供应链运行具有若干负面的影响；第五，SCM可以对供应链的需求、供给和约束进行监控，实时地将这三者进行比较，一旦出现不匹配时立刻发出预警信号，并执行智能的逻辑操作使它们重新恢复平衡，重新达到同步，这也是ERP无法实现的；最后，与SCM相比，ERP缺少优化和决策支持、业务伙伴关系管理、上下游业务协同管理和物流管理等功能，无法实现供应链上企业间的协同运作和更有效地利用企业外部的资源。</p>
<p>因此，ERP只能告诉你怎样去执行，而SCM则能帮助你决定怎样去执行才能作出改善，如何通过基于利润考虑的优化的总价值？如何确定优化生产工序序列以最小化工序、设备整合和设置？供应商和客户的位置变化将怎样影响运输成本？增加或取消一个配送中心，或越库作业将如何影响运输成本？如何基于最低的运输成本来决定对多余的维修部件进行最佳的重新配置等，这些都是ERP无法解答、而必须让位给SCM来解决的。</p>
<p>五、在运行速度上：SCM Vs ERP </p>
<p>SCM采用了第4代常驻内存运行的技术，将所有相关的数据脱离主服务器读入内存，避免频繁访问<a href="http://wiki.ccw.com.cn/%E7%A1%AC%E7%9B%98" target="_blank"><font color="#0000cc"><strong>硬盘</strong></font></a>，这比基于MRP法的ERP运算速度要快几百倍，具有更快的运送与决策速度和响应能力，以尽快捕捉和把握市场商机，先人一筹抢占市场。同时，可视化的GUI技术支持先进的供应链导航功能，为企业提供可视的户图化形界面，将实际的供应链网络结构、供需关系和连接路线等实况尽显眼前，更是优与ERP系统，使管理人员能更容易地作出决策，实现盈利。</p>
<p>六、在事务处理上：SCM Vs ERP </p>
<p>虽然SCM具有众多的计划、优化和决策功能，但它却不具备某些事务处理和数据维护功能，例如，货物的接收、盘点与出库，工单/采购单发放、发票/文挡管理、会计管理，对项目主文件/BOM、列表进行维护等。而ERP则可以完成这些功能。目前，有些SCM系统也扩展了它的功能，增加了供应链执行系统SCE，由该系统来完成上述工作。 </p>
<p>综上所述，SCM更能满足新的经济环境和市场竞争需求，能为企业和整个供应链提供比ERP更多、更好地管理功能和手段去应付复杂多变的竞争环境，实现供应链上的共赢。这也正是为什么全球500强企业在经过若干年的ERP应用后又纷纷引入了SCM的原因。(amt)</p>
          <br/>
          <span style="color:red;">
            <a href="http://murain.javaeye.com/blog/50807#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 27 Jan 2007 12:44:40 +0800</pubDate>
        <link>http://murain.javaeye.com/blog/50807</link>
        <guid>http://murain.javaeye.com/blog/50807</guid>
      </item>
      <item>
        <title>[转]Spring XML配置的12个技巧</title>
        <author>muRain</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://murain.javaeye.com">muRain</a>&nbsp;
          链接：<a href="http://murain.javaeye.com/blog/50646" style="color:red;">http://murain.javaeye.com/blog/50646</a>&nbsp;
          发表时间: 2007年01月26日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>:<font face="Arial">http://www.evget.com/article/read_1722.aspx</font></p>
<p>Spring是一个强有力的java程序框架，其被广泛应用于java的程序中。它用POJO提供了企业级服务。Spring利用依赖注入可以获得简单而有效的测试能力。Spring beans，依赖关系，以及服务所需要的bean都将在配置文件中予以描述，配置文件一般采用XML格式。然而XML配置文件冗长而不易使用，在你进行一个使用了大量bean的大项目中它将变得难以阅读和控制。<br />
<br />
&nbsp;&nbsp; 在这篇文章中我将给你展示12种的有关Spring XML配置文件的最佳技巧。它们中的一些具有更多的实际意义，而不仅是最好的技巧。请注意另外一些因素，例如域模型的设计，会影响到XML配置，但是这篇文章更关注于XML配置的可读性和可操控性。<br />
<br />
<font color="#008000"><strong><br />
1． 避免使用自动装配</strong></font><br />
<br />
Spring可以通过bean类的自省来实现自动装配依赖，这样的话你就不必明确地描述bean的属性或者构造函数的参数。根据属性名称活匹配类型，bean属性可以自动进行装配。而构造函数可以根据匹配类型自动装配。你甚至可以设置自动装配进行自动侦测，这样Spring替你就会选择一个合适的机制。请看下面的例子：<br />
<br />
Spring可以通过bean类的自省来实现自动装配依赖，这样的话你就不必明确地描述bean的属性或者构造函数的参数。根据属性名称活匹配类型，bean属性可以自动进行装配。而构造函数可以根据匹配类型自动装配。你甚至可以设置自动装配进行自动侦测，这样Spring替你就会选择一个合适的机制。请看下面的例子：<br />
<br />
<br />
&lt;bean id=&quot;orderService&quot;<br />
<br />
class=&quot;com.lizjason.spring.OrderService&quot;<br />
<br />
autowire=&quot;byName&quot;/&gt;<br />
<br />
<br />
OrderService类的属性名被用来和容器中的一个bean实例进行匹配。自动装配会默默的保存一些类型信息并降低混乱。然而，由于它会牺牲掉这种配置的直观性和可维护性，你在实际的项目中将不会用到它。许多指南和陈述材料都把它吹捧为Spring的一个非常cool的特性，而没有提到它的这个缺点。依我之见，就像Spring的对象池一样，它更多了一些商业味道。它看起来好像可以使XML配置文件更精简一些，但实际上却增加其复杂性，尤其是在你的较大规模的工程中已经定义了很多bean的时候更是如此。Spring允许你混合使用自动和手动装配，但是这种矛盾会使XML配置更加的令人费解。<br />
<br />
<font color="#008000"><strong><br />
2． 使用命名规范</strong></font><br />
<br />
和Java编码的理念一样，在项目中始终用清晰的，描述性的，一致的命名规范对开发人员理解XML配置非常有用。拿bean ID举例来说，你可以遵循Java类中属性的命名规范。比如说，OrderServiceDAO的bean ID应该是orderServiceDAO。对于大项目来说，在bean ID前加包名来作为前缀。<br />
<br />
<font color="#008000"><strong><br />
3． 使用简化格式</strong></font><br />
<br />
简化格式有利于减少冗余，因为它把属性值和引用作为属性，而不是子元素。看下面的例子：<br />
<br />
&lt;bean id=&quot;orderService&quot;<br />
class=&quot;com.lizjason.spring.OrderService&quot;&gt;<br />
&lt;property name=&quot;companyName&quot;&gt;<br />
&lt;value&gt;lizjason&lt;/value&gt;<br />
&lt;/property&gt;<br />
&lt;constructor-arg&gt;<br />
&lt;ref bean=&quot;orderDAO&quot;&gt;<br />
&lt;/constructor-arg&gt;<br />
&lt;/bean&gt;<br />
以上程序可以重新以简化格式书写为：<br />
<br />
&lt;bean id=&quot;orderService&quot;<br />
class=&quot;com.lizjason.spring.OrderService&quot;&gt;<br />
&lt;property name=&quot;companyName&quot;<br />
value=&quot;lizjason&quot;/&gt;<br />
&lt;constructor-arg ref=&quot;orderDAO&quot;/&gt;<br />
&lt;/bean&gt;<br />
简化格式在1.2版本时已经可用了，但请注意不存在&lt;ref local=&quot;...&quot;&gt;这种简化格式不仅可以较少你的代码输入量，而且可以使XML配置更加的清晰。当你的配置文件中存在大量的bean定义时，它可以显著地提高可读性。<br />
<br />
<br />
<font color="#008000"><strong>4． 尽量使用type而不是index去解决构造函数参数的匹配问题</strong></font><br />
<br />
当构造函数中有多个同类型的参数时，Spring只允许你使用从0开始的index或者value标签来解决这个问题。请看下面的例子：<br />
<br />
&lt;bean id=&quot;billingService&quot;<br />
class=&quot;com.lizjason.spring.BillingService&quot;&gt;<br />
&lt;constructor-arg index=&quot;0&quot; value=&quot;lizjason&quot;/&gt;<br />
&lt;constructor-arg index=&quot;1&quot; value=&quot;100&quot;/&gt;<br />
&lt;/bean&gt;<br />
最好用type属性取代上面的做法：<br />
<br />
&lt;bean id=&quot;billingService&quot;<br />
class=&quot;com.lizjason.spring.BillingService&quot;&gt;<br />
&lt;constructor-arg type=&quot;java.lang.String&quot;<br />
value=&quot;lizjason&quot;/&gt;<br />
&lt;constructor-arg type=&quot;int&quot; value=&quot;100&quot;/&gt;<br />
&lt;/bean&gt;<br />
<br />
用index可以稍微减少冗余，但是它更容易出错且不如type属性可读性高。你应该仅在构造函数中有参数冲突时使用index。<br />
<br />
<br />
<font color="#008000"><strong>5． 如可能，尽量复用bean定义</strong></font><br />
<br />
Spring提供了一种类似于继承的机制来降低配置信息的重复并使XML配置更加的简单。一个子bean可以从它的父bean继承配置信息，本质上这个父bean就像它的子bean的一个模板。这是一个在大型项目中必须使用的特性。所有你要做的就是把父bean的abstract属性置为true，并在子bean中加以引用。例如：<br />
<br />
&lt;bean id=&quot;abstractService&quot; abstract=&quot;true&quot;<br />
class=&quot;com.lizjason.spring.AbstractService&quot;&gt;<br />
&lt;property name=&quot;companyName&quot;<br />
value=&quot;lizjason&quot;/&gt;<br />
&lt;/bean&gt;<br />
<br />
&lt;bean id=&quot;shippingService&quot;<br />
parent=&quot;abstractService&quot;<br />
class=&quot;com.lizjason.spring.ShippingService&quot;&gt;<br />
&lt;property name=&quot;shippedBy&quot; value=&quot;lizjason&quot;/&gt;<br />
&lt;/bean&gt;<br />
shippingService bean继承了abstractService bean的属性companyName的值lizjason。注意，如果你为bean声名一个class或工厂方法，这个bean将会默认为abstract<br />
<br />
<br />
<font color="#008000"><strong>6． 尽量使用ApplicationContext装配bean，而不是用import</strong></font><br />
<br />
像Ant脚本中imports一样，Spring的import 元素对于模块化bean的装配非常有用，例如：<br />
<br />
&lt;beans&gt;<br />
&lt;import resource=&quot;billingServices.xml&quot;/&gt;<br />
&lt;import resource=&quot;shippingServices.xml&quot;/&gt;<br />
&lt;bean id=&quot;orderService&quot;<br />
class=&quot;com.lizjason.spring.OrderService&quot;/&gt;<br />
&lt;beans&gt;<br />
<br />
然而，比起在XML中用imports预装配这些bean，利用ApplicationContext来配置它们将更加灵活，也可以使XML配置更加的易于管理。你可以像下面这样传递一个bean定义数组到ApplicationContext的构造函数中：<br />
<br />
String[] serviceResources =<br />
<br />
{&quot;orderServices.xml&quot;,<br />
<br />
&quot;billingServices.xml&quot;,<br />
<br />
&quot;shippingServices.xml&quot;};<br />
<br />
ApplicationContext orderServiceContext = new<br />
<br />
ClassPathXmlApplicationContext(serviceResources);<br />
<br />
<br />
<font color="#008000"><strong>7． 用id来标识bean</strong></font><br />
<br />
你可以用id或名字作为bean的标识。用id可读性较差，但是它可以影响XML分析器使bean的reference有效。如果id由于XML IDREF约束而无法使用，你可以用name作为bean的标识。XML IDREF约束是指id必须以字母开始(或者是在XML声名了的一个标点符号)，后面可以是字母，数字，连字符，下划线，冒号或full stops(不知道怎么翻译好)。在实际应用中很少会遇到XML IDREF约束问题。<br />
<br />
<br />
<font color="#008000"><strong>8． 在开发阶段使用依赖检查</strong></font><br />
<br />
你可以为bean的dependency-check属性设置一个值来取代默认的none，比如说simple，objects或者all，这样的话容器将替你做依赖有效性的检查。当一个bean的所有属性(或者某些属性目录)都被明确设置，或利用自动装配时将会非常有用。<br />
<br />
&lt;bean id=&quot;orderService&quot;<br />
class=&quot;com.lizjason.spring.OrderService&quot;<br />
dependency-check=&quot;objects&quot;&gt;<br />
&lt;property name=&quot;companyName&quot;<br />
value=&quot;lizjason&quot;/&gt;<br />
&lt;constructor-arg ref=&quot;orderDAO&quot;/&gt;<br />
&lt;/bean&gt;<br />
在这个例子中，容器将确保这些属性不是privitives或者保证collections是为orderService bean设置的。为所有的bean设置默认的依赖检查是可能的，但这个特性由于有些bean的属性不需要设置而很少使用。<br />
<br />
<br />
<font color="#008000"><strong>9． 为每个配置文件加一个描述注释</strong></font><br />
<br />
在XML配置文件中最好使用有描述性的id和name，而不是成堆的注释。另外，加一个文件描述头将会非常有用，这个描述可以概括文件中定义的bean。另一个选择，你可以在description元素中加入描述信息。例如：<br />
<br />
&lt;beans&gt;<br />
&lt;description&gt;<br />
This file defines billing service<br />
related beans and it depends on<br />
baseServices.xml,which provides<br />
service bean templates...<br />
&lt;/description&gt;<br />
...<br />
&lt;/beans&gt;<br />
用description元素的一个好处就是工具可以很容易的把描述信息从这个元素中提取出来。<br />
<br />
<br />
<font color="#008000"><strong>10． 和team members沟通变更</strong></font><br />
<br />
当你修改java源码后，要确保更改了配置文件中的相应部分并把这个情况告知你的team members。XML配置文件也是代码，它们是程序的重要组成部分，但它们很难阅读和维护。大多数时间里，你需要同时看XML配置文件和java代码才能知道是怎么回事。<br />
<br />
<br />
<font color="#008000"><strong>11． setter注入和构造函数注入，优先使用前者</strong></font><br />
<br />
Spring提供了三种注入方式：构造函数注入，setter注入和方法注入。一般我们使用前两种。<br />
<br />
&lt;bean id=&quot;orderService&quot;<br />
class=&quot;com.lizjason.spring.OrderService&quot;&gt;<br />
&lt;constructor-arg ref=&quot;orderDAO&quot;/&gt;<br />
&lt;/bean&gt;<br />
<br />
&lt;bean id=&quot;billingService&quot;<br />
class=&quot;com.lizjason.spring.BillingService&quot;&gt;<br />
&lt;property name=&quot;billingDAO&quot;<br />
ref=&quot;billingDAO&quot;&gt;<br />
&lt;/bean&gt;<br />
<br />
在这个例子中，orderService bean用了构造函数注入，而BillingService bean用了setter注入。构造函数注入可以确保bean正确地构建，但是setter注入更加的灵活和易于控制，特别是当class有多个属性并且它们中的一些是可选的情况是更是如此。<br />
<br />
<br />
<font color="#008000"><strong>12． 不要滥用注入</strong></font><br />
<br />
就像前面提到的，Spring的ApplicationContext可以替你创建java对象，但不是所有的java对象都应该通过注入创建。例如，域对象就不应该通过ApplicationContext创建。Spring是一个优秀的框架，但是考虑到可读性和可操控性，基于XML配置的配置会在定义很多bean的时候出现麻烦。过渡使用依赖注入将会使XML配置更加的复杂和冗长。切记，当使用高效的IDE时，例如Eclipse and IntelliJ，java代码更加的易于阅读，维护和管理比使XML文件<br />
<br />
<strong><br />
<font color="#008000">结论</font></strong><br />
<br />
XML是Spring流行的配置格式。存在大量bean定义时，基于XML的配置会变得冗长而不易使用。Spring提供了丰富的配置选项。适当地使用这些选项可以使XML配置更加的清晰，但其它的一些选项，例如自动装配，可能会降低可读性和可维护性。参考本文中提到的这些技巧可能会帮助你创建干净而易读的XML配置文件 <br />
</p>
          <br/>
          <span style="color:red;">
            <a href="http://murain.javaeye.com/blog/50646#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 26 Jan 2007 15:36:36 +0800</pubDate>
        <link>http://murain.javaeye.com/blog/50646</link>
        <guid>http://murain.javaeye.com/blog/50646</guid>
      </item>
      <item>
        <title>tomcat性能调整</title>
        <author>muRain</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://murain.javaeye.com">muRain</a>&nbsp;
          链接：<a href="http://murain.javaeye.com/blog/39522" style="color:red;">http://murain.javaeye.com/blog/39522</a>&nbsp;
          发表时间: 2006年12月18日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p><strong>一. 引言</strong></p>
<p>性能测试与分析是软件开发过程中介于架构和调整的一个广泛并比较不容易理解的领域，更是一项较为复杂的活动。就像下棋游戏一样，有效的性能测试和分析只能在一个良好的计划策略和具备了对不可预料事件的处理能力的条件下顺利地完成。一个下棋高手赢得比赛靠的不仅仅是对游戏规则的认识，更是靠他的自己的能力和不断地专注于分析自己对手的实力来更加有效地利用和发挥规则的作用。同样一个优秀的性能测试和分析人员将要面对的是来自一个全新的应用程序和环境下带来的整个项目的挑战。本文中作者结合自己的使用经验和参考文档，对Tomcat性能方面的调整做一简要的介绍，并给出Tomcat性能的测试、分析和调整优化的一些方法。</p>
<p><strong>二. 测量Web服务器的性能</strong></p>
<p>测量web服务器的性能是一项让人感到畏缩的任务，但是我们在这里将给出一些需要注意的地方并且指点你了解其中更多的细节性的内容。它不像一些简单的任务，如测量CPU的速率或者是测量程序占用CPU的比例，web服务器的性能优化中包括许调整许多变量来达到目标。许多的测量策略中都包含了一个看似简单的浏览实际上是在向服务器发送大量的请求，我们称之为客户端的程序，来测量响应时间。客户端和服务器端是在同一台机器上吗？服务器在测试的时候还运行着其它的什么程序吗？客户端和服务器端的通讯是通过局域网，100baseT，10baseT还是使用调制解调器？客户端是否一直重复请求相同的页面，还是随机地访问不同的页面？（这些影响到了服务缓存的性能）客户端发送请求的有规律的还是突发的？你是在最终的配置环境下运行服务的还是在调试的配置环境下运行服务的？客户端请求中包含图片还是只有HTML页面？是否有请求是通过servlets和JSP的，CGI程序，服务端包含（Server-Side Includes ，SSI是一个可以让你使用动态HTML文件的技术）？所有这些都将是我们要关心的，并且几乎我们不可能精确地把所有的问题都清楚地列出来。</p>
<p><strong>1</strong>.压力测试工具</p>
<p>&ldquo;工欲善其事，必先利其器&rdquo;，压力测试只有借助于一些工具才可得以实施。</p>
<p>大多数web压力测试工具的实现原理都是通过重复的大量的页面请求来模拟多用户对被测系统的并发访问，以此达到产生压力的目的。产生压力的手段都是通过录制或者是编写压力脚本，这些脚本以多个进程或者线程的形式在客户端运行，这样通过人为制造各种类型的压力，我们可以观察被测系统在各种压力状况下的表现，从而定位系统瓶颈，作为系统调优的基础。目前已经存在的性能测试工具林林总总，数量不下一百种，从单一的开放源码的免费小工具如 Aapache 自带的 web 性能测试工具 Apache Benchmark、开源的Jmeter 到大而全的商业性能测试软件如 Mercury 的 LoadRunner 等等。任何性能测试工具都有其优缺点，我们可以根据实际情况挑选用最合适的工具。您可以在这里找到一些web压力测试工具http://www.softwareqatest.com/qatweb1.html#LOAD</p>
<p>这里我们所使用的工具要支持web应用服务认证才可以，要支持接收发送cookies，不仅如此Tomcat支持多种认证方式，比如基本认证、基于表单的认证、相互认证和客户端认证，而一些工具仅仅支持HTTP基本认证。真实地模拟用户认证是性能测试工具的一个重要的部分，因为认证机制将对一个web站点的性能特征产生重要的影响。基于你在产品中使用的不同的认证方式，你需要从上面的工具列表中选择使用这种特性的测试工具。</p>
<p>Apache Benchmark和http_load是命令行形式的工具，非常易于使用。Apache Benchmark可以模仿单独的URL请求并且重复地执行，可以使用不同的命令行参数来控制执行迭代的次数，并发用户数等等。它的一个特点是可以周期性地打印出处理过程的信息，而其它工具只能给出一个全局的报告。</p>
<p><strong>2</strong>.压力测试工具介绍</p>
<p><strong>三. 外部环境的调整<br />
<br />
</strong>　　在Tomcat和应用程序进行了压力测试后，如果您对应用程序的性能结果不太满意，就可以采取一些性能调整措施了，当然了前提是应用程序没有问题，我们这里只讲Tomcat的调整。由于Tomcat的运行依赖于JVM，所以在这里我们把Tomcat的调整可以分为两类来详细描述：<br />
<br />
　　外部环境调整 <br />
<br />
　　调整非Tomcat组件，例如Tomcat运行的操作系统和运行Tomcat的java虚拟机。<br />
<br />
　　自身调整 <br />
<br />
　　修改Tomcat自身的参数，调整Tomcat配置文件中的参数。<br />
<br />
　　下面我们将详细讲解外部环境调整的有关内容，Tomcat自身调整的内容将在第2部分中阐述。<br />
<br />
　　<strong>1</strong>.JAVA虚拟机性能优化<br />
<br />
　　Tomcat本身不能直接在计算机上运行，需要依赖于硬件基础之上的操作系统和一个java虚拟机。您可以选择自己的需要选择不同的操作系统和对应的JDK的版本（只要是符合Sun发布的Java规范的），但我们推荐您使用Sun公司发布的JDK。确保您所使用的版本是最新的，因为Sun公司和其它一些公司一直在为提高性能而对java虚拟机做一些升级改进。一些报告显示JDK1.4在性能上比JDK1.3提高了将近10%到20%。<br />
<br />
　　可以给Java虚拟机设置使用的内存，但是如果你的选择不对的话，虚拟机不会补偿。可通过命令行的方式改变虚拟机使用内存的大小。如下表所示有两个参数用来设置虚拟机使用内存的大小。<br />
<table cellspacing="1" border="1" width="342" cellpadding="1">
    <tbody>
        <tr>
            <td align="center">
            <p class="MsoNormal" align="left" widow-orphan=""><font size="3"><span style="FONT-FAMILY: ">参数</span><span lang="EN-US" style="FONT-FAMILY: "></span></font></p>
            </td>
            <td align="center">
            <p class="MsoNormal" align="left" widow-orphan=""><font size="3"><span style="FONT-FAMILY: ">描述</span><span lang="EN-US" style="FONT-FAMILY: "></span></font></p>
            </td>
        </tr>
        <tr>
            <td align="center">
            <p class="MsoNormal" align="left" widow-orphan=""><span lang="EN-US" style="FONT-FAMILY: "><font size="3">-Xms<size></size></font></span></p>
            </td>
            <td align="center">
            <p class="MsoNormal" align="left" widow-orphan=""><font size="3"><span lang="EN-US" style="FONT-FAMILY: ">JVM</span><span style="FONT-FAMILY: ">初始化堆的大小</span><span lang="EN-US" style="FONT-FAMILY: "></span></font></p>
            </td>
        </tr>
        <tr>
            <td align="center"><span lang="EN-US" style="FONT-FAMILY: "><font size="3">
            <p class="MsoNormal" align="left" widow-orphan=""><span lang="EN-US" style="FONT-FAMILY: "><font size="3">-Xmx<size></size></font></span></p>
            </font></span></td>
            <td align="center">
            <p class="MsoNormal" align="left" widow-orphan=""><font size="3"><span lang="EN-US" style="FONT-FAMILY: ">JVM</span><span style="FONT-FAMILY: ">堆的最大值</span><span lang="EN-US" style="FONT-FAMILY: "></span></font></p>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
　　这两个值的大小一般根据需要进行设置。初始化堆的大小执行了虚拟机在启动时向系统申请的内存的大小。一般而言，这个参数不重要。但是有的应用程序在大负载的情况下会急剧地占用更多的内存，此时这个参数就是显得非常重要，如果虚拟机启动时设置使用的内存比较小而在这种情况下有许多对象进行初始化，虚拟机就必须重复地增加内存来满足使用。由于这种原因，我们一般把-Xms和-Xmx设为一样大，而堆的最大值受限于系统使用的物理内存。一般使用数据量较大的应用程序会使用持久对象，内存使用有可能迅速地增长。当应用程序需要的内存超出堆的最大值时虚拟机就会提示内存溢出，并且导致应用服务崩溃。因此一般建议堆的最大值设置为可用内存的最大值的80%。<br />
<br />
　　Tomcat默认可以使用的内存为128MB，在较大型的应用项目中，这点内存是不够的，需要调大。<br />
<br />
　　Windows下，在文件{tomcat_home}/bin/catalina.bat，Unix下，在文件{tomcat_home}/bin/catalina.sh的前面，增加如下设置：<br />
<br />
　　JAVA_OPTS='-Xms【初始化内存大小】 -Xmx【可以使用的最大内存】'<br />
<br />
　　需要把这个两个参数值调大。例如：<br />
<br />
　　JAVA_OPTS='-Xms256m -Xmx512m'<br />
<br />
　　表示初始化内存为256MB，可以使用的最大内存为512MB。<br />
<br />
　　另外需要考虑的是Java提供的垃圾回收机制。虚拟机的堆大小决定了虚拟机花费在收集垃圾上的时间和频度。收集垃圾可以接受的速度与应用有关，应该通过分析实际的垃圾收集的时间和频率来调整。如果堆的大小很大，那么完全垃圾收集就会很慢，但是频度会降低。如果你把堆的大小和内存的需要一致，完全收集就很快，但是会更加频繁。调整堆大小的的目的是最小化垃圾收集的时间，以在特定的时间内最大化处理客户的请求。在基准测试的时候，为保证最好的性能，要把堆的大小设大，保证垃圾收集不在整个基准测试的过程中出现。<br />
<br />
　　如果系统花费很多的时间收集垃圾，请减小堆大小。一次完全的垃圾收集应该不超过 3-5 秒。如果垃圾收集成为瓶颈，那么需要指定代的大小，检查垃圾收集的详细输出，研究 垃圾收集参数对性能的影响。一般说来，你应该使用物理内存的 80% 作为堆大小。当增加处理器时，记得增加内存，因为分配可以并行进行，而垃圾收集不是并行的。<br />
</p>
<p>[NextPage]</p>
<p>　<strong>2</strong>.操作系统性能优化<br />
<br />
　　这里说的操作系统是指运行web服务器的系统软件，当然，不同的操作系统是为不同的目的而设计的。比如OpenBSD是面向安全的，因此在它的内核中有许多的限制来防止不同形式的服务攻击（OpenBSD的一句座右铭是&ldquo;默认是最安全的&rdquo;）。这些限制或许更多地用来运行活跃的web服务器。<br />
<br />
　　而我们常用的Linux操作系统的目标是易用使用，因此它有着更高的限制。使用BSD内核的系统都带有一个名为&ldquo;Generic&rdquo;的内核，表明所有的驱动器都静态地与之相连。这样就使系统易于使用，但是如果你要创建一个自定义的内核来加强其中某些限制，那就需要排除不需要的设备。Linux内核中的许多驱动都是动态地加载的。但是换而言之，内存现在变得越来越便宜，所以因为加载额外的设备驱动就显得不是很重要的。重要的是要有更多的内存，并且在服务器上腾出更多的可用内存。<br />
<br />
　　<u>小提示</u>：虽然现在内存已经相当的便宜，但还是尽量不要购买便宜的内存。那些有牌子的内存虽然是贵一点，但是从可靠性上来说，性价比会更高一些。<br />
<br />
　　如果是在Windows操作系统上使用Tomcat，那么最好选择服务器版本。因为在非服务器版本上，最终用户授权数或者操作系统本身所能承受的用户数、可用的网络连接数或其它方面的一些方面都是有限制的。并且基于安全性的考虑，必须经常给操作系统打上最新的补丁。<br />
<br />
　　<strong>3</strong>.Tomcat与其它web服务器整合使用<br />
<br />
　　虽然tomcat也可以作web服务器,但其处理静态html的速度比不上apache,且其作为web服务器的功能远不如apache,因此我们想把apache和tomcat集成起来，将html与jsp的功能部分进行明确分工，让tomcat只处理jsp部分，其它的由apache,IIS等这些web服务器处理，由此大大节省了tomcat有限的工作&ldquo;线程&rdquo;。<br />
<br />
　　<strong>4</strong>.负载均衡<br />
<br />
　　在负载均衡的思路下，多台服务器为对称方式，每台服务器都具有同等的地位，可以单独对外提供服务而无须其他服务器的辅助。通过负载分担技术，将外部发送来的请求按一定规则分配到对称结构中的某一台服务器上，而接收到请求的服务器都独立回应客户机的请求。<br />
<br />
　　提供服务的一组服务器组成了一个应用服务器集群(cluster)，并对外提供一个统一的地址。当一个服务请求被发至该集群时，根据一定规则选择一台服务器，并将服务转定向给该服务器承担，即将负载进行均衡分摊。<br />
<br />
　　通过应用负载均衡技术，使应用服务超过了一台服务器只能为有限用户提供服务的限制，可以利用多台服务器同时为大量用户提供服务。当某台服务器出现故障时，负载均衡服务器会自动进行检测并停止将服务请求分发至该服务器，而由其他工作正常的服务器继续提供服务，从而保证了服务的可靠性。<br />
<br />
　　负载均衡实现的方式大概有四种：第一是通过DNS，但只能实现简单的轮流分配，不能处理故障，第二如果是基于MS IIS，Windows 2003 server本身就带了负载均衡服务，第三是硬件方式，通过交换机的功能或专门的负载均衡设备可以实现，第四种是软件方式，通过一台负载均衡服务器进行，上面安装软件。使用Apache Httpd Server做负载平衡器，Tomcat集群节点使用Tomcat就可以做到以上第四种方式。这种方式比较灵活，成本相对也较低。另外一个很大的优点就是可以根据应用的情况和服务器的情况采取一些策略。 </p>
<p>[NextPage]</p>
<p><strong>四. 自身调整<br />
<br />
</strong>　　本节将向您详细介绍一些加速可使Tomcat实例加速运行的技巧和方法，无论是在什么操作系统或者何种Java虚拟机上。在有些情况下，您可能没有控制部署环境上的操作系统或者Java虚拟机。在这种情况下，您就需要逐行了解以下的的一些建议，然而你应该在修改后使之生效。我认为以下方法是Tomcat性能自身调整的最佳方式。<br />
<br />
　　<strong>1</strong>.禁用DNS查询<br />
<br />
　　当web应用程序向要记录客户端的信息时，它也会记录客户端的IP地址或者通过域名服务器查找机器名转换为IP地址。DNS查询需要占用网络，并且包括可能从很多很远的服务器或者不起作用的服务器上去获取对应的IP的过程，这样会消耗一定的时间。为了消除DNS查询对性能的影响我们可以关闭DNS查询，方式是修改server.xml文件中的enableLookups参数值：<br />
</p>
<p class="code">Tomcat4<br />
<connector acceptcount="100" enablelookups="false" disableuploadtimeout="true" classname="org.apache.coyote.tomcat4.CoyoteConnector" connectiontimeout="20000" port="80" maxprocessors="75" redirectport="8443" minprocessors="5" debug="0" useurivalidationhack="false"></connector>&lt;Connector className=org.apache.coyote.tomcat4.CoyoteConnector port=80 minProcessors=5 maxProcessors=75 enableLookups=false redirectPort=8443 acceptCount=100 debug=0 connectionTimeout=20000 useURIValidationHack=false disableUploadTimeout=true /&gt;<br />
<br />
Tomcat5<br />
&lt;Connector port=80 maxThreads=150 minSpareThreads=25 maxSpareThreads=75 enableLookups=false redirectPort=8443 acceptCount=100 debug=0 connectionTimeout=20000 disableUploadTimeout=true/&gt;</p>
<p class="code"><br />
　　除非你需要连接到站点的每个HTTP客户端的机器名，否则我们建议在生产环境上关闭DNS查询功能。可以通过Tomcat以外的方式来获取机器名。这样不仅节省了网络带宽、查询时间和内存，而且更小的流量会使日志数据也会变得更少，显而易见也节省了硬盘空间。对流量较小的站点来说禁用DNS查询可能没有大流量站点的效果明显，但是此举仍不失为一良策。谁又见到一个低流量的网站一夜之间就流量大增呢？<br />
<br />
　　<strong>2</strong>.调整线程数<br />
<br />
　　另外一个可通过应用程序的连接器（Connector）进行性能控制的的参数是创建的处理请求的线程数。Tomcat使用线程池加速响应速度来处理请求。在Java中线程是程序运行时的路径，是在一个程序中与其它控制线程无关的、能够独立运行的代码段。它们共享相同的地址空间。多线程帮助程序员写出CPU最大利用率的高效程序，使空闲时间保持最低，从而接受更多的请求。<br />
<br />
　　Tomcat4中可以通过修改minProcessors和maxProcessors的值来控制线程数。这些值在安装后就已经设定为默认值并且是足够使用的，但是随着站点的扩容而改大这些值。minProcessors服务器启动时创建的处理请求的线程数应该足够处理一个小量的负载。也就是说，如果一天内每秒仅发生5次单击事件，并且每个请求任务处理需要1秒钟，那么预先设置线程数为5就足够了。但在你的站点访问量较大时就需要设置更大的线程数，指定为参数maxProcessors的值。maxProcessors的值也是有上限的，应防止流量不可控制（或者恶意的服务攻击），从而导致超出了虚拟机使用内存的大小。如果要加大并发连接数，应同时加大这两个参数。web server允许的最大连接数还受制于操作系统的内核参数设置，通常Windows是2000个左右，Linux是1000个左右。<br />
<br />
　　在Tomcat5对这些参数进行了调整，请看下表：<br />
</p>
<p class="code">
<table cellspacing="1" border="1" width="525" cellpadding="1">
    <tbody>
        <tr>
            <td align="center">
            <p class="MsoNormal" align="center" widow-orphan=""><font size="3"><strong normal="" style="mso-bidi-font-weight: "><span arial="" style="FONT-FAMILY: ">属性名</span></strong><strong normal="" style="mso-bidi-font-weight: "><span lang="EN-US" style="FONT-FAMILY: "></span></strong></font></p>
            </td>
            <td align="center">
            <p class="MsoNormal" align="center" widow-orphan=""><font size="3"><strong normal="" style="mso-bidi-font-weight: "><span arial="" style="FONT-FAMILY: ">描述</span></strong><strong normal="" style="mso-bidi-font-weight: "><span lang="EN-US" style="FONT-FAMILY: "></span></strong></font></p>
            </td>
        </tr>
        <tr>
            <td align="center">
            <p class="MsoNormal" align="left" widow-orphan=""><span lang="EN-US" style="FONT-FAMILY: "><font size="3">maxThreads</font></span></p>
            </td>
            <td align="center">
            <p class="MsoNormal" align="left" widow-orphan=""><font size="3"><span lang="EN-US" style="FONT-FAMILY: ">Tomcat</span><span arial="" style="FONT-FAMILY: ">使用线程来处理接收的每个请求。这个值表示</span><span lang="EN-US" style="FONT-FAMILY: ">Tomcat</span><span arial="" style="FONT-FAMILY: ">可创建的最大的线程数。</span><span lang="EN-US" style="FONT-FAMILY: "></span></font></p>
            </td>
        </tr>
        <tr>
            <td align="center">
            <p class="MsoNormal" align="left" widow-orphan=""><span lang="EN-US" style="FONT-FAMILY: "><font size="3">acceptCount </font></span></p>
            </td>
            <td align="center">
            <p class="MsoNormal" align="left" widow-orphan=""><font size="3"><span arial="" style="FONT-FAMILY: ">指定当所有可以使用的处理请求的线程数都被使用时，可以放到处理队列中的请求数，超过这个数的请求将不予处理。</span><span lang="EN-US" style="FONT-FAMILY: "></span></font></p>
            </td>
        </tr>
        <tr>
            <td align="center">
            <p class="MsoNormal" align="left" widow-orphan=""><span lang="EN-US" style="FONT-FAMILY: "><font size="3">connnectionTimeout </font></span></p>
            </td>
            <td align="center">
            <p class="MsoNormal" align="left" widow-orphan=""><font size="3"><span arial="" style="FONT-FAMILY: ">网络连接超时，单位：毫秒。设置为</span><span lang="EN-US" style="FONT-FAMILY: ">0</span><span arial="" style="FONT-FAMILY: ">表示永不超时，这样设置有隐患的。通常可设置为</span><span lang="EN-US" style="FONT-FAMILY: ">30000</span><span arial="" style="FONT-FAMILY: ">毫秒。</span><span lang="EN-US" style="FONT-FAMILY: "></span></font></p>
            </td>
        </tr>
        <tr>
            <td align="center">
            <p class="MsoNormal" align="left" widow-orphan=""><span lang="EN-US" style="FONT-FAMILY: "><font size="3">minSpareThreads </font></span></p>
            </td>
            <td align="center">
            <p class="MsoNormal" align="left" widow-orphan=""><font size="3"><span lang="EN-US" style="FONT-FAMILY: ">Tomcat</span><span arial="" style="FONT-FAMILY: ">初始化时创建的线程数。</span><span lang="EN-US" style="FONT-FAMILY: "></span></font></p>
            </td>
        </tr>
        <tr>
            <td align="center">
            <p class="MsoNormal" align="left" widow-orphan=""><span lang="EN-US" style="FONT-FAMILY: "><font size="3">maxSpareThreads </font></span></p>
            </td>
            <td align="center">
            <p class="MsoNormal" align="left" widow-orphan=""><font size="3"><span arial="" style="FONT-FAMILY: ">一旦创建的线程超过这个值，</span><span lang="EN-US" style="FONT-FAMILY: ">Tomcat</span><span arial="" style="FONT-FAMILY: ">就会关闭不再需要的</span><span lang="EN-US" style="FONT-FAMILY: ">socket</span><span arial="" style="FONT-FAMILY: ">线程。</span><span lang="EN-US" style="FONT-FAMILY: "></span></font></p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p class="code"><br />
<br />
　　最好的方式是多设置几次并且进行测试，观察响应时间和内存使用情况。在不同的机器、操作系统或虚拟机组合的情况下可能会不同，而且并不是所有人的web站点的流量都是一样的，因此没有一刀切的方案来确定线程数的值。<br />
</p>
<p>[NextPage]</p>
<p><strong>3</strong>.加速JSP编译速度<br />
<br />
　　当第一次访问一个JSP文件时，它会被转换为Java serverlet源码，接着被编译成Java字节码。你可以控制使用哪个编译器，默认情况下，Tomcat使用使用命令行javac进行使用的编译器。也可以使用更快的编译器，但是这里我们将介绍如何优化它们。<br />
<br />
　　另外一种方法是不要把所有的实现都使用JSP页面，而是使用一些不同的java模板引擎变量。显然这是一个跨越很大的决定，但是事实证明至少这种方法是只得研究的。如果你想了解更多有关在Tomcat可使用的模板语言，你可以参考Jason Hunter和William Crawford合著的《Java Servlet Programming 》一书（O'Reilly公司出版）。<br />
<br />
　　在Tomcat 4.0中可以使用流行而且免费的Jikes编译器。Jikes编译器的速度要由于Sun的Java编译器。首先要安装Jikes（可访问http://oss.software.ibm.com/pub/jikes 获得更多的信息），接着需要在环境变量中设置JIKESPATH包含系统运行时所需的JAR文件。装好Jikes以后还需要设置让JSP编译servlet使用Jikes，需要修改web.xml文件中jspCompilerPlugin的值：<br />
</p>
<p class="code"><servlet></servlet><br />
<servlet-name></servlet-name>jsp<br />
<servlet-class></servlet-class><br />
org.apache.jasper.servlet.JspServlet<br />
<br />
<init-param></init-param><br />
<param-name></param-name>
logVerbosityLevel<br />
<param-value></param-value>
WARNING<br />
<br />
<init-param></init-param><br />
<param-name></param-name>
jspCompilerPlugin<br />
<param-value></param-value>
<br />
org.apache.jasper.compiler.JikesJavaCompiler<br />
<br />
<br />
<init-param></init-param><br />
<!-- <param-name><br />
org.apache.catalina.jsp_classpath<br />
</param-name> --><br />
<param-name></param-name>
classpath<br />
<param-value></param-value>
<br />
/usr/local/jdk1.3.1-linux/jre/lib/rt.jar:<br />
/usr/local/lib/java/servletapi/servlet.ja<br />
r<br />
<br />
<load-on-startup></load-on-startup>3<br />
</p>
<p><br />
　　在Tomcat 4.1（或更高版本），JSP的编译由包含在Tomcat里面的Ant程序控制器直接执行。这听起来有一点点奇怪，但这正是Ant有意为之的一部分，有一个API文档指导开发者在没有启动一个新的JVM的情况下，使用Ant。这是使用Ant进行Java开发的一大优势。另外，这也意味着你现在能够在Ant中使用任何javac支持的编译方式，这里有一个关于Apache Ant使用手册的javac page列表。使用起来是容易的，因为你只需要在 元素中定义一个名字叫&ldquo;compiler&rdquo;，并且在value中有一个支持编译的编译器名字，示例如下：<br />
</p>
<p class="code"><servlet></servlet><br />
<servlet-name></servlet-name>jsp<br />
<servlet-class></servlet-class><br />
org.apache.jasper.servlet.JspServlet<br />
<br />
<init-param></init-param><br />
<param-name></param-name>
logVerbosityLevel<br />
<param-value></param-value>
WARNING<br />
<br />
<init-param></init-param><br />
<param-name></param-name>
compiler<br />
<param-value></param-value>
jikes<br />
<br />
<load-on-startup></load-on-startup>3<br />
</p>
<p><br />
<br />
Ant可用的编译器<br />
<table cellspacing="1" border="1" width="485" cellpadding="1">
    <tbody>
        <tr>
            <td>
            <p class="MsoNormal" align="center" auto=""><font size="3"><strong><span arial="" style="FONT-FAMILY: ">名称</span></strong><strong><span lang="EN-US" style="FONT-FAMILY: "></span></strong></font></p>
            </td>
            <td>
            <p class="MsoNormal" align="center" auto=""><font size="3"><strong><span arial="" style="FONT-FAMILY: ">别名</span></strong><strong><span lang="EN-US" style="FONT-FAMILY: "></span></strong></font></p>
            </td>
            <td>
            <p class="MsoNormal" align="center" auto=""><font size="3"><strong><span arial="" style="FONT-FAMILY: ">调用的编译器</span></strong><strong><span lang="EN-US" style="FONT-FAMILY: "></span></strong></font></p>
            </td>
        </tr>
        <tr>
            <td>
            <p class="MsoNormal" align="left" auto=""><span lang="EN-US" style="FONT-FAMILY: "><font size="3">classic </font></span></p>
            </td>
            <td>
            <p class="MsoNormal" align="left" auto=""><span lang="EN-US" style="FONT-FAMILY: "><font size="3">javac1.1, javac1.2 </font></span></p>
            </td>
            <td>
            <p class="MsoNormal" align="left" auto=""><span lang="EN-US" style="FONT-FAMILY: "><font size="3">Standard JDK 1.1/1.2 compiler </font></span></p>
            </td>
        </tr>
        <tr>
            <td>
            <p class="MsoNormal" align="left" auto=""><span lang="EN-US" style="FONT-FAMILY: "><font size="3">modern </font></span></p>
            </td>
            <td>
            <p class="MsoNormal" align="left" auto=""><span lang="EN-US" style="FONT-FAMILY: "><font size="3">javac1.3, javac1.4 </font></span></p>
            </td>
            <td>
            <p class="MsoNormal" align="left" auto=""><span lang="EN-US" style="FONT-FAMILY: "><font size="3">Standard JDK 1.3/1.4 compiler </font></span></p>
            </td>
        </tr>
        <tr>
            <td>
            <p class="MsoNormal" align="left" auto=""><span lang="EN-US" style="FONT-FAMILY: "><font size="3">jikes </font></span></p>
            </td>
            <td>　　</td>
            <td>
            <p class="MsoNormal" align="left" auto=""><span lang="EN-US" style="FONT-FAMILY: "><font size="3">The Jikes compiler </font></span></p>
            </td>
        </tr>
        <tr>
            <td>JVC</td>
            <td>
            <p class="MsoNormal" align="left"><span lang="EN-US" style="FONT-FAMILY: "><font size="3">Microsoft </font></span></p>
            </td>
            <td>
            <p class="MsoNormal" align="left" auto=""><span lang="EN-US" style="FONT-FAMILY: "><font size="3">Microsoft command-line compiler from the Microsoft SDK for Java/Visual J++ </font></span></p>
            </td>
        </tr>
        <tr>
            <td>KJC</td>
            <td>　　</td>
            <td>
            <p class="MsoNormal" align="left" auto=""><span lang="EN-US" style="FONT-FAMILY: "><font size="3">The kopi compiler </font></span></p>
            </td>
        </tr>
        <tr>
            <td>GCJ</td>
            <td>　　</td>
            <td>
            <p class="MsoNormal" align="left" auto=""><span lang="EN-US" style="FONT-FAMILY: "><font size="3">The gcj compiler (included as part of gcc) </font></span></p>
            </td>
        </tr>
        <tr>
            <td>SJ</td>
            <td>
            <p class="MsoNormal" align="left" auto=""><span lang="EN-US" style="FONT-FAMILY: "><font size="3">Symantec </font></span></p>
            </td>
            <td>
            <p class="MsoNormal" align="left" auto=""><span lang="EN-US" style="FONT-FAMILY: "><font size="3">Symantec's Java compiler </font></span></p>
            </td>
        </tr>
        <tr>
            <td>
            <p class="MsoNormal" align="left" auto=""><span lang="EN-US" style="FONT-FAMILY: "><font size="3">extJavac</font></span></p>
            </td>
            <td>　　</td>
            <td>
            <p class="MsoNormal" align="left" auto=""><span lang="EN-US" style="FONT-FAMILY: "><font size="3">Runs either the modern or classic compiler in a JVM of its own </font></span></p>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
　　由于JSP页面在第一次使用时已经被编译，那么你可能希望在更新新的jsp页面后马上对它进行编译。实际上，这个过程完全可以自动化，因为可以确认的是新的JSP页面在生产服务器和在测试服务器上的运行效果是一样的。 </p>
<p>[NextPage]</p>
<p>在Tomcat4的bin目录下有一个名为jspc的脚本。它仅仅是运行翻译阶段，而不是编译阶段，使用它可以在当前目录生成Java源文件。它是调试JSP页面的一种有力的手段。<br />
<br />
　　可以通过浏览器访问再确认一下编译的结果。这样就确保了文件被转换成serverlet，被编译了可直接执行。这样也准确地模仿了真实用户访问JSP页面，可以看到给用户提供的功能。也抓紧这最后一刻修改出现的bug并且修改它J<br />
<br />
　　Tomcat提供了一种通过请求来编译JSP页面的功能。例如，你可以在浏览器地址栏中输入http://localhost:8080/examples/jsp/dates/date.jsp?jsp_precompile=true，这样Tomcat就会编译data.jsp而不是执行它。此举唾手可得，不失为一种检验页面正确性的捷径。<br />
<br />
　　<strong>4</strong>. 其它<br />
<br />
　　前面我们提到过操作系统通过一些限制手段来防止恶意的服务攻击，同样Tomcat也提供了防止恶意攻击或禁止某些机器访问的设置。<br />
<br />
　　Tomcat提供了两个参数供你配置：RemoteHostValve 和RemoteAddrValve。 <br />
<br />
　　通过配置这两个参数，可以让你过滤来自请求的主机或IP地址，并允许或拒绝哪些主机/IP。与之类似的，在Apache的httpd文件里有对每个目录的允许/拒绝指定。 <br />
<br />
　　例如你可以把Admin Web application设置成只允许本地访问，设置如下：<br />
</p>
<p class="code"><context path="/path/to/secret_files"></context><br />
<valve classname="org.apache.catalina.valves.RemoteAddrValve"></valve><br />
allow=127.0.0.1 deny=/&gt; <br />
</p>
<p><br />
　　如果没有给出允许主机的指定，那么与拒绝主机匹配的主机就会被拒绝，除此之外的都是允许的。与之类似，如果没有给出拒绝主机的指定，那么与允许主机匹配的主机就会被允许，除此之外的都是拒绝的。<br />
<br />
<br />
<strong>五. 容量计划</strong><br />
<br />
　　容量计划是在生产环境中使用Tomcat不得不提的提高性能的另一个重要的话题。如果你没有对预期的网络流量下的硬件和带宽做考虑的话那么无论你如何做配置修改和测试都无济于事。<br />
<br />
　　这里先对提及的容量计划作一个简要的定义：容量计划是指评估硬件、操作系统和网络带宽，确定应用服务的服务范围，寻求适合需求和软件特性的软硬件的一项活动。因此这里所说的软件不仅包括Tomcat，也包括与Tomcat结合使用的任何第三方web服务器软件。<br />
<br />
　　如果在购买软硬件或部署系统前你对容量计划一无所知，不知道现有的软硬件环境能够支撑多少的访问量，甚至更糟直到你已经交付并且在生产环境上部署产品后才意识到配置有问题时再进行变更可能为时已晚。此时只能增加硬件投入，增加硬盘容量甚至购买更好的服务器。如果事先做了容量计划那么就不会搞的如此焦头烂额了。<br />
<br />
　　我们这里只介绍与Tomcat相关的内容。<br />
<br />
　　首先为了确定Tomcat使用机器的容量计划，你应该从一下列表项目种着手研究和计划：<br />
<br />
　　<strong>1</strong>. 硬件<br />
<br />
　　采用什么样的硬件体系？需要多少台计算机？使用一个大型的，还是使用多台小型机？每个计算机上使用几个CPU？使用多少内存？使用什么样的存储设备，I/O的处理速度有什么要求？怎样维护这些计算机？不同的JVM在这些硬件上运行的效果如何（比如IBM AIX系统只能在其设计的硬件系统上运行）？<br />
<br />
　　<strong>2</strong>. 网络带宽<br />
<br />
　　带宽的使用极限是多少？web应用程序如何处理过多的请求？<br />
<br />
　　<strong>3</strong>. 服务端操作系统<br />
<br />
　　采用哪种操作系统作为站点服务器最好？在确定的操作系统上使用哪个JVM最好？例如，JVM在这种系统上是否支持本地多线程，对称多处理？哪种系统可使web服务器更快、更稳定，并且更便宜。是否支持多CPU？ </p>
<p>[NextPage]</p>
<p><strong>4</strong>. Tomcat容量计划<br />
<br />
　　以下介绍针对Tomcat做容量计划的步骤：<br />
<br />
　　1） 量化负载。如果站点已经建立并运行，可以使用前面介绍的工具模仿用户访问，确定资源的需求量。<br />
<br />
　　2） 针对测试结果或测试过程中进行分析。需要知道那些请求造成了负载过重或者使用过多的资源，并与其它请求做比较，这样就确定了系统的瓶颈所在。例如：如果servlet在查询数据库的步骤上耗用较长的时间，那么就需要考虑使用缓冲池来降低响应时间。<br />
<br />
　　3） 确定性能最低标准。例如，你不想让用户花20秒来等待结果页面的返回，也就是说甚至在达到访问量的极限时，用户等待的时间也不能超过20秒种（从点击链接到看到返第一条返回数据）。这个时间中包含了数据库查询时间和文件访问时间。同类产品性能在不同的公司可能有不同的标准，一般最好采取同行中的最低标准或对这个标准做出评估。<br />
<br />
　　4） 确定如何合理使用底层资源，并逐一进行测试。底层资源包括CPU、内存、存储器、带宽、操作系统、JVM等等。在各种生产环境上都按顺序进行部署和测试，观察是否符合需求。在测试Tomcat时尽量多采用几种JVM，并且调整JVM使用内存和Tomcat线程池的大小进行测试。同时为了达到资源充分合理稳定地使用的效果，还需针对测试过程中出现的硬件系统瓶颈进行处理确定合理的资源配置。这个过程最为复杂，而且一般由于没有可参考的值所以只能靠理论推断和经验总结。<br />
<br />
　　5） 如果通过第4步的反复测试如果达到了最优的组合，就可以在相同的生产环境上部署产品了。<br />
<br />
　　此外应牢记一定要文档化你的测试过程和结果，因为此后可能还会进行测试，这样就可以拿以前的测试结果做为参考。另外测试过程要反复多次进行，每次的条件可能都不一样，因此只有记录下来才能进行结果比较和最佳条件的选择。<br />
<br />
　　这样我们通过测试找到了最好的组合方式，各种资源得到了合理的配置，系统的性能得到了极大的提升。</p>
          <br/>
          <span style="color:red;">
            <a href="http://murain.javaeye.com/blog/39522#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 18 Dec 2006 10:37:02 +0800</pubDate>
        <link>http://murain.javaeye.com/blog/39522</link>
        <guid>http://murain.javaeye.com/blog/39522</guid>
      </item>
      <item>
        <title>(转)tomcat基础配置说明</title>
        <author>muRain</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://murain.javaeye.com">muRain</a>&nbsp;
          链接：<a href="http://murain.javaeye.com/blog/39499" style="color:red;">http://murain.javaeye.com/blog/39499</a>&nbsp;
          发表时间: 2006年12月18日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>对很多初学者来说tomact的配置是非常头疼的事情，但是有了下面的文章你会易如反掌地配置tomcat了，赶快行动吧！</p>
<br />
<font face="Verdana">
<p><font size="2" face="verdana, arial, helvetica"><span class="javascript" id="text2545742" twffan="done" style="FONT-SIZE: 12px">来源：<a href="http://www.csdn.net/" target="_blank">www.csdn.net</a><br />
<br />
<server></server>元素<br />
它代表整个容器,是Tomcat实例的顶层元素.由org.apache.catalina.Server接口来定义.它包含一个<service></service>元素.并且它不能做为任何元素的子元素.<br />
<br />
<server shutdown="SHUTDOWN" port="8005" debug="0"></server><br />
<br />
1&gt;className指定实现org.apache.catalina.Server接口的类.默认值为org.apache.catalina.core.StandardServer<br />
2&gt;port指定Tomcat监听shutdown命令端口.终止服务器运行时,必须在Tomcat服务器所在的机器上发出shutdown命令.该属性是必须的.<br />
3&gt;shutdown指定终止Tomcat服务器运行时,发给Tomcat服务器的shutdown监听端口的字符串.该属性必须设置<br />
<br />
<br />
<br />
<service></service>元素<br />
该元素由org.apache.catalina.Service接口定义,它包含一个<engine></engine>元素,以及一个或多个<connector></connector>,这些Connector元素共享用同一个Engine元素<br />
<br />
<service name="Catalina"></service><br />
<service name="Apache"></service><br />
第一个<service></service>处理所有直接由Tomcat服务器接收的web客户请求.<br />
第二个<service></service>处理所有由Apahce服务器转发过来的Web客户请求<br />
<br />
1&gt;className 指定实现org.apahce.catalina.Service接口的类.默认为org.apahce.catalina.core.StandardService<br />
2&gt;name定义Service的名字<br />
<br />
<br />
<br />
<engine></engine>元素<br />
每个Service元素只能有一个Engine元素.元素处理在同一个<service></service>中所有<connector></connector>元素接收到的客户请求.由org.apahce.catalina.Engine接口定义.<br />
<br />
<engine name="Catalina" debug="0" defaulthost="localhost"></engine><br />
<br />
1&gt;className指定实现Engine接口的类,默认值为StandardEngine<br />
2&gt;defaultHost指定处理客户的默认主机名,在<engine></engine>中的<host></host>子元素中必须定义这一主机<br />
3&gt;name定义Engine的名字<br />
<br />
在<engine></engine>可以包含如下元素<logger></logger>, <realm></realm>, <value></value>, <host></host><br />
<br />
<br />
<br />
<host></host>元素<br />
它由Host接口定义.一个Engine元素可以包含多个<host></host>元素.每个<host></host>的元素定义了一个虚拟主机.它包含了一个或多个Web应用.<br />
<br />
<host name="localhost" autodeploy="true" unpackwars="true" debug="0" appbase="webapps"></host><br />
<br />
1&gt;className指定实现Host接口的类.默认值为StandardHost<br />
2&gt;appBase指定虚拟主机的目录,可以指定绝对目录,也可以指定相对于<catalina_home></catalina_home>的相对目录.如果没有此项,默认为<catalina_home></catalina_home>/webapps<br />
3&gt;autoDeploy如果此项设为true,表示Tomcat服务处于运行状态时,能够监测appBase下的文件,如果有新有web应用加入进来,会自运发布这个WEB应用<br />
4&gt;unpackWARs如果此项设置为true,表示把WEB应用的WAR文件先展开为开放目录结构后再运行.如果设为false将直接运行为WAR文件<br />
5&gt;alias指定主机别名,可以指定多个别名<br />
6&gt;deployOnStartup如果此项设为true,表示Tomcat服务器启动时会自动发布appBase目录下所有的Web应用.如果Web应用中的server.xml没有相应的<context></context>元素,将采用Tomcat默认的Context<br />
7&gt;name定义虚拟主机的名字<br />
<br />
在<host></host>元素中可以包含如下子元素<br />
<logger></logger>, <realm></realm>, <value></value>, <context></context><br />
<br />
<br />
<context></context>元素<br />
它由Context接口定义.是使用最频繁的元素.每个可以包含多个<context></context>元素.每个web应用有唯一<br />
的一个相对应的Context代表web应用自身.servlet容器为第一个web应用创建一个<br />
ServletContext对象.<br />
<br />
<context docbase="sample" path="/sample" debug="0" reloadbale="true"></context><br />
<br />
1&gt;className指定实现Context的类,默认为StandardContext类<br />
2&gt;path指定访问Web应用的URL入口,注意/myweb,而不是myweb了事<br />
3&gt;reloadable如果这个属性设为true, Tomcat服务器在运行状态下会监视在WEB-INF/classes和Web-INF/lib目录CLASS文件的改运.如果监视到有class文件被更新,服务器自重新加载Web应用<br />
3&gt;cookies指定是否通过Cookies来支持Session,默认值为true<br />
4&gt;useNaming指定是否支持JNDI,默认值为了true<br />
<br />
<br />
在<context></context>元素中可以包含如下元素<br />
<logger></logger>, <realm></realm>, <resource></resource>, <resourceparams></resourceparams><br />
<br />
<br />
<br />
Connector元素<br />
由Connector接口定义.<connector></connector>元素代表与客户程序实际交互的给件,它负责接收客户请求,以及向客户返回响应结果.<br />
<br />
<connector acceptcount="100" enablelookups="false" disableuploadtimeout="true" connectiontimeout="20000" port="8080" maxthread="50" minsparethreads="25" redirectport="8443" debug="0" maxsparethread="75"></connector><br />
<br />
<connection enablelookups="false" port="8009" protocol="AJP/1.3" redirectport="8443" debug="0"></connection><br />
第一个Connector元素定义了一个HTTP Connector,它通过8080端口接收HTTP请求;第二个Connector元素定义了一个JD Connector,它通过8009端口接收由其它服务器转发过来的请求.<br />
<br />
Connector元素共用属性<br />
1&gt;className指定实现Connector接口的类<br />
2&gt;enableLookups如果设为true,表示支持域名解析,可以把IP地址解析为主机名.WEB应用中调用request.getRemoteHost方法返回客户机主机名.默认值为true<br />
3&gt;redirectPort指定转发端口.如果当前端口只支持non-SSL请求,在需要安全通信的场命,将把客户请求转发至SSL的redirectPort端口<br />
HttpConnector元素的属性<br />
1&gt;className实现Connector的类<br />
2&gt;port设定Tcp/IP端口,默认值为8080,如果把8080改成80,则只要输入<a href="http://localhost即可/" target="_blank">http://localhost即可</a><br />
因为TCP/IP的默认端口是80<br />
3&gt;address如果服务器有二个以上ip地址,此属性可以设定端口监听的ip地址.默认情况下,端口会监听服务器上所有的ip地址<br />
4&gt;bufferSize设定由端口创建的输入流的缓存大小.默认值为2048byte<br />
5&gt;protocol设定Http协议,默认值为HTTP/1.1<br />
6&gt;maxThreads设定在监听端口的线程的最大数目,这个值也决定了服务器可以同时响应客户请求的最大数目.默认值为200<br />
7&gt;acceptCount设定在监听端口队列的最大客户请求数量,默认值为10.如果队列已满,客户必须等待.<br />
8&gt;connectionTimeout定义建立客户连接超时的时间.如果为-1,表示不限制建立客户连接的时间<br />
JkConnector的属性<br />
1&gt;className实现Connector的类<br />
2&gt;port设定AJP端口号<br />
3&gt;protocol必须设定为AJP/1.3</span></font></p>
</font>
          <br/>
          <span style="color:red;">
            <a href="http://murain.javaeye.com/blog/39499#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 18 Dec 2006 09:13:00 +0800</pubDate>
        <link>http://murain.javaeye.com/blog/39499</link>
        <guid>http://murain.javaeye.com/blog/39499</guid>
      </item>
      <item>
        <title>(转)JAVA性能优化技巧 </title>
        <author>muRain</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://murain.javaeye.com">muRain</a>&nbsp;
          链接：<a href="http://murain.javaeye.com/blog/34753" style="color:red;">http://murain.javaeye.com/blog/34753</a>&nbsp;
          发表时间: 2006年11月17日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <div onload="this.style.overflowX='auto';" style="PADDING-RIGHT: 0px; MARGIN-TOP: 10px; FONT-SIZE: 9pt; OVERFLOW-X: hidden; WIDTH: 97%; WORD-BREAK: break-all; TEXT-INDENT: 0px; LINE-HEIGHT: normal; HEIGHT: 150px; WORD-WRAP: break-word">
<div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">转自：<font face="Arial">http://www.operamasks.org/ForumTopic.jsp?TopicID=7bfd86ff010d1000e0000004c0a800d3&amp;TID=77be7ec7010d1000e0000003c0a80081&amp;TParentID=C003</font></span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">=================================== <br />
</span><span style="FONT-SIZE: 9pt; COLOR: #555555">摘要: <br />
=================================== <br />
可供程序利用的资源（内存、CPU时间、网络带宽等）是有限的，优化的目的就是让程序用尽可能少的资源完成预定的任务。优化通常包含两方面的内容：减小代码的体积，提高代码的运行效率。本文讨论的主要是如何提高代码的效率。 <br />
=================================== <br />
提纲: <br />
=================================== <br />
一、通用篇 <br />
　　1.1 不用new关键词创建类的实例 <br />
　　1.2 使用非阻塞I/O <br />
　　1.3 慎用异常 <br />
　　1.4 不要重复初始化变量 <br />
　　1.5 尽量指定类的final修饰符 <br />
　　1.6 尽量使用局部变量 <br />
　　1.7 乘法和除法 <br />
二、J2EE篇 <br />
　　2.1 使用缓冲标记 <br />
　　2.2 始终通过会话Bean访问实体Bean <br />
　　2.3 选择合适的引用机制 <br />
　　2.4 在部署描述器中设置只读属性 <br />
　　2.5 缓冲对EJB Home的访问 <br />
　　2.6 为EJB实现本地接口 <br />
　　2.7 生成主键 <br />
　　2.8 及时清除不再需要的会话 <br />
　　2.9 在JSP页面中关闭无用的会话 <br />
　　2.10 Servlet与内存使用 <br />
　　2.11 HTTP Keep-Alive <br />
　　2.12 JDBC与Unicode <br />
　　2.13 JDBC与I/O <br />
　　1.14 内存数据库 <br />
三、GUI篇 <br />
　　3.1 用JAR压缩类文件 <br />
　　3.2 提示Applet装入进程 <br />
　　3.3 在画出图形之前预先装入它 <br />
　　3.4 覆盖update方法 <br />
　　3.5 延迟重画操作 <br />
　　3.6 使用双缓冲区 <br />
　　3.7 使用BufferedImage <br />
　　3.8 使用VolatileImage <br />
　　3.9 使用Window Blitting <br />
四、补充资料 <br />
=================================== <br />
正文: <br />
=================================== <br />
一、通用篇 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">&ldquo;通用篇&rdquo;讨论的问题适合于大多数Java应用。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">1.1 </span><span style="FONT-SIZE: 9pt; COLOR: #555555">不用new关键词创建类的实例 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">用new关键词创建类的实例时，构造函数链中的所有构造函数都会被自动调用。但如果一个对象实现了Cloneable接口，我们可以调用它的clone()方法。clone()方法不会调用任何类构造函数。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">在使用设计模式（Design Pattern）的场合，如果用Factory模式创建对象，则改用clone()方法创建新的对象实例非常简单。例如，下面是Factory模式的一个典型实现： </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
public static Credit getNewCredit() {<br />
return new Credit();<br />
} </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
</span><span style="FONT-SIZE: 9pt; COLOR: #555555">改进后的代码使用clone()方法，如下所示： </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
private static Credit BaseCredit = new Credit();<br />
public static Credit getNewCredit() {<br />
return (Credit) BaseCredit.clone();<br />
} </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
</span><span style="FONT-SIZE: 9pt; COLOR: #555555">上面的思路对于数组处理同样很有用。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">1.2 </span><span style="FONT-SIZE: 9pt; COLOR: #555555">使用非阻塞I/O </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">版本较低的JDK不支持非阻塞I/O API。为避免I/O阻塞，一些应用采用了创建大量线程的办法（在较好的情况下，会使用一个缓冲池）。这种技术可以在许多必须支持并发I/O流的应用中见到，如Web服务器、报价和拍卖应用等。然而，创建Java线程需要相当可观的开销。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">JDK 1.4</span><span style="FONT-SIZE: 9pt; COLOR: #555555">引入了非阻塞的I/O库（java.nio）。如果应用要求使用版本较早的JDK，在这里有一个支持非阻塞I/O的软件包。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">请参见Sun中国网站的《调整Java的I/O性能》。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">1.3 </span><span style="FONT-SIZE: 9pt; COLOR: #555555">慎用异常 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">异常对性能不利。抛出异常首先要创建一个新的对象。Throwable接口的构造函数调用名为fillInStackTrace()的本地（Native）方法，fillInStackTrace()方法检查堆栈，收集调用跟踪信息。只要有异常被抛出，VM就必须调整调用堆栈，因为在处理过程中创建了一个新的对象。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">异常只能用于错误处理，不应该用来控制程序流程。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">1.4 </span><span style="FONT-SIZE: 9pt; COLOR: #555555">不要重复初始化变量 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">默认情况下，调用类的构造函数时， Java会把变量初始化成确定的值：所有的对象被设置成null，整数变量（byte、short、int、long）设置成0，float和double变量设置成0.0，逻辑值设置成false。当一个类从另一个类派生时，这一点尤其应该注意，因为用new关键词创建一个对象时，构造函数链中的所有构造函数都会被自动调用。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">1.5 </span><span style="FONT-SIZE: 9pt; COLOR: #555555">尽量指定类的final修饰符 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">带有final修饰符的类是不可派生的。在Java核心API中，有许多应用final的例子，例如java.lang.String。为String类指定final防止了人们覆盖length()方法。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">另外，如果指定一个类为final，则该类所有的方法都是final。Java编译器会寻找机会内联（inline）所有的final方法（这和具体的编译器实现有关）。此举能够使性能平均提高50%。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">1.6 </span><span style="FONT-SIZE: 9pt; COLOR: #555555">尽量使用局部变量 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">调用方法时传递的参数以及在调用中创建的临时变量都保存在栈（Stack）中，速度较快。其他变量，如静态变量、实例变量等，都在堆（Heap）中创建，速度较慢。另外，依赖于具体的编译器/JVM，局部变量还可能得到进一步优化。请参见《尽可能使用堆栈变量》。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">1.7 </span><span style="FONT-SIZE: 9pt; COLOR: #555555">乘法和除法 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">考虑下面的代码： </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
for (val = 0; val &lt; 100000; val +=5) { alterX = val * 8; myResult = val * 2; } </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
</span><span style="FONT-SIZE: 9pt; COLOR: #555555">用移位操作替代乘法操作可以极大地提高性能。下面是修改后的代码： </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
for (val = 0; val &lt; 100000; val += 5) { alterX = val &lt;&lt; 3; myResult = val &lt;&lt; 1; } </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
</span><span style="FONT-SIZE: 9pt; COLOR: #555555">修改后的代码不再做乘以8的操作，而是改用等价的左移3位操作，每左移1位相当于乘以2。相应地，右移1位操作相当于除以2。值得一提的是，虽然移位操作速度快，但可能使代码比较难于理解，所以最好加上一些注释。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">二、J2EE篇 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">前面介绍的改善性能技巧适合于大多数Java应用，接下来要讨论的问题适合于使用JSP、EJB或JDBC的应用。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">2.1 </span><span style="FONT-SIZE: 9pt; COLOR: #555555">使用缓冲标记 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">一些应用服务器加入了面向JSP的缓冲标记功能。例如，BEA的WebLogic Server从6.0版本开始支持这个功能，Open Symphony工程也同样支持这个功能。JSP缓冲标记既能够缓冲页面片断，也能够缓冲整个页面。当JSP页面执行时，如果目标片断已经在缓冲之中，则生成该片断的代码就不用再执行。页面级缓冲捕获对指定URL的请求，并缓冲整个结果页面。对于购物篮、目录以及门户网站的主页来说，这个功能极其有用。对于这类应用，页面级缓冲能够保存页面执行的结果，供后继请求使用。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">对于代码逻辑复杂的页面，利用缓冲标记提高性能的效果比较明显；反之，效果可能略逊一筹。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">请参见《用缓冲技术提高JSP应用的性能和稳定性》。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">2.2 </span><span style="FONT-SIZE: 9pt; COLOR: #555555">始终通过会话Bean访问实体Bean </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">直接访问实体Bean不利于性能。当客户程序远程访问实体Bean时，每一个get方法都是一个远程调用。访问实体Bean的会话Bean是本地的，能够把所有数据组织成一个结构，然后返回它的值。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">用会话Bean封装对实体Bean的访问能够改进事务管理，因为会话Bean只有在到达事务边界时才会提交。每一个对get方法的直接调用产生一个事务，容器将在每一个实体Bean的事务之后执行一个&ldquo;装入-读取&rdquo;操作。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">一些时候，使用实体Bean会导致程序性能不佳。如果实体Bean的唯一用途就是提取和更新数据，改成在会话Bean之内利用JDBC访问数据库可以得到更好的性能。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">2.3 </span><span style="FONT-SIZE: 9pt; COLOR: #555555">选择合适的引用机制 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">在典型的JSP应用系统中，页头、页脚部分往往被抽取出来，然后根据需要引入页头、页脚。当前，在JSP页面中引入外部资源的方法主要有两种：include指令，以及include动作。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
include</span><span style="FONT-SIZE: 9pt; COLOR: #555555">指令：例如&lt;%@ include file=&quot;copyright.html&quot; %&gt;。该指令在编译时引入指定的资源。在编译之前，带有include指令的页面和指定的资源被合并成一个文件。被引用的外部资源在编译时就确定，比运行时才确定资源更高效。 <br />
include动作：例如&lt;jsp:include page=&quot;copyright.jsp&quot; /&gt;。该动作引入指定页面执行后生成的结果。由于它在运行时完成，因此对输出结果的控制更加灵活。但时，只有当被引用的内容频繁地改变时，或者在对主页面的请求没有出现之前，被引用的页面无法确定时，使用include动作才合算。 <br />
2.4 在部署描述器中设置只读属性 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">实体Bean的部署描述器允许把所有get方法设置成&ldquo;只读&rdquo;。当某个事务单元的工作只包含执行读取操作的方法时，设置只读属性有利于提高性能，因为容器不必再执行存储操作。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">2.5 </span><span style="FONT-SIZE: 9pt; COLOR: #555555">缓冲对EJB Home的访问 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">EJB Home</span><span style="FONT-SIZE: 9pt; COLOR: #555555">接口通过JNDI名称查找获得。这个操作需要相当可观的开销。JNDI查找最好放入Servlet的init()方法里面。如果应用中多处频繁地出现EJB访问，最好创建一个EJBHomeCache类。EJBHomeCache类一般应该作为singleton实现。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">2.6 </span><span style="FONT-SIZE: 9pt; COLOR: #555555">为EJB实现本地接口 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">本地接口是EJB 2.0规范新增的内容，它使得Bean能够避免远程调用的开销。请考虑下面的代码。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
PayBeanHome home = (PayBeanHome) <br />
&nbsp; &nbsp; &nbsp; &nbsp; javax.rmi.PortableRemoteObject.narrow <br />
&nbsp; &nbsp; &nbsp; &nbsp; (ctx.lookup (&quot;PayBeanHome&quot;), PayBeanHome.class); <br />
PayBean bean = (PayBean) <br />
&nbsp; &nbsp; &nbsp; &nbsp; javax.rmi.PortableRemoteObject.narrow <br />
&nbsp; &nbsp; &nbsp; &nbsp; (home.create(), PayBean.class); </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
</span><span style="FONT-SIZE: 9pt; COLOR: #555555">第一个语句表示我们要寻找Bean的Home接口。这个查找通过JNDI进行，它是一个RMI调用。然后，我们定位远程对象，返回代理引用，这也是一个RMI调用。第二个语句示范了如何创建一个实例，涉及了创建IIOP请求并在网络上传输请求的stub程序，它也是一个RMI调用。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">要实现本地接口，我们必须作如下修改： </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
</span><span style="FONT-SIZE: 9pt; COLOR: #555555">方法不能再抛出java.rmi.RemoteException异常，包括从RemoteException派生的异常，比如TransactionRequiredException、TransactionRolledBackException和NoSuchObjectException。EJB提供了等价的本地异常，如TransactionRequiredLocalException、TransactionRolledBackLocalException和NoSuchObjectLocalException。 <br />
所有数据和返回值都通过引用的方式传递，而不是传递值。 <br />
本地接口必须在EJB部署的机器上使用。简而言之，客户程序和提供服务的组件必须在同一个JVM上运行。 <br />
如果Bean实现了本地接口，则其引用不可串行化。 <br />
请参见《用本地引用提高EJB访问效率》。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">2.7 </span><span style="FONT-SIZE: 9pt; COLOR: #555555">生成主键 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">在EJB之内生成主键有许多途径，下面分析了几种常见的办法以及它们的特点。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">利用数据库内建的标识机制（SQL Server的IDENTITY或Oracle的SEQUENCE）。这种方法的缺点是EJB可移植性差。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">由实体Bean自己计算主键值（比如做增量操作）。它的缺点是要求事务可串行化，而且速度也较慢。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">利用NTP之类的时钟服务。这要求有面向特定平台的本地代码，从而把Bean固定到了特定的OS之上。另外，它还导致了这样一种可能，即在多CPU的服务器上，同一个毫秒之内生成了两个主键。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">借鉴Microsoft的思路，在Bean中创建一个GUID。然而，如果不求助于JNI，Java不能确定网卡的MAC地址；如果使用JNI，则程序就要依赖于特定的OS。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">还有其他几种办法，但这些办法同样都有各自的局限。似乎只有一个答案比较理想：结合运用RMI和JNDI。先通过RMI注册把RMI远程对象绑定到JNDI树。客户程序通过JNDI进行查找。下面是一个例子： </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
public class keyGenerator extends UnicastRemoteObject implements Remote { <br />
private static long Keyvalue = System.currentTimeMillis(); <br />
public static synchronized long getKey() throws RemoteException { return Keyvalue++; } </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
2.8 </span><span style="FONT-SIZE: 9pt; COLOR: #555555">及时清除不再需要的会话 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">为了清除不再活动的会话，许多应用服务器都有默认的会话超时时间，一般为30分钟。当应用服务器需要保存更多会话时，如果内存容量不足，操作系统会把部分内存数据转移到磁盘，应用服务器也可能根据&ldquo;最近最频繁使用&rdquo;（Most Recently Used）算法把部分不活跃的会话转储到磁盘，甚至可能抛出&ldquo;内存不足&rdquo;异常。在大规模系统中，串行化会话的代价是很昂贵的。当会话不再需要时，应当及时调用HttpSession.invalidate()方法清除会话。HttpSession.invalidate()方法通常可以在应用的退出页面调用。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">2.9 </span><span style="FONT-SIZE: 9pt; COLOR: #555555">在JSP页面中关闭无用的会话 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">对于那些无需跟踪会话状态的页面，关闭自动创建的会话可以节省一些资源。使用如下page指令： </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
&lt;%@ page session=&quot;false&quot;%&gt; </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
2.10 Servlet</span><span style="FONT-SIZE: 9pt; COLOR: #555555">与内存使用 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">许多开发者随意地把大量信息保存到用户会话之中。一些时候，保存在会话中的对象没有及时地被垃圾回收机制回收。从性能上看，典型的症状是用户感到系统周期性地变慢，却又不能把原因归于任何一个具体的组件。如果监视JVM的堆空间，它的表现是内存占用不正常地大起大落。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">解决这类内存问题主要有二种办法。第一种办法是，在所有作用范围为会话的Bean中实现HttpSessionBindingListener接口。这样，只要实现valueUnbound()方法，就可以显式地释放Bean使用的资源。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">另外一种办法就是尽快地把会话作废。大多数应用服务器都有设置会话作废间隔时间的选项。另外，也可以用编程的方式调用会话的setMaxInactiveInterval()方法，该方法用来设定在作废会话之前，Servlet容器允许的客户请求的最大间隔时间，以秒计。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">2.11 HTTP Keep-Alive </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">Keep-Alive</span><span style="FONT-SIZE: 9pt; COLOR: #555555">功能使客户端到服务器端的连接持续有效，当出现对服务器的后继请求时，Keep-Alive功能避免了建立或者重新建立连接。市场上的大部分Web服务器，包括iPlanet、IIS和Apache，都支持HTTP Keep-Alive。对于提供静态内容的网站来说，这个功能通常很有用。但是，对于负担较重的网站来说，这里存在另外一个问题：虽然为客户保留打开的连接有一定的好处，但它同样影响了性能，因为在处理暂停期间，本来可以释放的资源仍旧被占用。当Web服务器和应用服务器在同一台机器上运行时，Keep-Alive功能对资源利用的影响尤其突出。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">2.12 JDBC</span><span style="FONT-SIZE: 9pt; COLOR: #555555">与Unicode </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">想必你已经了解一些使用JDBC时提高性能的措施，比如利用连接池、正确地选择存储过程和直接执行的SQL、从结果集删除多余的列、预先编译SQL语句，等等。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">除了这些显而易见的选择之外，另一个提高性能的好选择可能就是把所有的字符数据都保存为Unicode（代码页13488）。Java以Unicode形式处理所有数据，因此，数据库驱动程序不必再执行转换过程。但应该记住：如果采用这种方式，数据库会变得更大，因为每个Unicode字符需要2个字节存储空间。另外，如果有其他非Unicode的程序访问数据库，性能问题仍旧会出现，因为这时数据库驱动程序仍旧必须执行转换过程。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">2.13 JDBC</span><span style="FONT-SIZE: 9pt; COLOR: #555555">与I/O </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">如果应用程序需要访问一个规模很大的数据集，则应当考虑使用块提取方式。默认情况下，JDBC每次提取32行数据。举例来说，假设我们要遍历一个5000行的记录集，JDBC必须调用数据库157次才能提取到全部数据。如果把块大小改成512，则调用数据库的次数将减少到10次。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">在一些情形下这种技术无效。例如，如果使用可滚动的记录集，或者在查询中指定了FOR UPDATE，则块操作方式不再有效。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">1.14 </span><span style="FONT-SIZE: 9pt; COLOR: #555555">内存数据库 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">许多应用需要以用户为单位在会话对象中保存相当数量的数据，典型的应用如购物篮和目录等。由于这类数据可以按照行/列的形式组织，因此，许多应用创建了庞大的Vector或HashMap。在会话中保存这类数据极大地限制了应用的可伸缩性，因为服务器拥有的内存至少必须达到每个会话占用的内存数量乘以并发用户最大数量，它不仅使服务器价格昂贵，而且垃圾收集的时间间隔也可能延长到难以忍受的程度。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">一些人把购物篮/目录功能转移到数据库层，在一定程度上提高了可伸缩性。然而，把这部分功能放到数据库层也存在问题，且问题的根源与大多数关系数据库系统的体系结构有关。对于关系数据库来说，运行时的重要原则之一是确保所有的写入操作稳定、可靠，因而，所有的性能问题都与物理上把数据写入磁盘的能力有关。关系数据库力图减少I/O操作，特别是对于读操作，但实现该目标的主要途径只是执行一套实现缓冲机制的复杂算法，而这正是数据库层第一号性能瓶颈通常总是CPU的主要原因。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">一种替代传统关系数据库的方案是，使用在内存中运行的数据库（In-memory Database），例如TimesTen。内存数据库的出发点是允许数据临时地写入，但这些数据不必永久地保存到磁盘上，所有的操作都在内存中进行。这样，内存数据库不需要复杂的算法来减少I/O操作，而且可以采用比较简单的加锁机制，因而速度很快。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">三、GUI篇 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">这一部分介绍的内容适合于图形用户界面的应用（Applet和普通应用），要用到AWT或Swing。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">3.1 </span><span style="FONT-SIZE: 9pt; COLOR: #555555">用JAR压缩类文件 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">Java</span><span style="FONT-SIZE: 9pt; COLOR: #555555">档案文件（JAR文件）是根据JavaBean标准压缩的文件，是发布JavaBean组件的主要方式和推荐方式。JAR档案有助于减少文件体积，缩短下载时间。例如，它有助于Applet提高启动速度。一个JAR文件可以包含一个或者多个相关的Bean以及支持文件，比如图形、声音、HTML和其他资源。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">要在HTML/JSP文件中指定JAR文件，只需在Applet标记中加入ARCHIVE = &quot;name.jar&quot;声明。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">请参见《使用档案文件提高 applet 的加载速度》。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">3.2 </span><span style="FONT-SIZE: 9pt; COLOR: #555555">提示Applet装入进程 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">你是否看到过使用Applet的网站，注意到在应该运行Applet的地方出现了一个占位符？当Applet的下载时间较长时，会发生什么事情？最大的可能就是用户掉头离去。在这种情况下，显示一个Applet正在下载的信息无疑有助于鼓励用户继续等待。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">下面我们来看看一种具体的实现方法。首先创建一个很小的Applet，该Applet负责在后台下载正式的Applet： </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
import java.applet.Applet;<br />
import java.applet.AppletStub;<br />
import java.awt.Label;<br />
import java.awt.Graphics;<br />
import java.awt.GridLayout; <br />
public class PreLoader extends Applet implements Runnable, AppletStub {<br />
&nbsp; &nbsp;String largeAppletName;<br />
&nbsp; &nbsp;Label label;<br />
&nbsp; &nbsp;public void init() {<br />
&nbsp; &nbsp; &nbsp; // </span><span style="FONT-SIZE: 9pt; COLOR: #555555">要求装载的正式Applet<br />
&nbsp; &nbsp; &nbsp; largeAppletName = getParameter(&quot;applet&quot;);<br />
&nbsp; &nbsp; &nbsp; // &ldquo;请稍等&rdquo;提示信息<br />
&nbsp; &nbsp; &nbsp; label = new Label(&quot;请稍等...&quot; + largeAppletName);<br />
&nbsp; &nbsp; &nbsp; add(label);<br />
&nbsp; &nbsp;} <br />
&nbsp; &nbsp;public void run(){<br />
&nbsp; &nbsp; &nbsp; try {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// 获得待装载Applet的类<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Class largeAppletClass = Class.forName(largeAppletName); <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// 创建待装载Applet的实例<br />
&nbsp; &nbsp; &nbsp; &nbsp; Applet largeApplet = (Applet)largeAppletClass.newInstance(); <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// 设置该Applet的Stub程序<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;largeApplet.setStub(this); <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// 取消&ldquo;请稍等&rdquo;信息<br />
&nbsp; &nbsp; &nbsp; &nbsp; remove(label); <br />
&nbsp; &nbsp; &nbsp; &nbsp; // 设置布局<br />
&nbsp; &nbsp; &nbsp; &nbsp; setLayout(new GridLayout(1, 0));<br />
&nbsp; &nbsp; &nbsp; &nbsp; add(largeApplet); <br />
&nbsp; &nbsp; &nbsp; &nbsp; // 显示正式的Applet<br />
&nbsp; &nbsp; &nbsp; &nbsp; largeApplet.init();<br />
&nbsp; &nbsp; &nbsp; &nbsp; largeApplet.start();<br />
&nbsp; &nbsp; &nbsp;}<br />
&nbsp; &nbsp; &nbsp;catch (Exception ex) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; // 显示错误信息<br />
&nbsp; &nbsp; &nbsp; &nbsp; label.setText(&quot;不能装入指定的Applet&quot;);<br />
&nbsp; &nbsp; &nbsp;} <br />
&nbsp; &nbsp; &nbsp;// 刷新屏幕<br />
&nbsp; &nbsp; &nbsp;validate();<br />
&nbsp; } <br />
&nbsp; public void appletResize(int width, int height) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; // 把appletResize调用从stub程序传递到Applet<br />
&nbsp; &nbsp; &nbsp; &nbsp;resize(width, height);<br />
&nbsp; }<br />
}</span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
</span><span style="FONT-SIZE: 9pt; COLOR: #555555">编译后的代码小于2K，下载速度很快。代码中有几个地方值得注意。首先，PreLoader实现了AppletStub接口。一般地，Applet从调用者判断自己的codebase。在本例中，我们必须调用setStub()告诉Applet到哪里提取这个信息。另一个值得注意的地方是，AppletStub接口包含许多和Applet类一样的方法，但appletResize()方法除外。这里我们把对appletResize()方法的调用传递给了resize()方法。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">3.3 </span><span style="FONT-SIZE: 9pt; COLOR: #555555">在画出图形之前预先装入它 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">ImageObserver</span><span style="FONT-SIZE: 9pt; COLOR: #555555">接口可用来接收图形装入的提示信息。ImageObserver接口只有一个方法imageUpdate()，能够用一次repaint()操作在屏幕上画出图形。下面提供了一个例子。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
public boolean imageUpdate(Image img, int flags, int x, int y, int w, int h) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if ((flags &amp; ALLBITS) !=0 {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; repaint();<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;else if (flags &amp; (ERROR |ABORT )) != 0) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; error = true;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // </span><span style="FONT-SIZE: 9pt; COLOR: #555555">文件没有找到，考虑显示一个占位符<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; repaint();<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return (flags &amp; (ALLBITS | ERROR| ABORT)) == 0;<br />
} </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
</span><span style="FONT-SIZE: 9pt; COLOR: #555555">当图形信息可用时，imageUpdate()方法被调用。如果需要进一步更新，该方法返回true；如果所需信息已经得到，该方法返回false。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">3.4 </span><span style="FONT-SIZE: 9pt; COLOR: #555555">覆盖update方法 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">update()</span><span style="FONT-SIZE: 9pt; COLOR: #555555">方法的默认动作是清除屏幕，然后调用paint()方法。如果使用默认的update()方法，频繁使用图形的应用可能出现显示闪烁现象。要避免在paint()调用之前的屏幕清除操作，只需按照如下方式覆盖update()方法： </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
public void update(Graphics g) {<br />
&nbsp; &nbsp; paint(g);<br />
} </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
</span><span style="FONT-SIZE: 9pt; COLOR: #555555">更理想的方案是：覆盖update()，只重画屏幕上发生变化的区域，如下所示： </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
public void update(Graphics g) {<br />
&nbsp; &nbsp; g.clipRect(x, y, w, h);<br />
&nbsp; &nbsp; paint(g);<br />
} </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
3.5 </span><span style="FONT-SIZE: 9pt; COLOR: #555555">延迟重画操作 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">对于图形用户界面的应用来说，性能低下的主要原因往往可以归结为重画屏幕的效率低下。当用户改变窗口大小或者滚动一个窗口时，这一点通常可以很明显地观察到。改变窗口大小或者滚动屏幕之类的操作导致重画屏幕事件大量地、快速地生成，甚至超过了相关代码的执行速度。对付这个问题最好的办法是忽略所有&ldquo;迟到&rdquo;的事件。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">建议在这里引入一个数毫秒的时差，即如果我们立即接收到了另一个重画事件，可以停止处理当前事件转而处理最后一个收到的重画事件；否则，我们继续进行当前的重画过程。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">如果事件要启动一项耗时的工作，分离出一个工作线程是一种较好的处理方式；否则，一些部件可能被&ldquo;冻结&rdquo;，因为每次只能处理一个事件。下面提供了一个事件处理的简单例子，但经过扩展后它可以用来控制工作线程。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
public static void runOnce(String id, final long milliseconds) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;synchronized(e_queue) { // e_queue: </span><span style="FONT-SIZE: 9pt; COLOR: #555555">所有事件的集合<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (!e_queue.containsKey(id)) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; e_queue.put(token, new LastOne());<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; final LastOne lastOne = (LastOne) e_queue.get(token);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; final long time = System.currentTimeMillis(); // 获得当前时间<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; lastOne.time = time; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (new Thread() {public void run() {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (milliseconds &gt; 0) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;try {Thread.sleep(milliseconds);} // 暂停线程<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;catch (Exception ex) {}<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;synchronized(lastOne.running) { // 等待上一事件结束<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (lastOne.time != time) // 只处理最后一个事件<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;return;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }}).start();<br />
} <br />
private static Hashtable e_queue = new Hashtable(); <br />
private static class LastOne {<br />
&nbsp; &nbsp; &nbsp; &nbsp;public long time=0;<br />
&nbsp; &nbsp; &nbsp; &nbsp;public Object running = new Object();<br />
} </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
3.6 </span><span style="FONT-SIZE: 9pt; COLOR: #555555">使用双缓冲区 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">在屏幕之外的缓冲区绘图，完成后立即把整个图形显示出来。由于有两个缓冲区，所以程序可以来回切换。这样，我们可以用一个低优先级的线程负责画图，使得程序能够利用空闲的CPU时间执行其他任务。下面的伪代码片断示范了这种技术。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
Graphics myGraphics;<br />
Image myOffscreenImage = createImage(size().width, size().height);<br />
Graphics offscreenGraphics = myOffscreenImage.getGraphics(); <br />
offscreenGraphics.drawImage(img, 50, 50, this);<br />
myGraphics.drawImage(myOffscreenImage, 0, 0, this); </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
3.7 </span><span style="FONT-SIZE: 9pt; COLOR: #555555">使用BufferedImage </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">Java JDK 1.2</span><span style="FONT-SIZE: 9pt; COLOR: #555555">使用了一个软显示设备，使得文本在不同的平台上看起来相似。为实现这个功能，Java必须直接处理构成文字的像素。由于这种技术要在内存中大量地进行位复制操作，早期的JDK在使用这种技术时性能不佳。为解决这个问题而提出的Java标准实现了一种新的图形类型，即BufferedImage。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">BufferedImage</span><span style="FONT-SIZE: 9pt; COLOR: #555555">子类描述的图形带有一个可访问的图形数据缓冲区。一个BufferedImage包含一个ColorModel和一组光栅图形数据。这个类一般使用RGB（红、绿、蓝）颜色模型，但也可以处理灰度级图形。它的构造函数很简单，如下所示： </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
public BufferedImage (int width, int height, int imageType) </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
ImageType</span><span style="FONT-SIZE: 9pt; COLOR: #555555">允许我们指定要缓冲的是什么类型的图形，比如5-位RGB、8-位RGB、灰度级等。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">3.8 </span><span style="FONT-SIZE: 9pt; COLOR: #555555">使用VolatileImage </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">许多硬件平台和它们的操作系统都提供基本的硬件加速支持。例如，硬件加速一般提供矩形填充功能，和利用CPU完成同一任务相比，硬件加速的效率更高。由于硬件加速分离了一部分工作，允许多个工作流并发进行，从而缓解了对CPU和系统总线的压力，使得应用能够运行得更快。利用VolatileImage可以创建硬件加速的图形以及管理图形的内容。由于它直接利用低层平台的能力，性能的改善程度主要取决于系统使用的图形适配器。VolatileImage的内容随时可能丢失，也即它是&ldquo;不稳定的（volatile）&rdquo;。因此，在使用图形之前，最好检查一下它的内容是否丢失。VolatileImage有两个能够检查内容是否丢失的方法： </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
public abstract int validate(GraphicsConfiguration gc);<br />
public abstract Boolean contentsLost(); </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
</span><span style="FONT-SIZE: 9pt; COLOR: #555555">每次从VolatileImage对象复制内容或者写入VolatileImage时，应该调用validate()方法。contentsLost()方法告诉我们，自从最后一次validate()调用之后，图形的内容是否丢失。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">虽然VolatileImage是一个抽象类，但不要从它这里派生子类。VolatileImage应该通过Component.createVolatileImage()或者GraphicsConfiguration.createCompatibleVolatileImage()方法创建。 </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">3.9 </span><span style="FONT-SIZE: 9pt; COLOR: #555555">使用Window Blitting </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555">进行滚动操作时，所有可见的内容一般都要重画，从而导致大量不必要的重画工作。许多操作系统的图形子系统，包括WIN32 GDI、MacOS和X/Windows，都支持Window Blitting技术。Window Blitting技术直接在屏幕缓冲区中把图形移到新的位置，只重画新出现的区域。要在Swing应用中使用Window Blitting技术，设置方法如下： </span></div>
<div align="left"><span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
setScrollMode(int mode); </span></div>
<span style="FONT-SIZE: 9pt; COLOR: #555555"><br />
</span><span style="FONT-SIZE: 9pt; COLOR: #555555">在大多数应用中，使用这种技术能够提高滚动速度。只有在一种情形下，Window Blitting会导致性能降低，即应用在后台进行滚动操作。如果是用户在滚动一个应用，那么它总是在前台，无需担心任何负面影响。</span></div>
</div>
          <br/>
          <span style="color:red;">
            <a href="http://murain.javaeye.com/blog/34753#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 17 Nov 2006 15:53:57 +0800</pubDate>
        <link>http://murain.javaeye.com/blog/34753</link>
        <guid>http://murain.javaeye.com/blog/34753</guid>
      </item>
      <item>
        <title>刚拿到《设计模式解析》《javascript高级编程指南》</title>
        <author>muRain</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://murain.javaeye.com">muRain</a>&nbsp;
          链接：<a href="http://murain.javaeye.com/blog/32882" style="color:red;">http://murain.javaeye.com/blog/32882</a>&nbsp;
          发表时间: 2006年11月07日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          设计模式一直有想法看，总是觉得需要有强的设计思想才能看明白，《设计模式解析》介绍否认了我的这个理解，就先从它入手了！
          <br/>
          <span style="color:red;">
            <a href="http://murain.javaeye.com/blog/32882#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 07 Nov 2006 10:58:07 +0800</pubDate>
        <link>http://murain.javaeye.com/blog/32882</link>
        <guid>http://murain.javaeye.com/blog/32882</guid>
      </item>
      <item>
        <title>最近都在看理论看oo思想，一点体会</title>
        <author>muRain</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://murain.javaeye.com">muRain</a>&nbsp;
          链接：<a href="http://murain.javaeye.com/blog/31798" style="color:red;">http://murain.javaeye.com/blog/31798</a>&nbsp;
          发表时间: 2006年10月31日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          系统中的各个类职责分明，需要协作的时候每个类只承担自己的职责所在，调用者也不需要考虑要调用的类对象是不是符合自己的条件，他应该是以一种假设自己的条件都满足的状况下进行工作，<br />越来越糊涂了，，汗
          <br/>
          <span style="color:red;">
            <a href="http://murain.javaeye.com/blog/31798#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 31 Oct 2006 11:06:43 +0800</pubDate>
        <link>http://murain.javaeye.com/blog/31798</link>
        <guid>http://murain.javaeye.com/blog/31798</guid>
      </item>
      <item>
        <title>61条面向对象设计的经验原则-《OOD启示录》</title>
        <author>muRain</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://murain.javaeye.com">muRain</a>&nbsp;
          链接：<a href="http://murain.javaeye.com/blog/31272" style="color:red;">http://murain.javaeye.com/blog/31272</a>&nbsp;
          发表时间: 2006年10月30日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          最近偶尔翻起了好久以前买的这本书，网上摘录来简要：<br />(1)所有数据都应该隐藏在所在的类的内部。p13<br /> <br />(2)类的使用者必须依赖类的共有接口，但类不能依赖它的使用者。p15<br /> <br />(3)尽量减少类的协议中的消息。p16<br /> <br />(4)实现所有类都理解的最基本公有接口[例如，拷贝操作(深拷贝和浅拷贝)、相等性判断、正确输出内容、从ASCII描述解析等等]。 p16<br /> <br />(5)不要把实现细节(例如放置共用代码的私有函数)放到类的公有接口中。p17<br />如果类的两个方法有一段公共代码，那么就可以创建一个防止这些公共代码的私有函数。<br /> <br />(6)不要以用户无法使用或不感兴趣的东西扰乱类的公有接口。p17<br /> <br />(7)类之间应该零耦合，或者只有导出耦合关系。也即，一个类要么同另一个类毫无关系，要么只使用另一个类的公有接口中的操作。 p18<br /> <br />(8)类应该只表示一个关键抽象。p19<br />包中的所有类对于同一类性质的变化应该是共同封闭的。一个变化若对一个包影响，则将对包中的所有类产生影响，而对其他的包不造成任何影响 .<br /> <br />(9)把相关的数据和行为集中放置。p19<br />设计者应当留意那些通过get之类操作从别的对象中获取数据的对象。这种类型的行为暗示着这条经验原则被违反了。<br /> <br />(10)把不相关的信息放在另一个类中(也即：互不沟通的行为)。p19<br />朝着稳定的方向进行依赖.<br /> <br />(11)确保你为之建模的抽象概念是类，而不只是对象扮演的角色。p23<br /> <br />(12)在水平方向上尽可能统一地分布系统功能，也即：按照设计，顶层类应当统一地共享工作。p30<br /> <br />(13)在你的系统中不要创建全能类/对象。对名字包含Driver、Manager、System、Susystem的类要特别多加小心。p30<br />规划一个接口而不是实现一个接口。<br /> <br />(14)对公共接口中定义了大量访问方法的类多加小心。大量访问方法意味着相关数据和行为没有集中存放。p30<br /> <br />(15)对包含太多互不沟通的行为的类多加小心。p31<br />这个问题的另一表现是在你的应用程序中的类的公有接口中创建了很多的get和set函数。<br /> <br />(16)在由同用户界面交互的面向对象模型构成的应用程序中，模型不应该依赖于界面，界面则应当依赖于模型。p33<br /> <br />(17)尽可能地按照现实世界建模(我们常常为了遵守系统功能分布原则、避免全能类原则以及集中放置相关数据和行为的原则而违背这条原则) 。p36<br /> <br />(18)从你的设计中去除不需要的类。p38<br />一般来说，我们会把这个类降级成一个属性。<br /> <br />(19)去除系统外的类。p39<br />系统外的类的特点是，抽象地看它们只往系统领域发送消息但并不接受系统领域内其他类发出的消息。<br /> <br />(20)不要把操作变成类。质疑任何名字是动词或者派生自动词的类，特别是只有一个有意义行为的类。考虑一下那个有意义的行为是否应当迁移到已经存在或者尚未发现的某个类中。p40<br /> <br />(21)我们在创建应用程序的分析模型时常常引入代理类。在设计阶段，我们常会发现很多代理没有用的，应当去除。p43<br /> <br />(22)尽量减少类的协作者的数量。p52<br />一个类用到的其他类的数目应当尽量少。<br /> <br />(23)尽量减少类和协作者之间传递的消息的数量。p55<br /> <br />(24)尽量减少类和协作者之间的协作量，也即：减少类和协作者之间传递的不同消息的数量。p55<br /> <br />(25)尽量减少类的扇出，也即：减少类定义的消息数和发送的消息数的乘积。p55<br /> <br />(26)如果类包含另一个类的对象，那么包含类应当给被包含的对象发送消息。也即：包含关系总是意味着使用关系。p55<br /> <br />(27)类中定义的大多数方法都应当在大多数时间里使用大多数数据成员。p57<br /> <br />(28)类包含的对象数目不应当超过开发者短期记忆的容量。这个数目常常是6。p57<br />当类包含多于6个数据成员时，可以把逻辑相关的数据成员划分为一组，然后用一个新的包含类去包含这一组成员。<br /> <br />(29)让系统功能在窄而深的继承体系中垂直分布。p58<br /> <br />(30)在实现语义约束时，最好根据类定义来实现。这常常会导致类泛滥成灾，在这种情况下，约束应当在类的行为中实现，通常是在构造函数中实现，但不是必须如此。p60<br /> <br />(31)在类的构造函数中实现语义约束时，把约束测试放在构造函数领域所允许的尽量深的包含层次中。p60<br /> <br />(32)约束所依赖的语义信息如果经常改变，那么最好放在一个集中式的第3方对象中。p60<br /> <br />(33)约束所依赖的语义信息如果很少改变，那么最好分布在约束所涉及的各个类中。p60<br /> <br />(34)类必须知道它包含什么，但是不能知道谁包含它。p61<br /> <br />(35)共享字面范围(也就是被同一个类所包含)的对象相互之间不应当有使用关系。p61<br /> <br />(36)继承只应被用来为特化层次结构建模。p74<br /> <br />(37)派生类必须知道基类，基类不应该知道关于它们的派生类的任何信息。p74<br /> <br />(38)基类中的所有数据都应当是私有的，不要使用保护数据。p75<br />类的设计者永远都不应该把类的使用者不需要的东西放在公有接口中。<br /> <br />(39)在理论上，继承层次体系应当深一点，越深越好。p77<br /> <br />(40)在实践中，继承层次体系的深度不应当超出一个普通人的短期记忆能力。一个广为接受的深度值是6。p77<br /> <br />(41)所有的抽象类都应当是基类。p81<br /> <br />(42)所有的基类都应当是抽象类。p82<br /> <br />(43)把数据、行为和/或接口的共性尽可能地放到继承层次体系的高端。p85<br /> <br />(44)如果两个或更多个类共享公共数据(但没有公共行为)，那么应当把公共数据放在一个类中，每个共享这个数据的类都包含这个类。 p88<br /> <br />(45)如果两个或更多个类有共同的数据和行为(就是方法)，那么这些类的每一个都应当从一个表示了这些数据和方法的公共基类继承。 p89<br /> <br />(46)如果两个或更多