<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>JavaEye论坛精彩帖子</title>
    <description>JavaEye论坛精彩帖子 - Java编程，Ruby编程，微软.net，AJAX，敏捷软件开发，综合软件技术</description>
    <link>http://www.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>MySQL数据库操作实战</title>
        <author>JavaEye网站</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://touya.javaeye.com">touya</a>&nbsp;
                    链接：<a href="http://www.javaeye.com/topic/251307" style="color:red;">http://www.javaeye.com/topic/251307</a>&nbsp;
          发表时间: 2008年10月10日
          <br/>
          声明：本文系JavaEye网站发布的原创文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          昨天项目发布，要做数据移行，要实现的功能很变态，时间很紧迫，基本上是使出了全身解数，才能有快又准地完成工作，期间发现很多小技巧串联起来使用，效果的确非常好。<br /><br />武器：<br />1 mysqldump+mysql命令=>数据的导入导出，备份恢复<br /><br />2 perl命令行=>很多时候，你有一个想法，它就能只用一句话，帮你实现它，省得编写很多代码的麻烦<br /><br />3 shell命令=>组合拳，单独的命令谁都会，组合起来使用，需要一些经验的积累<br /><br />4 强大的编辑器支持UltraEdit/EmEdit等=>UltraEdit最专业，但不支持字符集转换；EmEdit支持字符集转换，且最新版有很多插件可供选择，基本够常见的操作了，对于WEB开发人员来说，常需要在GB2312、utf8或者其他国家语言euc-jp、shift-jis等等编码中转来转去，EmEdit的确是首选，特别赞的还有EmEdit的录制键盘动作并播放的功能<br /><br />常见组合：<br />1 数据移行时：<br />mysqldump --opt -t -h locahost -P 23236 -p mydb table -w "id>27000" > table.sql<br />>输入密码<br />OK , mydb中的表table中id大于27000的所有数据列的INSERT语句就保存到table.sql中了。-h、-P、-p就不多说了，--opt 是mysqldump的最常用组合选项，可以认为是加速语句；-t 则很有用，表示--no-create-info，就是你不需要建表语句，只需要数据（INSERT语句）；-w就是where条件，也非常有用，让你有选择的导出数据。其他常见选项有：-d ： 只需要建表语句，不需要insert语句；-c ：给每个insert语句加上列名（field），默认时，insert语句是“insert into `table` values (1),(2),(3);”，而加上-c后，就变成了“insert into table(id) values (1),(2),(3);”。有什么用？一会儿自会明白。<br /><br />如果表中数据非常多，导出insert语句就非常长（默认情况下，一个表的所有数据都在一个insert语句中，不换行），要查看这个文件常常引起死机，无论是linux下用vi，less查看，还是windows用EmEdit等编辑器，因为他们都是以行为单位load数据的，一行的数据过大就会内存占用过大，怎么办？<br /><br />你发现，只需要在EmEdit里查找“),(”这个字符串，将它替换为"),\n("，也就是加个换行，就行了，于是就这么做，结果发现——当数据量达到上万时，EmEdit就像数羊一样一个一个替换，等它换完，你都睡了一觉了！！<br /><br />怎么办呢？你当然知道写个脚本就行了，很容易，但是花的时间多，还需要测试，有简单办法吗？恩，那就轮到perl单命令行上场了：<br />perl -i.bak -pe 's/\),\(/\),\n\(/g' table.sql<br />OK，替换结束，且生成了一个备份文件table.sql.bak，如果你发现写错了，效果不对，还有救：）<br /><br />当然了，如果你会用sed、awk等强大的编辑工具，这个也是小case，这里就不多说了。<br /><br />2 数据导入<br />导出的数据经过处理，就要导入目标数据库，现在有一个表，字段非常多，其中主键为id，导入另一个数据库的相同表里，但是id不想直接插入，而是只要数据，id最好是自动生成的，跟在目标数据表最末一条数据之后即可，以免主键冲突。怎么搞呢？<br />这时候就能用上刚才说到的-c选项了，加了-c，导出的数据有field列，只需要把其中的id列都替换为空即可：）<br />这个替换过程，用EmEdit或上面说到的perl命令行都可以，只是速度的区别了。<br /><br />3 接上面的问题，老数据导入了新的表，生成了一串新的连续id，但和老数据完全不一样了，现在有其他几张表中使用了老数据的id作为外键，要把它们统一改为新的id。例如：<br />老数据中table.id=10010，有一个表table2.table=10010,是外键关系，要把这个table2.table改为新数据表中自动产生的那个id，就比如说是6041.现在手头上的数据只有老id列表，且知道插入新表中的id是自增的，每次加1，从6041开始，怎么快速修改 table2，table3中的相应外键呢？<br />一个文件：<br />10010<br />10201<br />11301<br />11499<br />……<br /><br />要替换成：<br />update table2 set table=6041 where id=10010;<br />update table2 set table=6042 where id=10201;<br />update table2 set table=6043 where id=11301;<br />update table2 set table=6044 where id=11499;<br />……<br /><br />我们可以这么组合手头的工具：<br />perl -i.bak -pe 'BEGIN {$x=6041} s/^(\d+)/update table2 set table=$x where id=$1;/ ; $x++' file.txt<br />OK，搞定<br />这个例子的特色其实就是perl命令行中的BEGIN的用法，有begin，当然有end，具体就自己查查文档吧：）<br /><br />4 还有EmEdit中的录制键盘动作并播放也很常用，毕竟写正则比较费脑子，用工具虽然处理速度慢一些，但是思考速度要快很多。今天碰到一件事，需要把服务器上某个目录中的bmp图片都转为jpg格式，也是要使用组合拳的，其中就用到了EmEdit的录制播放功能。<br />已知某目录下有N多bmp图片，通过convert命令可以转格式（安装Image::Magick模块即可）<br />怎么搞？<br />find ./ -name "*.bmp" > bmp.txt<br />先拿到文件列表再说。<br /><br />在EmEdit中打开该列表，一行行文件名。<br />你发现你想做的就是：<br />./dir1/dir2/file1.bmp<br />./dir1/dir2/file2.bmp<br />./dir1/dir2/file3.bmp<br />转变为：<br />convert ./dir1/dir2/file1.bmp ./dir1/dir2/file1.jpg<br />convert ./dir1/dir2/file2.bmp ./dir1/dir2/file2.jpg<br />convert ./dir1/dir2/file3.bmp ./dir1/dir2/file3.jpg<br /><br />这是一个非常有规律的动作，在行首，按shift+End（选择），ctrl+c（复制），End（到行尾），空格，ctrl+v（粘贴），backspace三次，输入jpg，HOME到行首，输入convert空格，下一行，HOME到行首<br />只要把这个操作序列记录下来，对每行都是一样操作，这时就可以使用EmEdit的录制+播放功能了，录制完成后，按F4快捷键，一爽到底~~yeah~~<br /><br />运行之后要删除原图：<br />在shell下：<br />find ./ -name "*.bmp" | xargs -n1 rm -f<br />(找到本目录下的bmp图片文件名列表，一个一个传给rm -f，删之，慎用)<br /><br />5 数据库恢复（从bin-log中恢复数据库也是一个非常重要的技巧），出现误操作或DB服务器不幸意外丢失数据时，常常要查bin-log来试图恢复数据，下次接着聊吧，累了……<br /><br />呵呵，乱写写，好像不只是数据库操作了，不过挺实在，欢迎各位拍砖吧
          <br/><br/>
          <span style="color:red;">
            <a href="http://www.javaeye.com/topic/251307" style="color:red;">已有 <strong>5</strong> 人发表回复，猛击-&gt;&gt;<strong>这里</strong>&lt;&lt;-参与讨论</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;">Windows7在微软WinHEC 2008上揭开神秘面纱</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, 10 Oct 2008 15:03:42 +0800</pubDate>
        <link>http://www.javaeye.com/topic/251307</link>
        <guid>http://www.javaeye.com/topic/251307</guid>
      </item>
          <item>
        <title>写给我的团队成员（二）—— 编程，乐趣何在？</title>
        <author>JavaEye网站</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://neora.javaeye.com">neora</a>&nbsp;
                    链接：<a href="http://www.javaeye.com/topic/243210" style="color:red;">http://www.javaeye.com/topic/243210</a>&nbsp;
          发表时间: 2008年09月17日
          <br/>
          声明：本文系JavaEye网站发布的原创文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p class="MsoNormal"><span style="font-family: 宋体;">前言：这是《写给我的团队成员》系列的第二期。</span></p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal"><span style="font-family: 宋体;">第一期参见<a href="../../../topic/241569">《</a></span><a href="../../../topic/241569"><span style="font-family: 宋体;">写给我的团队成员&mdash;&mdash;什么是BUG?》</span></a></p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal" style="text-align: center;"><span style="font-size: x-large;"><span lang="EN-US">&nbsp;编程，乐趣何在？</span></span></p>
<h1><span lang="EN-US"><span>1.<span style="font-family: &quot;Times New Roman&quot;; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp; </span></span></span><span style="font-family: 宋体;">什么是软件开发？</span></h1>
<p class="MsoNormal"><span lang="EN-US">&nbsp;</span></p>
<p class="MsoNormal"><span style="font-family: 宋体;">软件最基本的目标是让计算机硬件（运算</span><span lang="EN-US">/</span><span style="font-family: 宋体;">存储</span><span lang="EN-US">/</span><span style="font-family: 宋体;">输入输出）按照人们预想的规则来工作。我们又管软件叫程序，软件工程师定制编写一个&ldquo;顺序、序列&rdquo;，机器就按照这个序列来执行。软件开发，就是这个定制编写序列的过程。</span></p>
<p class="MsoNormal"><span lang="EN-US">&nbsp;</span></p>
<h1><span lang="EN-US"><span>2.<span style="font-family: &quot;Times New Roman&quot;; font-size-adjust: none; font-stretch: normal;">&nbsp;</span></span></span><span style="font-family: 宋体;">原本的乐趣：挑战和控制欲</span></h1>
<p class="MsoNormal" style="text-indent: 21pt;"><span style="font-family: 宋体;">解数学题</span><span lang="EN-US">,</span><span style="font-family: 宋体;">是很多理科学生都很喜欢的一项活动。特别是在高中时期，证明出一道立体几何或者在模拟考试中第一个交卷儿都是非常令人羡慕的，虚荣心和满足感也会随之飘飘然。同时，多数中学的老师和一些大学老师，喜欢把软件开发归于数学的范畴。按这个推理，喜欢数学的都应该喜欢编程。但事实并非如此。</span></p>
<p class="MsoNormal"><span lang="EN-US">&nbsp;</span></p>
<p class="MsoNormal" style="text-indent: 21pt;"><span style="font-family: 宋体;">无论男性还是女性，我们都有控制的欲望。在控制不了&ldquo;人&rdquo;这种活物的情况下，能够控制一台机器让他按照我们的意愿来运行，会带来极大的快感。我</span><span lang="EN-US">30</span><span style="font-family: 宋体;">多岁了还喜欢玩遥控汽车，但一直羞于去玩具店购买，直到我儿子</span><span lang="EN-US">2</span><span style="font-family: 宋体;">岁以后。玩跑车，用手臂和脚尖控制一台</span><span lang="EN-US">400</span><span style="font-family: 宋体;">马力的怒吼的发动机当然更加过瘾，但显然太昂贵了。编程则可能是达到这一目标最廉价又最冠冕堂皇的一种方式。而且编程这种活动似乎在发挥创造力和满足自我陶醉心理上有更大的空间。同样，现在的情况也非如此，越来越多的程序员开始不喜欢他的职业了。</span></p>
<p class="MsoNormal"><span lang="EN-US">&nbsp;</span></p>
<h1><span lang="EN-US"><span>3.<span style="font-family: &quot;Times New Roman&quot;; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp; </span></span></span><span style="font-family: 宋体;">为什么软件开发越来越无趣？</span></h1>
<h2><span lang="EN-US"><span>3.1.<span style="font-family: &quot;Times New Roman&quot;; font-size-adjust: none; font-stretch: normal;"> </span></span></span><span style="font-family: 黑体;">首先，软件开发并不是数学</span></h2>
<p class="MsoNormal" style="text-indent: 21pt;"><span style="font-family: 宋体;">我们在学校的时候，那些老师们把软件开发归于数学的范畴，这没错，但过于狭隘。在</span><span lang="EN-US">30</span><span style="font-family: 宋体;">年前数学或许是软件的</span><span lang="EN-US">80%</span><span style="font-family: 宋体;">，但今天我们更倾向于把软件开发称为&ldquo;工程&rdquo;。工程与数学是不同的范畴，尽管在工程中我们会用到数学，但并不是全部，而且在软件工业的逐步发展的过程中，由于行业的分工进一步细化，数学的应用在软件工程中的比例越来越小。</span></p>
<p class="MsoNormal"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体;">算法是数学在编程中最基基本的一种表现方式，但在</span><span lang="EN-US">80%</span><span style="font-family: 宋体;">的软件开发项目数百万行代码中，能真正让你去思考&ldquo;算法&rdquo;的部分寥寥无几。来自于&ldquo;解题&rdquo;的快感，自然无从寻觅。没有挑战，哪来成就感？</span></p>
<p class="MsoNormal"><span lang="EN-US">&nbsp;</span></p>
<h2><span lang="EN-US"><span>3.2.<span style="font-family: &quot;Times New Roman&quot;; font-size-adjust: none; font-stretch: normal;"> </span></span></span><span style="font-family: 黑体;">第二，软件工程技术的发展，限制了施展的空间</span></h2>
<p class="MsoNormal"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体;">计算机给程序员提供了一个广阔的思维空间，但计算机工业则根据自己的发展需求将这个空间切分成非常细小的片断。位于产业链上游和技术前沿的厂商、团体和个人通过中间件产品（如数据库、应用服务器）、开发工具、设计理念、框架、宣传等方式则各自独占了软件技术链上最&ldquo;有趣&rdquo;的一部分。多数现代程序员，则随流进入其它一个一个狭小的片断中。那些所谓技术含量较低的管理软件（广义的业务软件）领域，更是集中了大多数的从业者。</span></p>
<p class="MsoNormal"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体;">平台、数据库、应用服务器、开发工具、现代软件设计理念、软件框架、技术宣传，这些产品和理念在推动软件工业成熟和发展的同时，一方面在宏观上提高了整个行业的生产率，降低了技术门槛，吸引了更多的从业者；另一方面则在微观上剥夺了多数程序员享受编程乐趣的环境。你应用</span><span lang="EN-US">EJB</span><span style="font-family: 宋体;">或者</span><span lang="EN-US">SSH(struts/spring/hibernate)</span><span style="font-family: 宋体;">开发项目的过程中，由衷地体会到编程的乐趣了吗？我反正没有。</span></p>
<p class="MsoNormal"><span lang="EN-US">&nbsp;</span></p>
<h2><span lang="EN-US"><span>3.3.<span style="font-family: &quot;Times New Roman&quot;; font-size-adjust: none; font-stretch: normal;"> </span></span></span><span style="font-family: 黑体;">第三，</span><span lang="EN-US">VB</span><span style="font-family: 黑体;">、</span><span lang="EN-US">PHP</span><span style="font-family: 黑体;">和</span><span lang="EN-US">Java</span></h2>
<p class="MsoNormal"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>C</span><span style="font-family: 宋体;">语言是有趣的，因为它是&ldquo;计算机科学&rdquo;发展的产物。</span></p>
<p class="MsoNormal"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Python</span><span style="font-family: 宋体;">和</span><span lang="EN-US">Ruby</span><span style="font-family: 宋体;">是有趣的，因为它是&ldquo;天才&rdquo;的产物。</span></p>
<p class="MsoNormal"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Delphi</span><span style="font-family: 宋体;">是有趣的，因为他是史上&ldquo;最优美的结构化编程教学语言</span><span lang="EN-US">Pascal</span><span style="font-family: 宋体;">&rdquo;的延伸。</span></p>
<p class="MsoNormal"><span style="font-family: 宋体;">与之不同的是多数的资深程序员认为</span><span lang="EN-US">VB/PHP</span><span style="font-family: 宋体;">和</span><span lang="EN-US">Java</span><span style="font-family: 宋体;">是无趣的&mdash;&mdash;</span></p>
<p class="MsoNormal" style="text-indent: 21pt;"><span lang="EN-US">PHP</span><span style="font-family: 宋体;">是快速</span><span lang="EN-US">WEB</span><span style="font-family: 宋体;">生产需求催化的产物。</span><span lang="EN-US">VB</span><span style="font-family: 宋体;">和</span><span lang="EN-US">Java</span><span style="font-family: 宋体;">则是软件工程发展的产物。</span></p>
<p class="MsoNormal"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体;">当&ldquo;编程&rdquo;遇上&ldquo;快速生产&rdquo;和&ldquo;工程&rdquo;的时候，乐趣就开始退化了。然而与乐趣无关的是，他们三个却成为了现代软件工业中最成功的三把斧头。一把能快速的砍出一个</span><span lang="EN-US">WEB</span><span style="font-family: 宋体;">论坛；一把能跨速的砍出</span><span lang="EN-US">Client</span><span style="font-family: 宋体;">界面；一把则通过理念、框架、规范、中间件等等等等，使得软件开发更加模式化和规范化，令软件行业向大规模工业化生产方式向前迈进了一大步。</span></p>
<p class="MsoNormal"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>VB</span><span style="font-family: 宋体;">、</span><span lang="EN-US">PHP</span><span style="font-family: 宋体;">和</span><span lang="EN-US">Java</span><span style="font-family: 宋体;">本身都是成功的，而陷于三者的程序员则多半难以成功。我们现在常常赞赏地说：</span><span lang="EN-US">XX</span><span style="font-family: 宋体;">技术</span><span lang="EN-US">XX</span><span style="font-family: 宋体;">框架让程序员更关注于业务逻辑。我们在享受他们所带来的便捷的同时，也正在慢慢丧失程序员的天性&mdash;&mdash;创造力。</span></p>
<p class="MsoNormal"><span lang="EN-US">&nbsp;</span></p>
<h1><span lang="EN-US"><span>4.<span style="font-family: &quot;Times New Roman&quot;; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp; </span></span></span><span style="font-family: 宋体;">寻找新乐趣之旅</span></h1>
<p class="MsoNormal"><span style="font-family: 宋体;">我们不能选择放弃，那么就让我们开始去寻找新的乐趣吧！</span></p>
<p class="MsoNormal"><span lang="EN-US">&nbsp;</span></p>
<h2><span lang="EN-US"><span>4.1.</span></span><span style="font-family: 黑体;">创新：用户</span><span lang="EN-US">UI</span><span style="font-family: 黑体;">体验的乐趣</span></h2>
<p class="MsoNormal" style="text-indent: 21pt;"><span style="font-family: 宋体;">与</span><span lang="EN-US">20</span><span style="font-family: 宋体;">年前不同，当年的软件更接近&ldquo;底层&rdquo;，而今天我们所开发的软件则更多地接近用户的感官和操作。把成就感从底层的挖掘移向</span><span lang="EN-US">UI</span><span style="font-family: 宋体;">层的体验，显得顺理成章。</span></p>
<p class="MsoNormal" style="text-indent: 21pt;"><span style="font-family: 宋体;">同时，当今的</span><span lang="EN-US">UI</span><span style="font-family: 宋体;">技术和硬件渲染能力非</span><span lang="EN-US">20</span><span style="font-family: 宋体;">年前可比。以我们目前接触最多的</span><span lang="EN-US">WEB</span><span style="font-family: 宋体;">应用为例，最为普通的</span><span lang="EN-US">HTML/CSS/Ajax/JS/Flex</span><span style="font-family: 宋体;">等技术为我们提供了全所未有界面表现能力。我一直坚信优秀的用户体验是成功的一半。最近几年的</span><span lang="EN-US">Web</span><span style="font-family: 宋体;">创新很多都集中在表现方式上，如</span><span lang="EN-US">Ajax</span><span style="font-family: 宋体;">和</span><span lang="EN-US">Flex</span><span style="font-family: 宋体;">。</span></p>
<p class="MsoNormal" style="text-indent: 21pt;"><span style="font-family: 宋体;">一些小型的用户体验提升方式已经普及到了&ldquo;标配&rdquo;的程度。比如，</span><span lang="EN-US">5</span><span style="font-family: 宋体;">年前如果你在一个</span><span lang="EN-US">Web</span><span style="font-family: 宋体;">表单中输入了错误的数据，必须在提交后的下一个页面中被提示出错；而今天不能在</span><span lang="EN-US">Input</span><span style="font-family: 宋体;">框的右边提供实时交验信息的界面则是令人恼火的经历。</span></p>
<p class="MsoNormal" style="text-indent: 21pt;"><span style="font-family: 宋体;">在</span><span lang="EN-US">UI</span><span style="font-family: 宋体;">上的创新远不止这些。在</span><span lang="EN-US">Ajax</span><span style="font-family: 宋体;">和</span><span lang="EN-US">Flash</span><span style="font-family: 宋体;">令界面表现的丰富程度达到</span><span lang="EN-US">VB/Delphi</span><span style="font-family: 宋体;">望尘莫及的今天，我们追捧着</span><span lang="EN-US">gmail</span><span style="font-family: 宋体;">，研究着</span><span lang="EN-US">google map</span><span style="font-family: 宋体;">，效仿着</span><span lang="EN-US">flickr</span><span style="font-family: 宋体;">，甚至崇拜着</span><span lang="EN-US">fins</span><span style="font-family: 宋体;">的</span><span lang="EN-US">GT Grid</span><span style="font-family: 宋体;">。一旦有人能够向</span><span lang="EN-US">UI</span><span style="font-family: 宋体;">体验发出挑战性的创新，就会给开发者赢来众多赞赏的目光和追随者的效仿，伴随而来的是开发人员极大的快乐。</span></p>
<p class="MsoNormal"><span lang="EN-US">&nbsp;</span></p>
<h2><span lang="EN-US"><span>4.2.<span style="font-family: &quot;Times New Roman&quot;; font-size-adjust: none; font-stretch: normal;"> </span></span></span><span style="font-family: 黑体;">探险：扒开&ldquo;框架&rdquo;的乐趣</span></h2>
<p class="MsoNormal" style="text-indent: 21pt;"><span style="font-family: 宋体;">使用</span><span lang="EN-US">Hibernete</span><span style="font-family: 宋体;">谈不上乐趣，至少是乐趣有限。但如果你扒开</span><span lang="EN-US">Hibernate</span><span style="font-family: 宋体;">的代码，跟着作者的思路在数十万行代码迷宫中探险的时候，当你拨开一层层迷雾，为一段思路一行程序一种理念一个技巧而拍案叫绝的时候，你可能会得到前所未有的乐趣：</span></p>
<p class="MsoNormal" style="text-indent: 21pt;"><span style="font-family: 宋体;">这种乐趣可能，</span></p>
<p class="MsoNormal" style="margin-left: 21pt;"><span style="font-family: 宋体;">来自于&ldquo;发现&rdquo;的惊喜，</span></p>
<p class="MsoNormal" style="margin-left: 21pt;"><span style="font-family: 宋体;">来自于&ldquo;理解&rdquo;的激动，</span></p>
<p class="MsoNormal" style="margin-left: 21pt;"><span style="font-family: 宋体;">来自于&ldquo;学习&rdquo;的充实，</span></p>
<p class="MsoNormal" style="margin-left: 21pt;"><span style="font-family: 宋体;">来自于&ldquo;顿悟&rdquo;的爽快！</span></p>
<p class="MsoNormal" style="margin-left: 21pt;"><span style="font-family: 宋体;">来自于&ldquo;英雄所见略同&rdquo;的自豪感！</span></p>
<p class="MsoNormal"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体;">在咱们软件圈儿，大师用书说话，大侠则用代码说话。&ldquo;书上得来终觉浅，绝知此事要躬行&rdquo;。转进大侠的代码里去吧，那里有无穷的乐趣等着我们。</span></p>
<p class="MsoNormal"><span lang="EN-US">&nbsp;</span></p>
<h2><span lang="EN-US"><span>4.3.<span style="font-family: &quot;Times New Roman&quot;; font-size-adjust: none; font-stretch: normal;"> </span></span></span><span style="font-family: 黑体;">拓展：扩展眼界的乐趣</span></h2>
<p class="MsoNormal" style="text-indent: 21pt;"><span style="font-family: 宋体;">我一直鼓励身边共事的开发人员多学习一些编程语言，不一定在工作中用，但起码能够见识一下另一种思维方式。这不仅能扩宽眼界，我们更能从中体会到这个职业的乐趣。</span></p>
<p class="MsoNormal" style="text-indent: 21pt;"><span lang="EN-US">&nbsp;</span></p>
<p class="MsoNormal" style="text-indent: 21pt;"><span style="font-family: 宋体;">出于管理上的效率和能力，</span><span lang="EN-US">5</span><span style="font-family: 宋体;">年来我们的团队一直以</span><span lang="EN-US">Java</span><span style="font-family: 宋体;">为主，但从编程艺术的角度，我不喜欢</span><span lang="EN-US">Java</span><span style="font-family: 宋体;">。尽管我早就开始认识到软件跟艺术风马牛不相及，但有时还会以这种欺骗自己的方式自我陶醉一把。</span></p>
<p class="MsoNormal"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体;">我不喜欢</span><span lang="EN-US">Java</span><span style="font-family: 宋体;">的原因是，这种一无是处而又无处不在的编程语言养成了我的惰性，让我在工作中找不到去触碰和学习</span><span lang="EN-US">Python</span><span style="font-family: 宋体;">和</span><span lang="EN-US">Ruby</span><span style="font-family: 宋体;">的&ldquo;官方&rdquo;理由。</span></p>
<p class="MsoNormal"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体;">有幸的是在过去的</span><span lang="EN-US">1</span><span style="font-family: 宋体;">年里我经历的三件事情重新点燃了我学习新的编成语言的激情：</span></p>
<p class="MsoNormal"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>* 12</span><span style="font-family: 宋体;">个月前，我因项目需要花费了整整</span><span lang="EN-US">1</span><span style="font-family: 宋体;">个月的时间钻研</span><span lang="EN-US">Javascript</span><span style="font-family: 宋体;">。</span></p>
<p class="MsoNormal"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>* 5</span><span style="font-family: 宋体;">个月前，我因项目需要重新拾回了</span><span lang="EN-US">C</span><span style="font-family: 宋体;">语言（之前我已经</span><span lang="EN-US">4</span><span style="font-family: 宋体;">年没碰过</span><span lang="EN-US">make</span><span style="font-family: 宋体;">了）。</span></p>
<p class="MsoNormal"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>* </span><span style="font-family: 宋体;">一星期前的一天，我无聊到把</span><span lang="EN-US">JE</span><span style="font-family: 宋体;">的</span><span lang="EN-US">Ruby</span><span style="font-family: 宋体;">论坛里的精华良好帖全部看了一遍。</span></p>
<p class="MsoNormal"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p class="MsoNormal"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体;">试试吧，多学一种，我们一起学。</span></p>
<p class="MsoNormal"><span lang="EN-US">&nbsp;</span></p>
<h2><span lang="EN-US"><span>4.4.</span></span><span style="font-family: 黑体;">协作：大制作的乐趣</span></h2>
<p class="MsoNormal" style="text-indent: 21pt;"><span style="font-family: 宋体;">大师令我们敬仰，大侠令我们敬畏。那些底层的、抽象地、框架性的、被称为无法重造得更好的轮子的作品，似乎只与他们有缘，给我</span><span lang="EN-US">100</span><span style="font-family: 宋体;">个脑袋，我也没有信心去挑战他们的领域。那么，好吧，没骨气就没骨气了，我们还有我们取得成就感的办法&mdash;&mdash;协作。</span></p>
<p class="MsoNormal" style="text-indent: 21pt;"><span style="font-family: 宋体;">钢琴王子的独奏固然经典，气势磅礴的交响乐同样能博得喝彩。跟交响乐一样，软件工程演奏的关键同样是配合。</span></p>
<p class="MsoNormal" style="text-indent: 21pt;"><span style="font-family: 宋体;">大制作的软件产品是任何独行侠无法完成的，一个人的精力有限兴趣狭隘，不可能达到面面俱到，也懒于照顾上至</span><span lang="EN-US">UI</span><span style="font-family: 宋体;">体验下至数据库优化的每一个细节。这正是我等发挥的乐园。</span></p>
<p class="MsoNormal" style="text-indent: 21pt;"><span style="font-family: 宋体;">然而我不得不承认，在从树上的猴子进化到键盘前的你我他的过程中，&ldquo;协作&rdquo;是我们退化得最迅速的优良品质。</span></p>
<p class="MsoNormal" style="text-indent: 21pt;"><span style="font-family: 宋体;">如何在协作中取得成就感，获得乐趣，正是我们现在不断尝试和孜孜追求的东西，它需要我们共同的努力。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
          <br/><br/>
          <span style="color:red;">
            <a href="http://www.javaeye.com/topic/243210" style="color:red;">已有 <strong>48</strong> 人发表回复，猛击-&gt;&gt;<strong>这里</strong>&lt;&lt;-参与讨论</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;">Windows7在微软WinHEC 2008上揭开神秘面纱</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 17 Sep 2008 17:55:33 +0800</pubDate>
        <link>http://www.javaeye.com/topic/243210</link>
        <guid>http://www.javaeye.com/topic/243210</guid>
      </item>
          <item>
        <title>Pomer：基于Flex和Java EE的信息管理系统基础框架</title>
        <author>JavaEye网站</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://ke2000.javaeye.com">ke2000</a>&nbsp;
                    链接：<a href="http://www.javaeye.com/topic/249587" style="color:red;">http://www.javaeye.com/topic/249587</a>&nbsp;
          发表时间: 2008年10月06日
          <br/>
          声明：本文系JavaEye网站发布的原创文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          Pomer是几个无聊的人利用业余时间做的希望不是无聊的一个基础框架：），目前的功能还不完整，不过我们这几个无聊的人会继续做下去，现在推出的版本功能：一个可以运行的基础框架和代码生成器。<br />&nbsp;&nbsp;&nbsp; 欢迎大家试用，欢迎拍砖 ^_^,以下是简单的介绍和说明，详细请访问:<a href="http://www.pomer.org.cn" target="_blank">http://www.pomer.org.cn</a><br />&nbsp;&nbsp;&nbsp; * Flex 开发框架 <br />Pomer主要采用spring和hibernate做为后台框架，通过blazeds(LCDS)RemoteObject?与java后台通信，前台采用Cairngorm MVC框架， Pomer框架主要作用就是简化blazeds(LCDS)RemoteObject的访问方式，提高开发效率。Pomer通过注解 (Annotation)将普通java对象，Spring Bean和EJB发布为远程对象（RemoteObject）提供给flex访问，而不需要进行任何配置 。<br /><br />&nbsp;&nbsp;&nbsp; * 代码生成器 <br />Pomer 代码生成器包括两个部分，一个是强大灵活通用代码生成器模型和一套基于Pomer的默认模板。 Pomer通用代码生成器采用freemark做为模板引擎，可以生成任何基于数据表或javaPojo和java接口的代码，用户只需提供 freemark的模板即可。 Pomer提供的默认生成功能和默认模板可以通过数据库生成基于Pomer框架可运行的增删改查代码，也可以从javaBean生成 RemoteObject的数据传输对象、表单和列表，还能通过java接口生成Cairngorm的框架代码。<br />&nbsp;&nbsp; <img src="http://lh4.ggpht.com/yulinlincom/SOGFRdmtH5I/AAAAAAAAAcA/OZkaarZMgFU/s576/pomerUserIndex.JPG" />
          <br/><br/>
          <span style="color:red;">
            <a href="http://www.javaeye.com/topic/249587" style="color:red;">已有 <strong>35</strong> 人发表回复，猛击-&gt;&gt;<strong>这里</strong>&lt;&lt;-参与讨论</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;">Windows7在微软WinHEC 2008上揭开神秘面纱</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 06 Oct 2008 22:32:11 +0800</pubDate>
        <link>http://www.javaeye.com/topic/249587</link>
        <guid>http://www.javaeye.com/topic/249587</guid>
      </item>
          <item>
        <title>TL日记</title>
        <author>JavaEye网站</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://cceyjames.javaeye.com">cceyjames</a>&nbsp;
                    链接：<a href="http://www.javaeye.com/topic/248050" style="color:red;">http://www.javaeye.com/topic/248050</a>&nbsp;
          发表时间: 2008年09月29日
          <br/>
          声明：本文系JavaEye网站发布的原创文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          十一长假干啥？原本计划购物+电影+游戏+运动，今早起来忽然回忆起半个月之前工作中的问题，很多问题想不明白，Google了一下到了JE，拜读了不少高人的大作，感觉就像蒸了一个桑拿，痛快淋漓。从中发现自己很多不足，感觉和高人差距太大，于是心中愤慨，工作8年了还有这么多不懂的地方，一定要振奋起来，看看从我现在的状况达到高人的境界需要几年，计划将今后的工作日志一一在这里贴出来，根据每天所发生的问题，希望能与高人实时互动，得到高人的指点。<br /><br />先说一下个人情况。81年，中专，2000年开始工作，最初做美工，过了一年转php，过了一年转java，至今。期间有过2年创业，1年部门经理。当前在一个150人的公司，软件部门50人左右，我在一个Java组（3人）。<br /><br />部门将计划在半年左右扩招Java组到10-20人左右，这给我提出了严峻的挑战。而无论半年后是否会扩招到20人，从个人成长来看也非常有必要提高各项水平。<br /><br />我个人当前存在的问题：<br />1、软件工程只是略懂理论，没有真正的实践经验；<br />2、Java技术只是停留在应用开发阶段，对OOP思想没有真正贯彻，在进行大的系统设计时感到很吃力；<br /><br />小组内另外两个人的情况：<br />1、A君：本科，7年经验，Java2年；<br />2、B君：硕士，半年经验；<br /><br />小组当前工作情况：<br />1、我：负责公司主要软件的版本；<br />2、A君：另外一个软件的版本；<br />3、B君：配合我和A君的工作；<br /><br />小组内用到的工具：<br />IDE:MyEclipse<br />版本控制：VSS(因为整个软件部门很多C++代码，领导用惯了vss)<br />Bug跟踪：JTrac<br /><br />部门内常用的文档：<br />规格，概设，详设，修改。<br /><br />回顾前两周工作中存在的问题：<br />1、一个我自认为我可以5天搞定的项目（项目A），直接给了A、君（我去修改另外一个软件的UI和实现新功能），结果做了10天（包括每天加班到11点）都没有作出合格的版本；领导很生气，后果很严重。部门领导认为我在这个项目中计划、人员分配上有严重的失误。<br />2、A君和B君在开发过程中没有文档、设计；<br /><br />针对以上两个问题，部门计划：<br />1、项目A重写；<br />2、Java组建立一套严格的规范（我已经草创了一套《JavaWeb开发规范》，但感觉执行起来很困难）<br /><br />国庆7天之后，我将把每天工作中遇到的问题和心得贴出来，希望高人能够解惑。
          <br/><br/>
          <span style="color:red;">
            <a href="http://www.javaeye.com/topic/248050" style="color:red;">已有 <strong>12</strong> 人发表回复，猛击-&gt;&gt;<strong>这里</strong>&lt;&lt;-参与讨论</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;">Windows7在微软WinHEC 2008上揭开神秘面纱</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, 29 Sep 2008 11:32:36 +0800</pubDate>
        <link>http://www.javaeye.com/topic/248050</link>
        <guid>http://www.javaeye.com/topic/248050</guid>
      </item>
          <item>
        <title>LightURL——打造零配置的Struts2开发方式</title>
        <author>JavaEye网站</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://downpour.javaeye.com">downpour</a>&nbsp;
                    链接：<a href="http://www.javaeye.com/topic/242838" style="color:red;">http://www.javaeye.com/topic/242838</a>&nbsp;
          发表时间: 2008年09月16日
          <br/>
          声明：本文系JavaEye网站发布的原创文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <strong>背景</strong><br /><br />Struts2已经日益成为Web层比较主流的开发框架，它来源于Webwork2，是一个非常优秀的MVC框架。在Webwork2设计之处，Annotation和Ruby on Rails还没有像现在那么火，所以整个框架在配置方面还是沿用了Web框架惯用的XML作为主要的配置方式。<br /><br />随着时代的发展，对于Web程序员来说，如何简化配置成了一个很重要的课题。在这方面，Struts2也有一些探索。在Struts2的官方网站上，我们可以找到一些优秀的plugin来做这些工作：<br /><br /><a href="http://cwiki.apache.org/S2PLUGINS/codebehind-plugin.html" target="_blank">http://cwiki.apache.org/S2PLUGINS/codebehind-plugin.html</a><br /><br /><a href="http://cwiki.apache.org/S2PLUGINS/smarturls-plugin.html" target="_blank">http://cwiki.apache.org/S2PLUGINS/smarturls-plugin.html</a><br /><br />LightURL的目的是为了吸取这些优秀的plugin的优点，并支持更方便的配置方式。<br /><br /><strong>安装</strong><br /><br />1. 将struts2-lighturl-plugin.jar加入到classpath下<br /><br />2. 配置你的web.xml<br /><br /><pre name="code" class="java">        
        &lt;!-- Struts Filter -->
    &lt;filter>
        &lt;filter-name>struts&lt;/filter-name>
        &lt;filter-class>com.demo2do.lighturl.LightURLFilter&lt;/filter-class>
    &lt;/filter>

    &lt;!-- Struts URL Definition -->
    &lt;filter-mapping>
        &lt;filter-name>struts&lt;/filter-name>
        &lt;url-pattern>/*&lt;/url-pattern>
    &lt;/filter-mapping>    
</pre><br /><br />3. 需要指定你的action package所在位置和一些基本配置<br /><br />这一步一般在struts.properties中完成。你可以建一个struts.properties的配置文件，并放到classpath下，并指定如下配置<br /><br /><pre name="code" class="java">
## action package config
lighturl.action.packages=com.demo2do.lighturl.action

## action mapping implementation class
struts.mapper.class=com.demo2do.lighturl.LightURLActionMapper

## default parent package
lighturl.action.default.parent.package=struts-config

## define your entity package (optional)
# lighturl.entity.package=com.demo2do.lighturl.entity
</pre><br /><br />在完成以上的步骤后，lighturl的所有配置即告完成，现在你已经可以使用所有lighturl所提供的特性。<br /><br /><strong>Namespace, ActionName和URL映射</strong><br /><br />lighturl所提供的最基本的特性是根据你的Action所在的package，确定namespace和actionName，并进行对应的URL映射。<br /><br />举例说明，你在上面所讲述的struts.properties中，已经指定了你的action package<br /><br /><pre name="code" class="java">
lighturl.action.packages=com.demo2do.lighturl.action
</pre><br /><br />那么，如果你有以下的Action类，那么lighturl将根据如下的规则来确定每个Action类所对应的namespace，actionName和url映射关系<br /><br />1. 根据lighturl.action.packages的配置的package到你的Action类的相对package来确定namespace，如果其中有驼峰法命名，那么转化成"-"连接的单词<br /><ul><li>com.demo2do.lighturl.action.Index&nbsp; ----> /</li><li>com.demo2do.lighturl.action.user.Search ----> /user</li><li>com.demo2do.lighturl.action.blog.category.Index ----> /blog/category</li><li>com.demo2do.lighturl.action.accoutDetail.View ----> /account-detail</li></ul><br />2. 将Action类的类名转化成actionName。如果碰到的Action类以"Action"结尾，则去掉末尾的"Action"，如果其中有驼峰法命名,那么转化成"-"连接的单词<br /><ul><li>com.demo2do.lighturl.action.Index ----> index</li><li>com.demo2do.lighturl.action.user.SearchAction ----> search</li><li>com.demo2do.lighturl.action.blog.CategoryBlog ----> category-blog</li></ul><br />3. 将namespace与actionName拼起来，就成构成映射到具体Action类的url<br /><ul><li>http://host:port/app/user/index ---> com.demo2do.lighturl.action.user.Index </li><li>http://host:port/app/blog/category-blog ---> com.demo2do.lighturl.action.blog.CategoryBlog</li></ul><br />上面的这种URL匹配方式，我称之为：<strong>package匹配</strong><br /><br /><strong>特殊形式的URL</strong><br /><br />应该说根据package来进行Action映射，可以解决绝大多数从url到action的映射配置问题。不过有的时候，我们可能需要支持一些特殊形式的url。LightURL在默认情况下，支持下列2种特殊形式的URL<br /><br />1. 支持将名为Index的Action直接映射到package上<br /><br />这种匹配我称之为：<strong>Namespace匹配</strong>。这一个特性很直观。如果你在某个Action的package下面有一个名为Index的Action类，那么如果你直接访问这个package，那么你可以访问到这个类：<br /><ul><li>com.demo2do.lighturl.action.Index ---> http://host:port/app/</li><li>com.demo2do.lighturl.action.user.Index ---> http://host:port/app/user</li><li>com.demo2do.lighturl.action.blog.category.Index ---> http://host:port/app/blog/category</li></ul><br />2. 支持类似: /entity/${id}形式的URL<br /><br />这种匹配我通常称之为：<strong>entity匹配</strong>。这个特性也比较简单，如果你有某个entity，并且在你的Action package下有一个与entity同名的package。同时在这个package下有一个叫View的Action，那么上述形式的URL会被映射到该Action。<br /><ul><li>com.demo2do.lighturl.action.user.View ---> http://host:port/app/user/3456</li><li>com.demo2do.lighturl.action.blog.View ---> http://host:port/app/user/1113</li></ul><br />你可以通过在struts.properties中指定你entity所在的package来对哪些url可以具备这些特性，如果你输入的url不在你所指定的package中含有entity，那么这个url将无法被识别。<br />针对有些情况，数据库的主键可能不是数字。此时，你可以通过自己实现com.demo2do.lighturl.config.EntityPrimaryKeyIdentifier的接口来指定你的url中id具备什么特点。默认的实现是将主键识别为数字。<br /><br /><strong>使用Annotation来指定映射</strong><br /><br />除了上述这些基本特性以外，还可以通过Annotaion来指定URL映射。目前情况下，LightURL所支持的Annotation有两种类型:<br /><br />1. URL完整匹配<br /><br />URL完整匹配是指如果某个url完整匹配于Annotation中所指定的内容，那么这个URL将被映射到Annotation所在的Action类的method<br /><br /><pre name="code" class="java">
package com.demo2do.lighturl.action.user;

import com.demo2do.lighturl.annotation.Action;
import com.opensymphony.xwork2.ActionSupport;

/**
 * @author Downpour
 *
 */
public class Search extends ActionSupport {
	
	private static final long serialVersionUID = -1728616675239859226L;

	/* (non-Javadoc)
	 * @see com.opensymphony.xwork2.ActionSupport#execute()
	 */
	@Override
	@Action("/all/search-user")
	public String execute() throws Exception {
		return super.execute();
	}

}
</pre><br /><br />例如，上述的Action类有一个Annotation，那么这个Action和method将被映射到对应的URL:http://host:port/app/all/search-user。<br /><br />注意，此时，虽然从url上来看，这是一个没有什么规则的url，但是其所对应的namespace和actionName还是根据com.demo2do.lighturl.action.user.Search来进行计算的。<br /><br />2. URL Template<br /><br />URL Template是指，url可以匹配Annotaion中指定的某种URL Template，并将其中的可变部分作为参数映射到Action中。<br /><br /><pre name="code" class="java">
package com.demo2do.lighturl.action.blog;

import com.demo2do.lighturl.annotation.Action;
import com.opensymphony.xwork2.ActionSupport;

/**
 * @author Downpour
 *
 */
public class Category extends ActionSupport {

	private static final long serialVersionUID = -1535992103374733252L;

        private Long id;
	
	private int year;
	
	private int month;
	
	private int day;

	/* (non-Javadoc)
	 * @see com.opensymphony.xwork2.ActionSupport#execute()
	 */
	@Override
	@Action("/blogs/${year}/${month}/${day}")
	public String execute() throws Exception {
		return super.execute();
	}
        
        @Action("/blog/${id}/edit")
	public String edit() throws Exception {
		return super.execute();
	}
        // setters and getters
	
}
</pre><br /><br />在上述的例子中，可以发现，在Annotation中所指定的内容是一个URL Template，如果你有一个url，可以匹配上面的URL Template，那么url将匹配到这个Action的method，并且将对应位置的值注入到Action中的同名参数中。<br /><br />针对上面的例子：<br />http://host:port/app/blogs/2008/08/07将被映射到这个Action的execute，并且2008，08和07分别映射到year，month和day中。<br />http://host:port/app/blog/2345/edit将被映射到Action的edit方法，并且2345映射到id中作为参数<br /><br /><strong>URL的匹配顺序与重复配置的校验</strong><br /><br />在上面的例子中，介绍了那么多的url映射到Action中的方式。他们之间可能会出现冲突，有些冲突，LightURL会在系统启动时为你检查出来，并强制要求你纠正它，而有些冲突，则通过优先级匹配的方式进行。<br /><br />下列冲突将被认为是你必须在系统启动前就进行纠正的：<br />1. Annotation中定义的URL Template互相之间冲突<br /><br />例如：/blogs/${year}/${month}/${day}和/blogs/${category}/${id}/edit就是冲突的。<br /><br />2. Annotation中定义的URL Template与其他Annotation中定义的完全匹配URL冲突<br /><br />例如：/blogs/${year}/${month}和/blogs/category/index就是冲突的。<br /><br />3. Annotation中定义的URL Template与形如/entity/${id}的url定义冲突<br /><br />例如：/user/${id}形式的Annotation定义可能会与系统默认支持的冲突。<br /><br />在其他情况下，如果你定义的URL映射互相直接有冲突，那么LightURL将根据某个顺序进行URL匹配，并找到第一个匹配的映射方式，然后放弃查找。这个顺序为：<br />1. 首先进行<strong>Namespace匹配</strong>，如果url恰好能匹配某个namespace，并且其对应的package下有Index作为Action，那么直接进行匹配。<br />2. 其次查看所有的Annotation定义中，是否存在<strong>完整的URL匹配</strong>，如果找到，那么进行直接匹配。<br />3. 接着进行<strong>package匹配</strong>，将url分解成相应的namespace和actionName，与已有的配置进行匹配，如果找到，那么直接匹配。<br />4. 然后进行<strong>entity匹配</strong>，看看url是否形如:/entity/${id}，如果是，那么直接匹配。<br />5. 最后进行Annotation定义的<strong>Url Template匹配</strong>。<br /><br />如果所有的五种情况都无法进行匹配，那么这个URL将无法被LightURL识别，继续交由Struts2进行后续处理。<br /><br /><br />上面所描述的内容都是Url到Action的映射。下面的部分，描述的是如何在Action执行完毕之后，转到相应的结果view。<br /><br /><strong>Codebehind</strong><br /><br />LightURL支持codebehind。有关codebehind的相关知识，可以参考struts2的相关文档：<br /><a href="http://struts.apache.org/2.x/docs/codebehind-plugin.html" target="_blank">http://struts.apache.org/2.x/docs/codebehind-plugin.html</a><br /><br />有了codebehind的支持，那么从Action转到类似jsp，ftl或者vm的view层组件就不需要任何配置，只要符合一定的命名规范，就可以直接进行转向。<br /><br /><strong>ResultCode的识别</strong><br /><br />但是在很多情况下，我们需要的是全方位的Result类型的支持。例如，有的时候我们需要返回JSON Result，有的时候，我们可能需要Redirect到一个新的Action。此时，我们不得不为此增加一些配置，或者借助Annotation来完成。<br /><br />为此，LightURL提供了一种根据ResultCode进行识别的命名方式来匹配你所指定的Result。<br /><br />以JSON Result为例，你可以这么写：<br /><br /><pre name="code" class="java">
package com.demo2do.lighturl.action.user;

import com.demo2do.lighturl.entity.User;
import com.opensymphony.xwork2.ActionSupport;

/**
 * @author Downpour
 *
 */
public class Index extends ActionSupport {

	private static final long serialVersionUID = -5017825114664788765L;

	private User user;
	
	/* (non-Javadoc)
	 * @see com.opensymphony.xwork2.ActionSupport#execute()
	 */
	@Override
	public String execute() throws Exception {
		return "j:user";
	}

        // setters and getters
}
</pre><br /><br />在这里，你可以发现，resultCode变成了j:user。那么此时，LightURL会将这个resultCode识别成：请返回JSON Result，并且JSON的Result的root为user对象。<br /><br />再例如：<br /><br /><pre name="code" class="java">
package com.demo2do.lighturl.action.user;

import com.demo2do.lighturl.entity.User;
import com.opensymphony.xwork2.ActionSupport;

/**
 * @author Downpour
 *
 */
public class Index extends ActionSupport {

	private static final long serialVersionUID = -5017825114664788765L;

	private User user;
	
	/* (non-Javadoc)
	 * @see com.opensymphony.xwork2.ActionSupport#execute()
	 */
	@Override
	public String execute() throws Exception {
		return "r:/user/add-user";
	}

        // setters and getters
}
</pre><br /><br />在这里，"r:/user/add-user"会被识别成：请返回Redirect Result。Redirect的URL为/user/add-user。<br /><br />LightURL默认情况下，实现了对Redirect Result和JSON Result的ResultCode识别，并将他们的前缀分别定制为："r:"("redirect:")或者"j:"("json:")。<br /><br />当然，你可以根据你自己的情况，实现你自己的ResultCode的识别程序，你只需要实现com.demo2do.lighturl.result.ResultCodeConfig接口即可。并在struts.properties中指定你希望LightURL进行ResultCode匹配的顺序。<br /><br /><pre name="code" class="java">
lighturl.result.code.config=yourpackage.ResultCodeConfigImpl1,yourpackage.ResultCodeConfigImpl2
</pre><br /><br />这样，LightURL会根据你所指定的顺序，依次进行ResultCode的匹配，直到找到第一个匹配的Result Type为之。<br /><br />当然，默认情况下，LightURL的匹配顺序为：<br /><br />codebehind -> 你自定义的ResultCode识别实现 -> json -> redirect
          <br/><br/>
          <span style="color:red;">
            <a href="http://www.javaeye.com/topic/242838" style="color:red;">已有 <strong>26</strong> 人发表回复，猛击-&gt;&gt;<strong>这里</strong>&lt;&lt;-参与讨论</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;">Windows7在微软WinHEC 2008上揭开神秘面纱</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, 16 Sep 2008 17:39:08 +0800</pubDate>
        <link>http://www.javaeye.com/topic/242838</link>
        <guid>http://www.javaeye.com/topic/242838</guid>
      </item>
          <item>
        <title>Ruby on Rails 在国内的使用情况</title>
        <author>JavaEye网站</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lfhaha.javaeye.com">lfhaha</a>&nbsp;
                    链接：<a href="http://www.javaeye.com/topic/245185" style="color:red;">http://www.javaeye.com/topic/245185</a>&nbsp;
          发表时间: 2008年09月21日
          <br/>
          声明：本文系JavaEye网站发布的原创文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          自己一直在做Java相关开发，请问各位Ruby on Rails在国内的使用情况，比如哪些企业、公司在使用，总体开发人员等
          <br/><br/>
          <span style="color:red;">
            <a href="http://www.javaeye.com/topic/245185" style="color:red;">已有 <strong>85</strong> 人发表回复，猛击-&gt;&gt;<strong>这里</strong>&lt;&lt;-参与讨论</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;">Windows7在微软WinHEC 2008上揭开神秘面纱</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sun, 21 Sep 2008 20:54:25 +0800</pubDate>
        <link>http://www.javaeye.com/topic/245185</link>
        <guid>http://www.javaeye.com/topic/245185</guid>
      </item>
          <item>
        <title>理解渐进增强（Progressive Enhancement）</title>
        <author>JavaEye网站</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lifesinger.javaeye.com">lifesinger</a>&nbsp;
                    链接：<a href="http://www.javaeye.com/topic/250687" style="color:red;">http://www.javaeye.com/topic/250687</a>&nbsp;
          发表时间: 2008年10月09日
          <br/>
          声明：本文系JavaEye网站发布的原创文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <div class="post-content">
<p>原文：<a href="http://www.alistapart.com/articles/understandingprogressiveenhancement">Understanding Progressive Enhancement</a><br />
作者：<a href="http://www.alistapart.com/authors/g/aarongustafson">Aaron Gustafson</a><br />
翻译：<a href="http://lifesinger.org/">http://lifesinger.org/</a></p>
<p><img class="alignright size-full wp-image-302" title="progressive-enhancement" src="http://lifesinger.org/blog/wp-content/uploads/2008/10/progressive-enhancement.jpg" height="452" alt="" style="float: right;" width="270" /><br />
从1994年开始，Web开发社区就敲响了<a href="http://en.wikipedia.org/wiki/Graceful_degradation">优雅降级（Graceful Degradation）</a>的
鼓声。这个概念来自工程世界，其核心理念是给最新最强大的浏览器全套餐式的体验，而给那些不幸还在使用Netscape
4的可怜人们只留些残羹冷炙般的基本功能。毫无疑问，这发挥了作用。但是它并不很符合Tim
Berners-Lee（译注：万维网之父）关于普遍可访问性网络（Universally Accessible Web）的原始愿景。</p>
<p>大约10年后，一些聪明的家伙开始质疑优雅降级，发现它在很多层面上有不足之处。他们将精力集中在内容可用性（Content
Availability）、总体可访问性（Overall
Accessibility）和移动设备浏览器的能力上，寻找到了一条Web开发的新途径&mdash;&mdash;此方法将内容作为关注焦点，而不只是对旧设备的支持嘴上说说
却没有实际行动。</p>
<p>在2003年的<a href="http://sxsw.com/">SXSW</a>会议（译注：一个关于电影、音乐和交互的会议）中，Steve Champeon和Nick Finck做了一个名为 &ldquo;<a href="http://www.hesketh.com/publications/inclusive_web_design_for_the_future/">面向未来的全方位Web开发</a>&rdquo; 的演讲。这样，他们揭示了这种Web开发新方法的蓝图。Steve还给它取了个名称：渐进增强（<a href="http://en.wikipedia.org/wiki/Progressive_enhancement">Progressive Enhancement</a>）。</p>
<p>&nbsp;</p>
<h3>这里有个（微妙的）差别</h3>
<p>如果你挠着头想弄清楚优雅降级和渐进增强的区别，我告诉你，这是一个关于视角的问题。优雅降级和渐进增强都考虑一个网页在各种设备的各种浏览器上如何良好运转。两者区别的关键在于它们各自关注的焦点，以及这种关注对工作流程的影响。</p>
<p>&nbsp;</p>
<h4>优雅降级的视角</h4>
<p>优雅降级关注于在最先进/最全能的浏览器上构建网站。在被认为&ldquo;老的&rdquo;或能力不足的浏览器中的测试，经常要等到开发周期的最后一个环节才进行，并且通常限制在主流浏览器（如IE、Mozzila等）的前一个发布版本中。</p>
<p>在这种模式下，老的浏览器只可能提供差强人意（poor, but passable）的体验。或许会做些小补丁来适应某个特定浏览器，但这些浏览器毕竟不是关注的焦点，除了修正重大的错误，也不会再费多大的神了。</p>
<p>&nbsp;</p>
<h4>渐进增强的视角</h4>
<p>渐进增强关注于内容。请注意区别：我甚至都没提及浏览器。</p>
<p>内容是我们最初创建网站的原因。有些网站传播内容，有些收集内容，有些请求内容，有些操作内容，有些网站以上所有功能都有，然而它们都需要内容。这就是渐进增加成为一种更适合的模式的关键所在。这也是Yahoo!迅速采纳这种模式并用它创建了<a href="http://developer.yahoo.com/yui/articles/gbs/">分级浏览器支持（Graded Browser Support）</a>策略的原因。</p>
<p>&nbsp;</p>
<h3>它是怎样运作的</h3>
<p>进入渐进增强的思维方法很简单：只要从内容开始往外想。内容形成坚实的基石，在此之上你才能添加样式和交互。如果你爱吃糖果，可以将它想像成一颗M&amp;M花生巧克力：<br />
<img class="alignnone size-full wp-image-300" title="m-m" src="http://lifesinger.org/blog/wp-content/uploads/2008/10/m-m.png" height="231" alt="" width="500" /><br />
（图示：渐进增强的巧克力层）</p>
<p>从你的内容花生开始，将其标记为富含语义的(X)HTML. 接着给内容裹上一层富含奶油的CSS. 最后，添加JavaScript作为糖果硬壳，这就做成了一颗可口无比的美味（并使得它不会在你手里融化）。</p>
<p>如果你非常熟悉Web标准化运动的口号&mdash;&mdash;分离、分离、分离&mdash;&mdash;这个类比就相当清楚了。基于Web标准的开发经常被比作<a href="http://www.flickr.com/photos/aarongustafson/83123599/">夹心蛋糕</a>（或者，可以更富想像力地比作为<a href="http://www.stuffandnonsense.co.uk/archives/web_standards_trifle.html">松糕</a>（译注：一种多层蛋糕，可以夹水果、奶油、沙司等））。</p>
<p>我更倾向拿M&amp;M花生巧克力来类比，因为它的外层把内容完全包裹住了，这和我们的样式与脚本将内容包裹起来几乎是一样的。</p>
<p>如果你允许我的食物类比论再多讲一点（希望不会让你感觉饥饿），我将解释为什么这种方式更好以及在这种模式下各层之间是如何交互的。</p>
<p>&nbsp;</p>
<h4>花生仁</h4>
<p>有些人喜欢花生。实际上，有人喜欢花生胜过M&amp;M花生巧克力。类似地，有些家伙（以及像搜索引擎爬虫这类东西）只想要内容。</p>
<p>还有些人无福消受花生上的巧克力和糖果层（例如糖尿病患者）。和他们类似，移动设备或老浏览器用户可能无法看到你漂亮的设计，或者与你流畅的AJAX驱动的界面交互。</p>
<p>确保你的标记能够将所包裹的内容的细节最大限度地传达出来，这对于给这些用户提供基本体验至关重要。</p>
<p>&nbsp;</p>
<h4>巧克力外衣</h4>
<p>接下来，你可以将内容小心地浸入芳香美味的CSS暖浴中了。不过在你跳进糖果硬壳之前，还有些额外的考虑。</p>
<p>有的人爱吃巧克力裹着的花生。这些人就像中级用户，他们的浏览器有比较好的CSS支持，但可能没有很好的JavaScript支持。或者，可能在他
们工作的公司里，IT安全人员对JavaScript极其病态性地恐惧。对他们来说，JavaScript就可能完全被禁用了。</p>
<p>无论是倾向于爱吃<a href="http://en.wikipedia.org/wiki/Chocolate-coated_peanut">巧克力花生</a>（译注：一种直接用巧克力覆盖的花生，没有外层硬壳，类似于有内容和CSS但没有JavaScript支持的网站）还是被限制只能吃<a href="http://en.wikipedia.org/wiki/Chocolate-coated_peanut">巧克力花生</a>，这些人都应该得到满足。这里有几种渐进增强的方式可以将样式应用于内容，这将是本系列第二篇文章的话题。</p>
<p>&nbsp;</p>
<h4>糖果硬壳</h4>
<p>最后，你可以将JavaScript添加到内容和样式的混合体中了。JavaScript提供了富交互的可能性，同时具有操作内容层和展现层并与其交互的能力，这实际上使得JavaScript成为了把网站带入&ldquo;体验&rdquo;高度的一味配料。</p>
<p>我不确定糖果硬壳到底是怎样添加到M&amp;M花生巧克力上的（我猜是另一种什么浸蘸过程吧），但是，你脑中想着渐进增强的话，在你的网站上加入
基于JavaScript的功能和交互就轻而易举了。另外，就如M&amp;M花生巧克力有各种各样颜色一样，依据所运行的浏览器和设备的能
力，JavaScript的体验也可以各不相同。</p>
<p>正如你可能知道的那样，这种类型的开发叫做无侵入式（Unobtrusive）JavaScript. 我将在本系列的第三篇和最后一篇文章中讲述这些技巧和实践。</p>
<p>&nbsp;</p>
<h3>都放在一起</h3>
<p>一旦理解了渐进增强的理念并开始在实践中使用，那么用渐进增强进行开发就非常简单了。也许比做糖果还简单。本系列接下来的两篇文章将帮助你使用CSS和JavaScript来磨练你的渐进增强技巧，并向你展示怎样把哲学转换成代码。</p>
<p>&nbsp;</p>
<p>~~~~~~</p>
<h3>译注：</h3>
<ol>
<li>Graceful Degradation有译为预留退路、平稳退化的，但我觉得这两个翻译没有表达原意，不如直接翻译成优雅降级的好。</li>
<li>Unobtrusive有译为不唐突的、分离的、低调的，在Web开发领域，我觉得翻译成&ldquo;无侵入的&rdquo;最能表达原意。</li>
</ol>
<p>2008年10月8日 射雕&amp;安吉 译于杭州<br />
2008年10月9日 审校</p>
</div>
          <br/><br/>
          <span style="color:red;">
            <a href="http://www.javaeye.com/topic/250687" style="color:red;">已有 <strong>8</strong> 人发表回复，猛击-&gt;&gt;<strong>这里</strong>&lt;&lt;-参与讨论</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;">Windows7在微软WinHEC 2008上揭开神秘面纱</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>Thu, 09 Oct 2008 12:48:34 +0800</pubDate>
        <link>http://www.javaeye.com/topic/250687</link>
        <guid>http://www.javaeye.com/topic/250687</guid>
      </item>
          <item>
        <title>ruby处理中文URL的办法</title>
        <author>JavaEye网站</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://meatloaf.javaeye.com">meatloaf</a>&nbsp;
                    链接：<a href="http://www.javaeye.com/topic/250355" style="color:red;">http://www.javaeye.com/topic/250355</a>&nbsp;
          发表时间: 2008年10月08日
          <br/>
          声明：本文系JavaEye网站发布的原创文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          有些工具，比如浏览器在保存html页面的时候会把其中的中文路径名转化为$#12345的这种形式，这实际上是中文字符的编码点(codepoint)，一般浏览器都能够自动的处理进行转化，不看Html代码，你根本不知道存在这种转化。<br />但我目前有个需求，需要进行html的解析，并且要和本地目录进行比较，因此就需要在ruby中把这种使用编码点的url字符串转化为字符。<br />比如下面这个url<br /><pre name="code" class="java">
src="./&amp;#20912;&amp;#37239;&amp;#19968;&amp;#22799;.files/gif-0268.gif"
</pre><br />一开始，考虑用unpack来做<br /><pre name="code" class="java">
str = path.gsub(/&amp;#(\d+);/) { |s| [$1.to_i(16)].pack("U") } 
puts str
</pre><br />控制台的编码方式是gbk，这时输出是乱码，尝试进行转码<br /><pre name="code" class="java">
path = "./&amp;#20912;&amp;#37239;&amp;#19968;&amp;#22799;.files/gif-0268.gif"
str = path.gsub(/&amp;#(\d+);/) { |s| [$1.to_i(16)].pack("U") }
puts str
require "iconv"
puts Iconv.conv("gbk", "utf-8", str)
</pre><br />没想到输出是一样的<br /><pre name="code" class="java">
./馉鸱埞饳エ稷灆.files/gif-0268.gif
./馉鸱埞饳エ稷灆.files/gif-0268.gif
</pre><br />忽然想到java里面的做法<br /><pre name="code" class="java">
//str是通过正则表达式取出的数字，这样做是可以的
(char)Integer.valueof(str).intValue()
</pre><br />但是ruby并不支持unicode的编码点，因此ruby中int类型的to_char方法最大到256，这样也行不通。<br />于是想到用jruby来绕过这个转换的过程<br /><pre name="code" class="java">
    i = Ja::Integer.new(number)
    c = Ja::Character.new(i.intValue())
    Ja::String.valueOf(c)   
</pre><br />这个代码是可以运行的了。<br /><br />又过了好几天，忽然又想到问题可能是处在对编码点的理解上，一开始用的是16进制，应该使用十进制的，于是又试了一下，问题解决了<br /><pre name="code" class="java">
str = path.gsub(/&amp;#(\d+);/) { |s| [$1.to_i(10)].pack("U") }
puts str
require "iconv"
puts Iconv.conv("gbk", "utf-8", str)
</pre><br />其实，一开始就应该想到这个url是用十进制表示的编码点的，惯性思维，一看到编码点就直接用16进制了，这是个教训，呵呵。<br /><br />今天把这点经验放上来，希望能帮到同样也在做这个事情的朋友。
          <br/><br/>
          <span style="color:red;">
            <a href="http://www.javaeye.com/topic/250355" style="color:red;">已有 <strong>11</strong> 人发表回复，猛击-&gt;&gt;<strong>这里</strong>&lt;&lt;-参与讨论</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;">Windows7在微软WinHEC 2008上揭开神秘面纱</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>Wed, 08 Oct 2008 15:59:01 +0800</pubDate>
        <link>http://www.javaeye.com/topic/250355</link>
        <guid>http://www.javaeye.com/topic/250355</guid>
      </item>
          <item>
        <title>一行代码搞定ActiveRecord的二级缓存</title>
        <author>JavaEye网站</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://quake.javaeye.com">Quake Wang</a>&nbsp;
                    链接：<a href="http://www.javaeye.com/topic/249284" style="color:red;">http://www.javaeye.com/topic/249284</a>&nbsp;
          发表时间: 2008年10月06日
          <br/>
          声明：本文系JavaEye网站发布的原创文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <span style="font-size: large">背景介绍：</span><br /><br />JavaEye在今年1月份发布新版本以来，一直在使用cache_fu插件为整个网站提供缓存功能，在使用cache_fu时有遇到一些api调用不方便的问题：<br />1. 它没有对find(id)方法进行重写：<br /><pre name="code" class="ruby">
class User &lt; ActiveRecord::Base
  acts_as_cached
end

#find没有使用cache机制
User.find(params[:id])
#必需使用get_cache方法，才能让cache生效
User.get_cache(params[:id])
</pre><br /><br />2. 它没有提供belongs_to, has_one等关系上的缓存机制<br /><pre name="code" class="ruby">
class Topic &lt; ActiveRecord::Base
  belongs_to :user
end

#不会使用cache机制
topic.user
</pre><br /><br />3. 它没有针对ActiveRecord的update/destroy做callback，进行expire cache的工作<br /><br />当然这些问题都可以通过自己写一点代码来改善，比如问题2：<br /><a href="http://www.javaeye.com/topic/239580" target="_blank">cache_fu的关联补丁(belongs_to, has_one)</a><br /><br /><br /><span style="font-size: large">Rails.cache来了：</span><br /><br />在Rails2.1以后，Rails提供了一个内置的缓存机制（Rails.cache），看了<a href="http://www.thewebfellas.com/blog/2008/6/9/rails-2-1-now-with-better-integrated-caching" target="_blank">相关的文章</a>介绍以后，我觉得完全可以利用它来取代cache_fu，自制一个简单的ActiveRecord二级缓存。<br /><br />首先整理一下我们的需求，和Hibernate提供的二级缓存机制很类似：<br />1. 对于被标记为使用cache的Model，我们调用find(id)方法，将会先从cache中查找。<br />2. 对于belongs_to/has_one等关系的一方，如果该model被标记为使用cache，调用关系方法的时候也将会先从cache中查找。<br />3. update/destroy被缓存的模型能够自动清除缓存。<br /><br />通过阅读ActiveRecord的源码，发现其实我们只需要在Base.find_one方法上做一些小动作就可以了：<br /><pre name="code" class="ruby">
def find_one_with_cache(id, options)
  Rails.cache.fetch(cache_key(id)) {find_one_without_cache(id, options)}
end

alias_method_chain :find_one, :cache
</pre><br />一行代码就搞定了1和2的需求，需求3也很简单，写2个after_update/after_destroy的事件就可以了，也是一行代码搞定<br />最终的代码见附件，我承认做了一回标题党，其实搞定这个自制插件最终用了大概50行左右的代码，但是每个核心方法还是只有1~2行&nbsp;<img src="/images/smiles/icon_smile.gif"/>，我想这得益于Ruby的语言特性和Rails的设计<br /><br /><br /><span style="font-size: large">如何使用：</span><br /><br />只需要将文件解压到plugins目录下面，然后在environment.rb或者production.rb配置具体的cache机制即可：<br /><pre name="code" class="ruby">
config.cache_store = :mem_cache_store
</pre><br /><br />然后在你需要做缓存的对象上加上一句acts_as_cached（这里为了从cache_fu方便迁移，我使用了同名）。<br /><pre name="code" class="ruby">
class User &lt; ActiveRecord::Base
  acts_as_cached
end
</pre><br />然后观察log，就会出现类似这样的输出，说明缓存起作用了：<br />Cache miss: User/1 ({})<br />Cache hit: User/1 ({})<br />Cache hit: User/1 ({})<br /><br /><br /><span style="font-size: large">小技巧： </span><br /><br />1. 定义缓存失效时间： acts_as_cached :expires_in => 6.hours<br />2. 在单元测试代码中禁用缓存，你可以在environment/test.rb里面设置一个无法访问的memcache地址，比如：<br />config.cache_store = :mem_cache_store, "disable.test.cache.localhost"<br />3. 类似hibernate的query cache：<br /><pre name="code" class="ruby">
class Forum &lt; ActiveRecord::Base
  acts_as_cached
  def self.all_for_option
    get_cache("all_for_options") {find(:all, :order => 'category, position')}
  end  
end
</pre><br />不过这个简单插件没有提供hibernate那样完善的对query cache自动清理的功能，你可以试试看添加这个特性<br />4. memcached的undefined class/module错误，从cache_fu抄袭了一个autoload_missing_constants方法来解决这个问题：<br /><a href="http://www.philsergi.com/2007/06/rails-memcached-undefinded-classmodule.html" target="_blank">http://www.philsergi.com/2007/06/rails-memcached-undefinded-classmodule.html</a><br /><br /><br />最后show一下JavaEye的<a href="http://pecl.php.net/package/memcache" target="_blank">memcache stats</a><br /><img src="http://www.javaeye.com/upload/attachment/41180/7fb9f4c0-76b3-3217-8901-30f2a4df4bf8.png" />
          <br/><br/>
          <span style="color:red;">
            <a href="http://www.javaeye.com/topic/249284" style="color:red;">已有 <strong>18</strong> 人发表回复，猛击-&gt;&gt;<strong>这里</strong>&lt;&lt;-参与讨论</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;">Windows7在微软WinHEC 2008上揭开神秘面纱</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 06 Oct 2008 11:59:07 +0800</pubDate>
        <link>http://www.javaeye.com/topic/249284</link>
        <guid>http://www.javaeye.com/topic/249284</guid>
      </item>
          <item>
        <title>Cucumber - 将在RSpec1.1.5中取代Story Runner</title>
        <author>JavaEye网站</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://zhangpeihao.javaeye.com">zhangpeihao</a>&nbsp;
                    链接：<a href="http://www.javaeye.com/topic/248074" style="color:red;">http://www.javaeye.com/topic/248074</a>&nbsp;
          发表时间: 2008年09月29日
          <br/>
          声明：本文系JavaEye网站发布的原创文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p style="text-align: center;"><span style="font-size: medium;"><strong>Cucumber</strong>
（黄瓜）</span>
</p>
<p style="text-align: center;">原文：<a href="http://blog.davidchelimsky.net/2008/9/22/cucumber" title="Cucumber" target="_blank">Cucumber</a>
</p>
<p>在即将推出的RSpec1.1.5发布版中，将用Aslak Helles&oslash;y的<a href="http://github.com/aslakhellesoy/cucumber/tree/master" title="Cucumbe" target="_blank">Cucumber</a>
库来取代RSpec的故事运行器。</p>
<p>&nbsp;</p>
<p>Cucumber是完全重写的故事运行器。她使用了Treetop语法解析器。当我刚开始使用纯文本故事来工作的时候，由于很多原因我没有写一个语法解析器而是使用自己的解析。后来证明这给我们带来了许多好处。</p>
<p>&nbsp;</p>
<p><strong>Cucumber支持多种口语</strong>
</p>
<p>&nbsp;</p>
<p>现在你可以这样来描述：</p>
<p>&nbsp;</p>
<pre name="code" class="ruby">Funcionalidade: Adição
  Para evitar erros bobos
  Como um péssimo matemático
  Eu quero saber como somar dois números

  Cenário: Adicionar dois números
    Dado que eu digitei 50 na calculadora
    E que eu digitei 70 na calculadora
    Quando eu aperto o botão de soma
    Então o resultado na calculadora deve ser 120
</pre>
<p>&nbsp;用葡萄牙语写的，完全合法！（酷吧！虽然我一点也不明白写了些什么）</p>
<p>&nbsp;</p>
<p>现在已经支持了几种语言，增加新的语言需要一些工作，所以过一段时间我们也许会为增加你们自己的语言提供支持。</p>
<p>&nbsp;</p>
<p><strong>改善了代码回查</strong>
</p>
<p>&nbsp;</p>
<p>Cucumber在纯文本功能（Feature）文件上增加了行号，这使得定位错误变得非常非常方便。（注意：现在我们用功能取代了故事-在这个主题的另一个帖子里可以找到）</p>
<p>&nbsp;</p>
<p><strong>更简单的配置</strong>
</p>
<p>&nbsp;</p>
<p>Cucumber去掉了steps_for和using_steps_for。而是通过Given, When和Then函数简单地定义步骤：</p>
<pre name="code" class="ruby">#features/steps/accounts.rb
Given /I have \$(\d+) in my (.*) account/ do |dollars, account_type|
  ...
end
</pre>
<p>&nbsp;接下来，你需要引用包含这个步骤定义的文件：</p>
<pre name="code" class="ruby">cucumber -r features/steps/accounts.rb features/transfer_money
</pre>
<p>&nbsp;这样就搞定了。而且，大多数情况下，你都不需要这样明确地指定，你可以只是：</p>
<pre name="code" class="ruby">cucumber features</pre>
<p>&nbsp;Cucumber在运行功能文件之前会自动引用features目录下的所有.rb文件。</p>
<p>&nbsp;</p>
<p><strong>更少的手足无措<br />
</strong>
</p>
<p>&nbsp;</p>
<p>当RSpec的故事运行器在一个步骤的处理里找到了一个以上的步骤定义的话，找到的第一定义胜出。这可能会导致很多痛苦的调试工作。</p>
<p>&nbsp;</p>
<p>当Cucumber发现一个步骤的处理里能找到了一个以上的步骤定义的话，你将得到一个错误结果，告诉你步骤定义存在冲突，同时提供它们的位置（文件和行号），这样你就可以容易地找到和解决这个冲突了。</p>
<p>&nbsp;</p>
<p><strong>如果你已经使用了故事运行器，Cucumber对你意味着什么<br />
</strong>
</p>
<p>&nbsp;</p>
<p>Cucumber只有几个月大，几乎和RSpec的故事运行器的功能没有冲突，并且增加了很多功能强大的新功能。Aslak也把许多故事转换成了Cucumber的功能，并且<a href="http://github.com/aslakhellesoy/cucumber/wikis/migration-from-rspec-stories" target="_blank">把他的经验贴出来了</a>
，在帖子里他精炼出了他所完成的过程。在我们发布Cucumber的官方版本时，这个转移方法将会有很多文档支持并且不难得到。</p>
<p>&nbsp;</p>
<p>至于时限，这很难说。我们一直希望发布1.1.5版，但是由于一些原因一直被推后。我们很可能等到Rails2.2发布之后，在确认它们是否兼容后推出。虽然有传言1.1.5版马上就要发布，但是已经&ldquo;马上&rdquo;了好几个星期，我们只有耐心等待了。</p>
<p>&nbsp;</p>
<p>同时，我们正在结束故事运行器的开发，这样我们才能专注于Cucumber的开发。在Cucumber作为RSpec的官方部件发布之前，我们会在github上另建一个故事运行器的工程（可能叫rspec-stories）这样如果有人想继续使用或者维护故事运行器的话也可以找得到。</p>
<p>&nbsp;</p>
<p>我将在这里和<a href="http://rubyforge.org/mailman/listinfo/rspec-users" target="_blank">rspec-users邮件列表</a>
（<a href="http://groups.google.com/group/rspec" target="_blank">rspec google组</a>
的镜像）继续发布事情的进展。</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
          <br/><br/>
          <span style="color:red;">
            <a href="http://www.javaeye.com/topic/248074" style="color:red;">已有 <strong>3</strong> 人发表回复，猛击-&gt;&gt;<strong>这里</strong>&lt;&lt;-参与讨论</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;">Windows7在微软WinHEC 2008上揭开神秘面纱</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, 29 Sep 2008 14:26:23 +0800</pubDate>
        <link>http://www.javaeye.com/topic/248074</link>
        <guid>http://www.javaeye.com/topic/248074</guid>
      </item>
          <item>
        <title>大胆预测下JS框架的走势</title>
        <author>JavaEye网站</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lifesinger.javaeye.com">lifesinger</a>&nbsp;
                    链接：<a href="http://www.javaeye.com/topic/245057" style="color:red;">http://www.javaeye.com/topic/245057</a>&nbsp;
          发表时间: 2008年09月21日
          <br/>
          声明：本文系JavaEye网站发布的原创文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p class="post-meta">
				<span class="edit"><a href="http://lifesinger.org/blog/wp-admin/post.php?action=edit&amp;post=147" title="Edit post"></a></span>			</p>
<div class="post-content">
<p class="blockstrong"><span style="color: #0000ff; font-size: small;">MooTools将在接下来的几年内像jQuery一样迅速走红<br />
而jQuery则会慢慢销声匿迹<br />
YUI将不尴不尬的活着，YUI 3.x将成为小部分人的玩物，大部分人的忽视物<br />
Ext将在web app应用中有一番作为</span></p>
<h3>理由：</h3>
<ol>
<li>jQuery最大的特点是简洁易用，强大的CSS选择器和简洁易用的API，可以说是以用户为中心的理念在JS框架里的一次完胜。但jQuery
终究只是个人英雄主义的一个产物，其内部的基本架构、代码的可维护性等方面已渐渐散发出腐败的气息。jQuery像是一个一夜走红的神童，如今已渐渐的江
郎才尽，感觉很难再有大的突破。</li>
<li>YUI则带着浓厚的&ldquo;官方、团队&rdquo;js库的气息。其严谨的代码组织风格，以及对web开发思想（指Unobtrusive,
Progressive Enhancement等）的融入，让YUI成为不少开发团队的选择。但YUI
2.x缓慢的更新速度，以及对新思想的接纳程度，很多时候让人恨得牙痒痒，太慢了，和其它新生代框架相比，YUI
2.x像是一个步履蹒跚的老年人，让人很无奈。YUI
3.x目前还处于preview阶段，可以将其看成一个全新的JS库（底层代码全重写了，组织风格做了极大的调整）。YUI
3.x里最明显的一个改变是，将jQuery等框架里的CSS选择器、基本元素（jQuery里jQuery对象，MooTools里的Element对
象）等概念正式化，成为框架最基本的组成部分（在YUI 2.5.x里也有CSS
Selector和Element，但一直处于beta阶段，功能很鸡肋）。YUI
3.x自赞的动态加载，在新生代框架里也是早就有了的。仔细比较后，YUI
3.x并没有带来什么新东西，更多的只是吸收接纳了新生代框架的许多理念。对YUI的前景，就如对YAHOO的期待的一样，我相信它会存活着，但也许仅仅
就是这样活着下去。</li>
<li>MooTools是开源社区形式下发展起来的一个js框架。在它的早期代码里，能感觉到Prototype,
jQuery等框架的痕迹，但它一直保持着开放的心态，小步前进，快速更新。其代码组织风格、对无侵入等Web思想的理解，各个方面都呈现出少年新贵、武
林新秀的姿态来。不仅仅是对其它框架优点的吸收，MooTools每次更新经常给人惊喜：比如lambda表达式，比如Swiff,
还有非常小但很nice的chain操作的改进等等，一点一滴中能看出MooTools的开发者们开放的心态和极其活跃的思路。jQuery是个人英
雄，YUI是官僚体系，MooTools是开放团队，对我来说，一个开放的、活跃的团队下的产物是最让我放心的。</li>
<li>Ext最早叫做YUI-Ext. 一个使用YUI的牛人Jack Slocum,
用YUI用得不大爽，给YUI官方提意见，无奈YUI更新速度忒慢了，于是Slocum叫了声nnd，挽着袖子便自己干了起来。这一干不得了，Ext迅速
流行，噼里啪啦的如今已成立公司，过得很滋润。Ext我用得不多，直觉里感觉Ext过于庞大繁复，也许会在web
app的应用里有一番作为，但对于占据互联网上大部分web page来说，Ext的应用并不乐观。</li>
</ol>
<p>欢迎讨论。</p>
</div>
          <br/><br/>
          <span style="color:red;">
            <a href="http://www.javaeye.com/topic/245057" style="color:red;">已有 <strong>87</strong> 人发表回复，猛击-&gt;&gt;<strong>这里</strong>&lt;&lt;-参与讨论</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;">Windows7在微软WinHEC 2008上揭开神秘面纱</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sun, 21 Sep 2008 11:27:44 +0800</pubDate>
        <link>http://www.javaeye.com/topic/245057</link>
        <guid>http://www.javaeye.com/topic/245057</guid>
      </item>
          <item>
        <title>写给我的团队成员（一）——什么是BUG？</title>
        <author>JavaEye网站</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://neora.javaeye.com">neora</a>&nbsp;
                    链接：<a href="http://www.javaeye.com/topic/241569" style="color:red;">http://www.javaeye.com/topic/241569</a>&nbsp;
          发表时间: 2008年09月14日
          <br/>
          声明：本文系JavaEye网站发布的原创文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我知道你们都很忙。忙得连给代码写注释的时间都没有，哪有时间做总结呢？还是我来替大家做一些总结吧。我最近会找时间写一系列的短文，在email给你们的同时会发送到你们常去的JavaEye上。如果你抽空看看，对你和我们团队都有好处。今天我写了第一篇。</p>
<p>&nbsp;</p>
<p><strong><span style="color: #800000; font-size: small;">写给我的团队成员（一）&mdash;&mdash; 什么是BUG？</span>
</strong>
</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 什么是BUG？每个写过代码或者使用过软件的人似乎都知道它是什么。然而，我们的很多工作年限有限的开发人员总是简单认为：程序跑通了，自己测了N遍了就很少有BUG了。这是个危险的观念，没有理解深刻这一点的人会在自己的进步过中走很多弯路。更会给产品和团队带来各种大大小小的危机。</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对抗BUG是我们程序员永恒的主题，要在这场战斗中获胜，首先要做到&ldquo;知己知彼&rdquo;&mdash;&mdash;什么是BUG?</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 现在，我们来一起把BUG分为以下几个种类，你在Coding的时候要随时随地的想到这些：</p>
<p>&nbsp;</p>
<ul>
<li><strong><span style="color: #800000;">最最普通的BUG。</span>
</strong>
我实在缺乏用语言来给这类BUG下定义的能力，因此你现在能够识别，这就是BUG的东西，应该可以归属于这一类。<br />
</li>
</ul>
<ul>
<li><strong><span style="color: #800000;">编译不通过。</span>
</strong>
你可以认为这是最简单的BUG，根本不需要特别考虑，如果编译不过，Eclipse会在设计时给你个<span style="color: #ff0000;">红XX</span>
来提示的。但是，在下面的情况中，你可能看不到红XX，但BUG依然存在。</li>
</ul>
<blockquote><ol>
<li>spring的xml。缺省的eclipse可不会在design time时给任何检查。你写错一个字母，都会让你无法运行。跟业务逻辑相关的依赖关系，更别指望eclipse替你找出来。</li>
<li>jsp中引用的java代码。不用我解释了吧，大家可能都有体验。至少我目前还没找到完全可靠的jsp plugin 可以帮助 eclipse来随时随地找出jsp中的代码错误。（除非你把上千个jsp文件都关闭并重新打开一遍）。</li>
</ol>
</blockquote>
<ul>
<li><span style="color: #800000;"><strong>业务逻辑实现错误。</strong>
</span>
这就不需要过多赘述了。地球人都知道。</li>
</ul>
<ul>
<li><strong><span style="color: #800000;">缺乏必要的事务。</span>
</strong>
在99.9%的&ldquo;开发时&rdquo;，事务不是必须的。在仅挨着的两条insert语句执行的瞬间，出现系统失效的可能性微乎其微。然而，一旦进入了生产环境，用&ldquo;事务&rdquo;来保持你要进行的这个action的完整性就显得非常重要了。当然，并不是所有的业务逻辑步骤都需要用事务来保护，况且让容器帮你你管理事务也是一种懒惰但有效的做法，但与此同时自己去考虑一下&ldquo;这里如果没有事务，我是否安全？&ldquo;的问题，对你的进步更有好处。</li>
</ul>
<ul>
<li><strong><span style="color: #800000;">团队使用的基本库出错。</span>
</strong>
不要认为团队自己开发的基本类库是100%正确的，轻信不完善的API的思想是大量顽固BUG的藏身之处。团队自己生产的代码还在不断的完善和发展，毕竟咱们积累的这些&rdquo;精华&ldquo;与外面OpenSource的东西（而他们同样有BUG）相比，还差懂得远呢。我丝毫不怀疑里面存在超过100个算法缺陷和200个不安全的使用方式。因此，不要&rdquo;拿起来就用&ldquo;，而要&rdquo;三思而后行&ldquo;。</li>
</ul>
<ul>
<li><strong><span style="color: #800000;">性能陷阱。</span>
</strong>
为了尽快实现业务逻辑。我们在第一次编码的时候往往不先考虑性能问题。这个想法不算太错误，但这个想法不能太过分。特别是涉及到一些&rdquo;性能敏感&rdquo;的代码段，比如我们产品中多处涉及到的Tcp Server的内核。这些部件的代码1天可能遭受几百万次的访问，瞬时绝对并发100是最正常的情况。因此0.1秒的性能损失，也会带来100x0.1=10秒的性能损耗。10秒，足以使一个TCP Server达到实际&ldquo;不可用&rdquo;的严重程度！10行马虎的代码，可能毁掉客户对我们团队辛苦生产的100万代码的信任。切记！切记！</li>
</ul>
<ul>
<li><strong><span style="color: #800000;">安全隐患。</span>
</strong>
某些安全隐患在我们刚开始写实验性的代码时往往可以忽略，但绝不能忘记。你必须在这个产品进入到下一阶段的时候加上必要的安全检查代码和与安全相关的逻辑验证代码。回忆一下，你是否忽略了下面的工作：</li>
</ul>
<blockquote><ol>
<li><span style="text-decoration: underline;"><strong>http session检查。</strong>
</span>
尽管我们可以用框架来保证这一点。但你还是要检视一下，是否在某些功能的实现上，你确实忘记它了。</li>
<li><span style="text-decoration: underline;"><strong>参数类型校验。</strong>
</span>
当你把一个'a'传递到servlet用Internet.parse（）来处理的时候，你是否考虑了可能出现的异常情况。等等此类。</li>
<li><em><strong>NullException。</strong>
</em>
特别注意，千万不要让NullException出现在jsp中，否则你很难在系统部署后排查错误。在你第一次编写jsp代码时，你就必须考虑你所使用的对象或者属性是否可能为Null。</li>
<li><span style="text-decoration: underline;"><strong>Anti-flood。</strong>
</span>
最容易被初级程序员忽略的要点之一。因为这个bug永远不会出现在你的eclipse开发运行环境里。也往往被功能测试组的人忽略。但一旦存在这个隐患，一个最菜的Hacker用最普通的teardrop也会让你tear drop。</li>
</ol>
</blockquote>
<ul>
<li><strong><span style="color: #800000;">线程安全。</span>
</strong>
永远不要忘记，你的代码需要在一个多线程的环境中运行，随时随地都有可能出现并发的情况。你的产生的临时文件名是否用uuid来避免重名了？你的静态（或单态）变量是否线程安全。你是否忘记将spring里定义的bean设置为scope=prototype？</li>
</ul>
<ul>
<li><strong><span style="color: #800000;">忘记删除临时文件。</span>
</strong>
在上传文件、生成验证图片、生成缩略图的时候，你都可能用到临时文件。你是否在使用完毕后及时的删除了它？你是否考虑过在发生异常后，仍然安全的删除了这个文件？特别需要指出的是，我们在编码阶段的测试时，很难发现遗漏临时文件清理的工作。单在系统上线运行后，大量滞留在目录下的过期临时文件将用光客户的服务器磁盘空间，降低系统IO的性能。</li>
</ul>
<ul>
<li><strong><span style="color: #800000;">极不友好的UI操作。</span>
</strong>
极不友好的UI操作同样是严重的BUG。比如：</li>
</ul>
<blockquote><ol>
<li>当用户提交表单的时候可能填写了错误格式的信息，而你的程序再提示错误，重新显示表单的时候清除了用户已经填写的数据。这对你的软件的使用者来说是极其恼火的体验，对于创造这个代码的您来说则是一种耻辱。</li>
<li>另一种&ldquo;极不友好的UI操作&ldquo;可能发生在这种情况&mdash;&mdash;你必须跟测试人员解释&mdash;&mdash;他体验到这次系统出错的原因是他（测试人员）操作的步骤或顺序不正确。天那，这是噩梦，不仅是用户的噩梦，也是你的噩梦。如果你坚持你的做法没错，我将决定在系统上线后，把你的手机和家里的电话号码做为HELP放在你创造的界面的显著位置呈现给使用它的80万用户。</li>
</ol>
</blockquote>
<p>&nbsp;</p>
<p>......</p>
<p>&nbsp;</p>
<p>儿子刚刚给我倒了杯咖啡端倒了书房里，从味道上判断，他在厨房里误把味精当白糖给我放了很多。但无论如何他是在讨好我去陪他玩了。那么，这次就写到这里吧。</p>
<p>&nbsp;</p>
          <br/><br/>
          <span style="color:red;">
            <a href="http://www.javaeye.com/topic/241569" style="color:red;">已有 <strong>34</strong> 人发表回复，猛击-&gt;&gt;<strong>这里</strong>&lt;&lt;-参与讨论</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;">Windows7在微软WinHEC 2008上揭开神秘面纱</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sun, 14 Sep 2008 11:41:45 +0800</pubDate>
        <link>http://www.javaeye.com/topic/241569</link>
        <guid>http://www.javaeye.com/topic/241569</guid>
      </item>
          <item>
        <title>隐式转换：比动态类型更强大？</title>
        <author>JavaEye网站</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://eastsun.javaeye.com">Eastsun</a>&nbsp;
                    链接：<a href="http://www.javaeye.com/topic/242851" style="color:red;">http://www.javaeye.com/topic/242851</a>&nbsp;
          发表时间: 2008年09月16日
          <br/>
          声明：本文系JavaEye网站发布的原创文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          　　本文内容主要来自<a href="http://www.codecommit.com/blog/ruby/implicit-conversions-more-powerful-than-dynamic-typing" target="_blank">Implicit Conversions: More Powerful than Dynamic Typing?</a>，我只对其中一部分进行了翻译。<br />　　隐式转换是<a href="Haskell type classes" target="_blank">Scala</a>中实现的一种类似于Haskell type classes的类型系统，它使得Scala这种静态类型的语言具有某些“动态”的特性。下面用具体例子来说明。<br /><br /><span style="color: red">案例1</span>：<span style="color: blue">给String添加一个reduce方法，其返回值为字符串中大写字母组成的新字符串。</span><br />在Scala中，最终效果如下：<br /><pre name="code" class="java">//Scala
val acronym = "Microsoft Certified Systems Engineer".reduce
println(acronym)            // MCSE</pre><br />　　由于String是Java语言内置的类型，现在遇到的问题是：在静态语言中，我们不能在运行时往已经存在的类中动态添加方法。<br />　　在Scala中，我们可以定义一个新的类型，使它具有所需要的方法，然后再顶一个一个从已有类型到新类型的隐式转换。然后Scala编译器将会在幕后实施其魔法。代码如下：<br /><pre name="code" class="java">class MyStr(str:String) {
    def reduce = str.foldLeft(""){ (s,c) =>
                     if(c.isUpperCase) s+c else s
                 }
}

implicit def str2MyStr(str:String) = new MyStr(str)</pre><br /><br />作为对照，我们看看在Ruby中的实现：<br /><pre name="code" class="java">class String
  def reduce
    arr = unpack('c*').select { |c| (65..90).include? c }
    arr.pack 'c*'
  end
end
 
puts 'HyperText Transfer Protocol'.reduce       # HTTP</pre><br /><br /><br /><span style="color: red">案例2</span>：<span style="color: blue">一个稍微复杂一点的问题：重载整数类型的</span><span style="color: red">&lt;</span><span style="color: blue">操作符，使得它能与String比较，并且但String的长度小于该整数时返回true，否则返回false</span><br />　　在Scala中，同样可以通过隐式转换机制来解决该问题。这一次我们使用比上面更简洁的形式：<br /><pre name="code" class="java">//Scala,that's all
implicit def lessThanOverload(i: Int) = new {
    def &lt;(str: String) = str.length &lt; i
}</pre><br />　　下面我们看看Ruby中如何解决这个问题，一个很自然的想法是玩Fixnum中添加所需的方法：<br /><pre name="code" class="ruby">class Fixnum
  def &lt;(str)
    str.size &lt; self
  end
end</pre><br />　　很不幸的是，这个看起来很对的方法是行不通的。每次调用整数的&lt;操作符时都会进入一个死循环并很快导致堆栈溢出。<br />　　我们可以将方法改写成如下以避免这个问题：<br /><pre name="code" class="ruby">class Fixnum
  def &lt;(str)
    self >= str.size
  end
end</pre><br />　　这次不用担心堆栈溢出了，但新的问题出现了：<br /><div class="quote_title">引用</div><div class="quote_div">irb(main):006:0> 123 &lt; 'test'<br />=> true<br />irb(main):007:0> 123 &lt; 123<br />=> true</div><br />　　这个问题的最终Ruby解法是这样的：<br /><pre name="code" class="ruby">class Fixnum
  alias_method :__old_less_than__, '&lt;'.to_sym
  def &lt;(target)
    if target.kind_of? String
      __old_less_than__ target.size
    else
      __old_less_than__ target
    end
  end
end</pre><br />　　囧，相比Scala，Ruby的解决方法显得即冗长且丑陋。
          <br/><br/>
          <span style="color:red;">
            <a href="http://www.javaeye.com/topic/242851" style="color:red;">已有 <strong>13</strong> 人发表回复，猛击-&gt;&gt;<strong>这里</strong>&lt;&lt;-参与讨论</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;">Windows7在微软WinHEC 2008上揭开神秘面纱</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, 16 Sep 2008 18:37:38 +0800</pubDate>
        <link>http://www.javaeye.com/topic/242851</link>
        <guid>http://www.javaeye.com/topic/242851</guid>
      </item>
          <item>
        <title>JavaScript中的继承学习笔记(1)：Crockford uber方法中的陷阱</title>
        <author>JavaEye网站</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lifesinger.javaeye.com">lifesinger</a>&nbsp;
                    链接：<a href="http://www.javaeye.com/topic/248933" style="color:red;">http://www.javaeye.com/topic/248933</a>&nbsp;
          发表时间: 2008年10月04日
          <br/>
          声明：本文系JavaEye网站发布的原创文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          原文同步发表在<a href="http://lifesinger.org/blog/?p=192" target="_blank">岁月如歌</a>，欢迎讨论交流。<br /><br />先来看 Douglas Crockford 的经典文章：<a href="http://javascript.crockford.com/inheritance.html" target="_blank">Classical Inheritance in JavaScript</a>. 此文的关键技巧是给Function.prototype增加inherits方法，代码如下（注释是我的理解）：<br /><pre name="code" class="javascript">
Function.prototype.method = function (name, func) {
	this.prototype[name] = func;
	return this;
};

Function.method('inherits', function (parent) {
	var d = {}, // 递归调用时的计数器
		// 下面这行已经完成了最简单的原型继承：将子类的prototype设为父类的实例
		p = (this.prototype = new parent());
	
	// 下面给子类增加uber方法（类似Java中的super方法），以调用上层继承链中的方法
	this.method('uber', function uber(name) {
		if (!(name in d)) {
			d[name] = 0;
		}
		var f, r, t = d[name], v = parent.prototype;
		if (t) {
			while (t) {
				// 往上追溯一级
				v = v.constructor.prototype;
				t -= 1;
			}
			f = v[name];
		} else {
			f = p[name];
			if (f == this[name]) {
				f = v[name];
			}
		}
		// 因为f函数中，可能存在uber调用上层的f
		// 不设置d[name]的话，将导致获取的f始终为最近父类的f（陷入死循环）
		d[name] += 1;
		
		// slice.apply的作用是将第2个及其之后的参数转换为数组
		// 第一个参数就是f的名字，无需传递
		// 这样，通过uber调用上层方法时可以传递参数：
		// sb.uber(methodName, arg1, arg2, ...);
		r = f.apply(this, Array.prototype.slice.apply(arguments, [1]));
		
		// 还原计数器
		d[name] -= 1;
		
		return r;
	});
	// 返回this, 方便chain操作
	return this;
});
</pre><br />上面d[name]不好理解，我们来创建一些测试代码：<br /><pre name="code" class="javascript">
function println(msg) {
	document.write(msg + '&lt;br />');
}

// 例1
function A() { }
A.prototype.getName = function () { return 'A'; }; // @1

function B() { }
B.inherits(A);
B.prototype.getName = function () { return this.uber('getName') + ',B'; }; // @2

function C() { }
C.inherits(B);
C.prototype.getName = function () { return this.uber('getName') + ',C'; }; // @3

var c = new C();
println(c.getName()); // => A,B,C
println(c.uber('getName')); // => A,B
</pre><br />c.getName()调用的是@3, @3中的uber调用了@2. 在@2中，又有this.uber('getName'), 这时下面这段代码发挥作用：<br /><pre name="code" class="javascript">
while (t) {
	// 往上追溯一级
	v = v.constructor.prototype;
	t -= 1;
}
f = v[name];
</pre><br />可以看出，d[name]表示的是递归调用时的层级。如果不设此值，@2中的this.uber将指向@2本身，这将导致死循环。Crockford借助d[name]实现了uber对同名方法的递归调用。<br /><br />uber只是一个小甜点。类继承中最核心最关键的是下面这一句：<br /><pre name="code" class="javascript">
p = (this.prototype = new parent());
</pre><br /><strong>将子类的原型设为父类的一个实例，这样子类就拥有了父类的成员，从而实现了一种最简单的类继承机制。</strong>注意JavaScript中，获取obj.propName时，会自动沿着prototype链往上寻找。这就让问题变得有意思起来了：<br /><pre name="code" class="javascript">
// 例2
function D1() {}
D1.prototype.getName = function() { return 'D1' }; // @4

function D2() {}
D2.inherits(D1);
D2.prototype.getName = function () { return this.uber('getName') + ',D2'; }; // @5

function D3() {}
D3.inherits(D2);

function D4() {}
D4.inherits(D3);

function D5() {}
D5.inherits(D4);
D5.prototype.getName = function () { return this.uber('getName') + ',D5'; }; // @6

function D6() {}
D6.inherits(D5);

var d6 = new D6();
println(d6.getName()); // => ?
println(d6.uber('getName')); // => ?
</pre><br />猜猜最后两行输出什么？按照uber方法设计的原意，上面两行都应该输出D1,D2,D5, 然而实际结果是：<br /><pre name="code" class="javascript">
println(d6.getName()); // => D1,D5,D5
println(d6.uber('getName')); // => D1,D5
</pre><br />这是因为Crockford的inherits方法中，考虑的是一种理想情况（如例1），对于例2这种有“断层”的多层继承，d[name]的设计就不妥了。我们来分析下调用链：<br /><br />d6.getName()首先在d6对象中寻找是否有getName方法，发现没有，于是到D6.prototype(一个d5对象)中继续寻找，结果d5中也没有，于是到D5.protoype中寻找，这次找到了getName方法。找到后，立刻执行，注意this指向的是d6. this.uber('getName')此时表示的是d6.uber('getName'). 获取f的代码可以简化为：<br /><pre name="code" class="javascript">
// 对于d6来说, parent == D5
var f, v = parent.prototype;
f = p[name];
// 对于d6来说，p[name] == this[name]
if (f == this[name]) {
    // 因此f = D5.prototype[name]
    f = v[name];
}

// 计数器加1
d[name] += 1;

// 等价为 D5.prototype.getName.apply(d6);
f.apply(this);
</pre><br />至此，一级调用d6.getName()跳转进入二级递归调用D5.prototype.getName.apply(d6). 二级调用的代码可以简化为：<br /><pre name="code" class="javascript">
var f, t = 1, v = D5.prototype;
while (t) {
	// 这里有个陷阱，v.constructor == D1
	// 因为 this.prototype = new parent(), 形成了下面的指针链：
	// D5.prototype = d4
	// D4.prototype = d3
	// D3.prototype = d2
	// D2.prototype = d1
	// 因此v.constructor == d1.constructor
	// 而d1.constructor == D1.prototype.constructor
	// D1.prototype.constructor指向D1本身，因此最后v.constructor = D1
	v = v.constructor.prototype;
	t -= 1;
}
// 这时f = D1.prototype.getName
f = v[name];

d[name] += 1;
// 等价为 D1.prototype.getName.apply(d6)
f.apply(this);
</pre><br />上面的代码产生最后一层调用：<br /><pre name="code" class="javascript">
return 'D1';
</pre><br />因此d6.getName()的输出是D1,D5,D5.<br />同理分析，可以得到d6.uber('getName')的输出是D1,D5.<br /><br />上面分析了“断层”时uber方法中的错误。注意上面提到的v.constructor.prototype产生的陷阱，这个陷阱在“非断层”的理想继承链中也会产生错误：<br /><pre name="code" class="javascript">
// 例3
function F1() { }
F1.prototype.getName = function() { return 'F1'; };

function F2() { }
F2.inherits(F1);
F2.prototype.getName = function() { return this.uber('getName') + ',F2'; };

function F3() { }
F3.inherits(F2);
F3.prototype.getName = function() { return this.uber('getName') + ',F3'; };

function F4() { }
F4.inherits(F3);
F4.prototype.getName = function() { return this.uber('getName') + ',F4'; };

var f3 = new F3();
println(f3.getName()); // => F1,F2,F3

var f4 = new F4();
println(f4.getName()); // => F1,F3,F4
</pre><br />很完美的一个类继承链，但f4.getName()没有产生预料中的输出，这就是v.constructor.prototype这个陷阱导致的。<br /><br /><strong>小结</strong><br /><ul><li>在JavaScript中，模拟传统OO模型来实现类继承不是一个很好的选择（上面想实现一个uber方法都困难重重）。</li><li>在JavaScript中，考虑多重继承时，要非常小心。尽可能避免多重继承，保持简单性。</li><li>理解JavaScript中的普通对象，Function对象，Function对象的prototype和constructor, 以及获取属性时的原型追溯路径非常重要。（比如上面提到的constructor陷阱）</li><li>Crockford是JavaScript界的大仙级人物，但其代码中依旧有陷阱和错误。刚开始我总怀疑是不是自己理解错了，费了牛劲剖析了一把，才敢肯定是Crockford考虑不周，代码中的错误是的的确确存在的。学习时保持怀疑的态度非常重要。</li></ul><br /><br /><strong>后续</strong><br /><br />上面的分析花了一个晚上的时间，今天google了一把，发现对Crockford的<a href="http://blog.csdn.net/chensheng913/archive/2006/12/28/1465741.aspx" target="_blank">uber方法中的错误</a>能搜到些零星文章，还有人给出了<a href="http://hax.pie4.us/2006/12/bug-of-douglas-crockfords-uber.html" target="_blank">修正方案</a>（忍不住八卦一把：从链接上看，是CSDN上的一位兄弟第一次指出了Crockford uber方法中的这个bug，然后John Hax（估计也是个华人）给出了修正方案。更有趣的是，Crockford不知从那里得知了这个bug, 如今<a href="http://javascript.crockford.com/inheritance.html" target="_blank">Classical Inheritance in JavaScript</a>这篇文章中已经是修正后的版本^o^）。<br /><br />这里发现的uber方法中的<strong>constructor陷阱</strong>，尚无人提及。导致constructor陷阱的原因是：<br /><pre name="code" class="javascript">
p = (this.prototype = new parent());
</pre><br />上面这句导致while语句中v.constructor始终指向继承链最顶层的constructor. 分析出了原因，patch就简单了：<br /><pre name="code" class="javascript">
// patched by lifesinger@gmail.com 2008/10/4
Function.method('inherits', function (parent) {
    var d = { }, 
        p = (this.prototype = new parent());
        // 还原constructor
        p.constructor = this;
        // 添加superclass属性
        p.superclass = parent;
                
    this.method('uber', function uber(name) {
        if (!(name in d)) {
            d[name] = 0;
        }
        var f, r, t = d[name], v = parent.prototype;
        if (t) {
            while (t) {
                // 利用superclass来上溯，避免contructor陷阱
                v = v.superclass.prototype;
                // 跳过“断层”的继承点
                if(v.hasOwnProperty(name)) {
                    t -= 1;
                }
            }
            f = v[name];
        } else {
            f = p[name];
            if (f == this[name]) {
                f = v[name];
            }
        }
        d[name] += 1;        
        if(f == this[name]) { // this[name]在父类中的情景
            r = this.uber.apply(this, Array.prototype.slice.apply(arguments));
        } else {
            r = f.apply(this, Array.prototype.slice.apply(arguments, [1]));
        }
        d[name] -= 1;
        return r;
    });
    return this;
});
</pre><br />测试页面：<a href="/blog/wp-content/uploads/2008/10/crockford_classic_inheritance_test.html" target="_blank">crockford_classic_inheritance_test.html</a><br /><br />最后以Douglas Crockford的总结结尾：<br /><div class="quote_title">引用</div><div class="quote_div"><br />我编写JavaScript已经8个年头了，从来没有一次觉得需要使用uber方法。在类模式中，super的概念相当重要；但是在原型和函数式模式中，super的概念看起来是不必要的。现在回顾起来，我早期在JavaScript中支持类模型的尝试是一个错误。<br /></div>
          <br/><br/>
          <span style="color:red;">
            <a href="http://www.javaeye.com/topic/248933" style="color:red;">已有 <strong>10</strong> 人发表回复，猛击-&gt;&gt;<strong>这里</strong>&lt;&lt;-参与讨论</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;">Windows7在微软WinHEC 2008上揭开神秘面纱</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 04 Oct 2008 21:21:17 +0800</pubDate>
        <link>http://www.javaeye.com/topic/248933</link>
        <guid>http://www.javaeye.com/topic/248933</guid>
      </item>
          <item>
        <title>五款常用mysql slow log分析工具的比较</title>
        <author>JavaEye网站</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://galaxystar.javaeye.com">galaxystar</a>&nbsp;
                    链接：<a href="http://www.javaeye.com/topic/242516" style="color:red;">http://www.javaeye.com/topic/242516</a>&nbsp;
          发表时间: 2008年09月15日
          <br/>
          声明：本文系JavaEye网站发布的原创文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p><span style="font-family: 'Times New Roman'; font-size: 16px;">
<div style="min-height: 1100px; font-family: Arial; font-size: 10pt; line-height: normal; background-color: #ffffff; padding: 0px; margin: 6px;">mysql slow log 是用来记录执行时间较长(超过long_query_time秒)的sql的一种日志工具.
<div style="margin-top: 0px; margin-bottom: 0px;">
<h3 style="font-size: 12pt;">启用 slow log<br /></h3>
<div style="margin-top: 0px; margin-bottom: 0px;">有两种启用方式:</div>
1, 在my.cnf 里 通过 log-slow-queries[=file_name]</div>
<div style="margin-top: 0px; margin-bottom: 0px;">2, 在mysqld进程启动时,指定--log-slow-queries[=file_name]选项</div>
<h3 style="font-size: 12pt;">比较的五款常用工具<br /></h3>
mysqldumpslow, mysqlsla, myprofi, mysql-explain-slow-log, mysqllogfilter
<div style="margin-top: 0px; margin-bottom: 0px;"><br />
<div style="margin-top: 0px; margin-bottom: 0px;"><span style="font-weight: bold;">mysqldumpslow</span>, mysql官方提供的慢查询日志分析工具. 输出图表如下:</div>
<div style="margin-top: 0px; margin-bottom: 0px;">
<div id="vf-i" style="text-align: left; padding-top: 1em; padding-bottom: 1em; padding-right: 0px; padding-left: 0px; margin-top: 0px; margin-bottom: 0px;"><img src="http://docs.google.com/File?id=dtbhrmv_480cb9vf8hq_b" alt="" style="width: 643px; height: 269px;" /></div>
</div>
<div style="margin-top: 0px; margin-bottom: 0px;">主要功能是,&nbsp;统计不同慢sql的</div>
<div style="margin-top: 0px; margin-bottom: 0px;">出现次数(Count),&nbsp;</div>
<div style="margin-top: 0px; margin-bottom: 0px;">执行最长时间(Time),&nbsp;</div>
<div style="margin-top: 0px; margin-bottom: 0px;">累计总耗费时间(Time),&nbsp;</div>
<div style="margin-top: 0px; margin-bottom: 0px;">等待锁的时间(Lock),&nbsp;</div>
<div style="margin-top: 0px; margin-bottom: 0px;">发送给客户端的行总数(Rows),&nbsp;</div>
<div style="margin-top: 0px; margin-bottom: 0px;">扫描的行总数(Rows),&nbsp;</div>
<div style="margin-top: 0px; margin-bottom: 0px;">用户以及sql语句本身(抽象了一下格式, 比如 limit 1, 20 用 limit N,N 表示).</div>
<div style="margin-top: 0px; margin-bottom: 0px;"><span style="font-weight: bold;"><br /></span></div>
<div style="margin-top: 0px; margin-bottom: 0px;"><span style="font-weight: bold;">mysqlsla<span style="font-weight: normal;">, hackmysql.com推出的一款日志分析工具(该网站还维护了 mysqlreport, mysqlidxchk 等比较实用的mysql工具)</span><br /></span></div>
<div style="margin-top: 0px; margin-bottom: 0px;">
<div id="dt4b" style="text-align: left; padding-top: 1em; padding-bottom: 1em; padding-right: 0px; padding-left: 0px; margin-top: 0px; margin-bottom: 0px;"><img src="http://docs.google.com/File?id=dtbhrmv_481fwd54r4n_b" alt="" style="width: 642px; height: 343px;" /></div>
</div>
<div style="margin-top: 0px; margin-bottom: 0px;">整体来说, 功能非常强大. 数据报表,非常有利于分析慢查询的原因, 包括执行频率, 数据量, 查询消耗等.<br /></div>
<div style="margin-top: 0px; margin-bottom: 0px;"><br /></div>
<div style="margin-top: 0px; margin-bottom: 0px;">格式说明如下:</div>
<div style="margin-top: 0px; margin-bottom: 0px;">总查询次数 (queries total), 去重后的sql数量 (unique)</div>
<div style="margin-top: 0px; margin-bottom: 0px;">输出报表的内容排序(sorted by)</div>
<div style="margin-top: 0px; margin-bottom: 0px;">最重大的慢sql统计信息, 包括 平均执行时间, 等待锁时间, 结果行的总数, 扫描的行总数.</div>
<div style="margin-top: 0px; margin-bottom: 0px;"><br /></div>
<div style="margin-top: 0px; margin-bottom: 0px;">Count, sql的执行次数及占总的slow log数量的百分比.</div>
<div style="margin-top: 0px; margin-bottom: 0px;">Time, 执行时间, 包括总时间, 平均时间, 最小, 最大时间, 时间占到总慢sql时间的百分比.</div>
<div style="margin-top: 0px; margin-bottom: 0px;">95% of Time, 去除最快和最慢的sql, 覆盖率占95%的sql的执行时间.</div>
<div style="margin-top: 0px; margin-bottom: 0px;">Lock Time, 等待锁的时间.</div>
<div style="margin-top: 0px; margin-bottom: 0px;">95% of Lock , 95%的慢sql等待锁时间.</div>
<div style="margin-top: 0px; margin-bottom: 0px;">Rows sent, 结果行统计数量, 包括平均, 最小, 最大数量.<br />Rows examined, 扫描的行数量.</div>
<div style="margin-top: 0px; margin-bottom: 0px;">Database, 属于哪个数据库</div>
<div style="margin-top: 0px; margin-bottom: 0px;">Users, 哪个用户,IP, 占到所有用户执行的sql百分比</div>
<div style="margin-top: 0px; margin-bottom: 0px;"><br /></div>
<div style="margin-top: 0px; margin-bottom: 0px;">Query abstract, 抽象后的sql语句</div>
<div style="margin-top: 0px; margin-bottom: 0px;">Query sample, sql语句</div>
<div style="margin-top: 0px; margin-bottom: 0px;"><br /></div>
<div style="margin-top: 0px; margin-bottom: 0px;">除了以上的输出, 官方还提供了很多定制化参数, 是一款不可多得的好工具.</div>
<div style="margin-top: 0px; margin-bottom: 0px;"><br /></div>
<div style="margin-top: 0px; margin-bottom: 0px;"><span style="font-weight: bold;">mysql-explain-slow-log</span>, 德国人写的一个perl脚本.<br /><a href="http://www.willamowius.de/mysql-tools.html" style="color: #551a8b;">http://www.willamowius.de/mysql-tools.html</a><br /><br /></div>
<div style="margin-top: 0px; margin-bottom: 0px;"><img src="http://docs.google.com/File?id=dtbhrmv_482dbz73bc2_b" alt="" style="width: 642px; height: 188px;" /><br /></div>
<div style="margin-top: 0px; margin-bottom: 0px;">
<div id="zs8m" style="text-align: left; padding-top: 1em; padding-bottom: 1em; padding-right: 0px; padding-left: 0px; margin-top: 0px; margin-bottom: 0px;">
<div id="hkj8" style="text-align: left; padding-top: 1em; padding-bottom: 1em; padding-right: 0px; padding-left: 0px; margin-top: 0px; margin-bottom: 0px;"><img src="http://docs.google.com/File?id=dtbhrmv_4835rp8jbgx_b" alt="" style="width: 637px; height: 184px;" /></div>
<div id="hkj8" style="text-align: left; padding-top: 1em; padding-bottom: 1em; padding-right: 0px; padding-left: 0px; margin-top: 0px; margin-bottom: 0px;">功能上有点瑕疵, 不仅把所有的 slow log 打印到屏幕上, 而且统计也只有数量而已. 不推荐使用.</div>
<div id="hkj8" style="text-align: left; padding-top: 1em; padding-bottom: 1em; padding-right: 0px; padding-left: 0px; margin-top: 0px; margin-bottom: 0px;"><span style="font-weight: bold;">mysql-log-filter</span>, google code上找到的一个分析工具.提供了 python 和 php 两种可执行的脚本.<br /><a href="http://code.google.com/p/mysql-log-filter/" style="color: #551a8b;">http://code.google.com/p/mysql-log-filter/</a></div>
<div id="hkj8" style="text-align: left; padding-top: 1em; padding-bottom: 1em; padding-right: 0px; padding-left: 0px; margin-top: 0px; margin-bottom: 0px;"><img src="http://docs.google.com/File?id=dtbhrmv_484d77sswt8_b" alt="" style="width: 847px; height: 263px;" /></div>
<div id="hkj8" style="text-align: left; padding-top: 1em; padding-bottom: 1em; padding-right: 0px; padding-left: 0px; margin-top: 0px; margin-bottom: 0px;">功能上比官方的mysqldumpslow, 多了查询时间的统计信息(平均,最大, 累计), 其他功能都与 mysqldumpslow类似.<br />特色功能除了统计信息外, 还针对输出内容做了排版和格式化, 保证整体输出的简洁. 喜欢简洁报表的朋友, 推荐使用一下.</div>
<div id="hkj8" style="text-align: left; padding-top: 1em; padding-bottom: 1em; padding-right: 0px; padding-left: 0px; margin-top: 0px; margin-bottom: 0px;"><span style="font-weight: bold;">myprofi</span>, 纯php写的一个开源分析工具.项目在 sourceforge 上.<br /><a href="http://myprofi.sourceforge.net/" style="color: #551a8b;">http://myprofi.sourceforge.net/</a><br /><br /><img src="http://docs.google.com/File?id=dtbhrmv_485c5jzsbdw_b" alt="" style="width: 867px; height: 236px;" /></div>
<div id="hkj8" style="text-align: left; padding-top: 1em; padding-bottom: 1em; padding-right: 0px; padding-left: 0px; margin-top: 0px; margin-bottom: 0px;">功能上, 列出了总的慢查询次数和类型, 去重后的sql语句, 执行次数及其占总的slow log数量的百分比.<br />从整体输出样式来看, 比mysql-log-filter还要简洁. 省去了很多不必要的内容. 对于只想看sql语句及执行次数的用户来说, 比较推荐.</div>
<div id="hkj8" style="text-align: left; padding-top: 1em; padding-bottom: 1em; padding-right: 0px; padding-left: 0px; margin-top: 0px; margin-bottom: 0px;">
<h3 style="font-size: 12pt;">总结<br /></h3>
<div style="margin-top: 0px; margin-bottom: 0px;">
<table class="zeroBorder" cellspacing="0" border="0" id="fiar" width="100%" cellpadding="3" style="font-size: 1em; line-height: inherit; border: 1px dotted gray;">
<tbody>
<tr style="text-align: left;">
<td width="20%" style="border: 1px dotted gray;">工具/功能</td>
<td width="20%" style="border: 1px dotted gray;">一般统计信息</td>
<td width="20%" style="border: 1px dotted gray;">高级统计信息</td>
<td width="20%" style="border: 1px dotted gray;">脚本</td>
<td width="20%" style="border: 1px dotted gray;">优势</td>
</tr>
<tr style="text-align: left;">
<td width="20%" style="border: 1px dotted gray;">mysqldumpslow</td>
<td width="20%" style="border: 1px dotted gray;">支持</td>
<td width="20%" style="border: 1px dotted gray;">不支持</td>
<td width="20%" style="border: 1px dotted gray;">perl</td>
<td width="20%" style="border: 1px dotted gray;">mysql官方自带</td>
</tr>
<tr style="text-align: left;">
<td width="20%" style="border: 1px dotted gray;">mysqlsla</td>
<td width="20%" style="border: 1px dotted gray;">支持</td>
<td width="20%" style="border: 1px dotted gray;">支持</td>
<td width="20%" style="border: 1px dotted gray;">perl</td>
<td width="20%" style="border: 1px dotted gray;">功能强大,数据报表齐全,定制化能力强.</td>
</tr>
<tr style="text-align: left;">
<td width="20%" style="border: 1px dotted gray;">mysql-explain-slow-log</td>
<td width="20%" style="border: 1px dotted gray;">支持</td>
<td width="20%" style="border: 1px dotted gray;">不支持</td>
<td width="20%" style="border: 1px dotted gray;">perl</td>
<td width="20%" style="border: 1px dotted gray;">无</td>
</tr>
<tr style="text-align: left;">
<td width="20%" style="border: 1px dotted gray;">mysql-log-filter</td>
<td width="20%" style="border: 1px dotted gray;">支持</td>
<td width="20%" style="border: 1px dotted gray;">部分支持</td>
<td width="20%" style="border: 1px dotted gray;">python or php</td>
<td width="20%" style="border: 1px dotted gray;">不失功能的前提下,保持输出简洁</td>
</tr>
<tr style="text-align: left;">
<td width="20%" style="border: 1px dotted gray;">myprofi</td>
<td width="20%" style="border: 1px dotted gray;">支持</td>
<td width="20%" style="border: 1px dotted gray;">不支持</td>
<td width="20%" style="border: 1px dotted gray;">php</td>
<td width="20%" style="border: 1px dotted gray;">非常精简</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</span></p>
          <br/><br/>
          <span style="color:red;">
            <a href="http://www.javaeye.com/topic/242516" style="color:red;">已有 <strong>4</strong> 人发表回复，猛击-&gt;&gt;<strong>这里</strong>&lt;&lt;-参与讨论</a>
          </span>
          <br/><br/><br/>
          <s