<?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>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>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>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>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/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>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>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>22</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>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>一行代码搞定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>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/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, 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>写给我的团队成员（二）—— 编程，乐趣何在？</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>44</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, 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>大胆预测下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>86</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>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>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>84</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>写给我的团队成员（一）——什么是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>32</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>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>五款常用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/>
          <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, 15 Sep 2008 19:20:27 +0800</pubDate>
        <link>http://www.javaeye.com/topic/242516</link>
        <guid>http://www.javaeye.com/topic/242516</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>2</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>使用基于邻接表的Dijkstra算法求解Project Euler问题</title>
        <author>JavaEye网站</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://eastsun.javaeye.com">Eastsun</a>&nbsp;
                    链接：<a href="http://www.javaeye.com/topic/248941" style="color:red;">http://www.javaeye.com/topic/248941</a>&nbsp;
          发表时间: 2008年10月04日
          <br/>
          声明：本文系JavaEye网站发布的原创文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <span style="color: black"><strong><span style="font-size: medium">Project Euler中的几个问题</span></strong></span><br /><br />　　首先，来看一看<a href="http://projecteuler.net" target="_blank">Project Euler</a>上的第81到83题。这几个题目的前提条件是一样的，已知一个<a href="http://projecteuler.net/project/matrix.txt" target="_blank">80×80的矩阵</a>(由正整数组成)<br />　　<a href="http://projecteuler.net/index.php?section=problems&id=81" target="_blank">81题</a>：<span style="color: green">Find the minimal path sum, in the 80 by 80 matrix, from the top left to the bottom right by only moving right and down.</span><br />　　其意思是：<span style="color: orange">只允许从上往下或从左到右，找一条从这个矩阵左上角到右下角的路径，使得路径上的数字之和最小。</span><br /><br />　　<a href="http://projecteuler.net/index.php?section=problems&id=82" target="_blank">82题</a>：<span style="color: green">Find the minimal path sum, in the 80 by 80 matrix, from the left column to the right column,by only moving up, down, and right.</span><br />　　该题的意思是：<span style="color: orange">允许从上到下或从下到上或从左到右移动，找一条从该矩阵最左列到最右列的路径，使得路径上的数字之和最小。</span><br /><br />　　<a href="http://projecteuler.net/index.php?section=problems&id=83" target="_blank">83题</a>：<span style="color: green">Find the minimal path sum, in the 80 by 80 matrix, from the top left to the bottom right by moving left, right, up, and down.</span><br />　　翻译过来就是：<span style="color: orange">可以上下左右移动，找一条从该矩阵左上角到右下角的路径，使得路径上数字之和最小。</span><br /><br />　　这几个题非常类似，都是从一个给定矩阵找一条路径使得上面数字之和最小。只不过移动的方式越来越多，因此可以想象这几个题目的难度也是越来越大。如果对动态规划比较熟悉的话，很容易可以看出81与82题是可以使用动态规划来解决的。只不过81题属于典型的动态规划题，很容易写出代码，而82题稍稍麻烦了一点。<br />　　不过这里我不谈怎么使用动态规划来解决这些问题，而是转换一下考察问题的角度，将这几个问题转换成图论中经典的“最短路”问题，然后可以使用同样的方法完美解决之。<br />　　首先，<span style="color: blue">将80×80的矩阵看成一个80×80个顶点的图，如果能够从矩阵(i,j)移动到(u,v)，则用一条(i,j)到(u,v)的边将这两个顶点连接起来，并且该边的权即为矩阵在(u,v)处的取值。这样，就将一个矩阵转换为一个带权的有向图。</span>并且容易看出，题目中要求的“数字之和最小的路径”对应该有向图的一个“最短路”。这三个题的区别只在于顶点与顶点之间连接的边不一样。<br /><br /><br /><span style="color: black"><strong><span style="font-size: medium">Dijkstra算法与实现</span></strong></span><br /><br />　　图论中关于求“最短路”问题的算法有很多，著名的有Floyd-Warshall算法，Bellman-Ford 算法etc。不过，Floyd-Warshall算法虽然实现简单，但时间复杂度是O(n^3)，而这里n = 80*80 = 6400，使用Floyd-Warshall算法时间上无法接受。另外，这三个题中涉及到的有向图都属于<span style="color: orange">稀疏图</span>，所以我采用了基于图的邻接表结构的Dijkstra算法，这个实现的时间复杂度是O(e×log e)，其中e表示边的条数。由于这三个题中e = O(v)（v指图的顶点个数），因此最后我们得到了时间复杂度为O(v log v)的解法（这些题中，v = 80*80）。下面是使用Scala实现的Dijkstra算法：<br /><pre name="code" class="java">/**
  &#Graph.scala
  utils for graph algorithm
  @author Eastsun
*/
package eastsun.math

object Graph {
    /**
      This is an implementation of Dijkstra's algorithm to find the shortest path for a directed
      graph with non-negative edge weights.
    */
    def dijkstra(size :Int,start :Int,lst :Int=> Iterable[(Int,Int)]):Array[Int] = {
        import java.lang.Integer.{ MAX_VALUE => INF }
        implicit def t2o(t :(Int,Int)) = new Ordered[(Int,Int)]{
            def compare(that :(Int,Int)) = that._2 - t._2
        }
        val pq = new scala.collection.mutable.PriorityQueue[(Int,Int)]
        val dist = Array.make(size,INF)
        val mark = Array.make(size,false)
        pq += start->0
        while(!pq.isEmpty){
            val (idx,dst) = pq.dequeue
            if(!mark(idx)){
                mark(idx) = true
                dist(idx) = dst
                for((i,d) &lt;- lst(idx);if dist(i)>dst+d){
                    dist(i) = dst+d
                    pq += i->dist(i)
                }                
            }
        }
        dist   
    }
}</pre><br />　　考虑到对Scala熟悉的不多，我简单解释一下上面这段代码。首先，函数<br /><pre name="code" class="java">    def dijkstra(size :Int,start :Int,lst :Int=> Iterable[(Int,Int)]):Array[Int] </pre><br />有三个参数，其中：<br />　　　　size　　　 <span style="color: orange">表示要考虑的有向图的阶</span>（也就是顶点个数）<br />　　　　start　　　 <span style="color: orange">要求的最短路的起始点</span><br />　　　　lst　　　　 <span style="color: orange">一个参数为Int返回值为Iterable[(Int,Int)]的函数。lst(k)将返回图的第k个顶点对应的邻接表，邻接表的每个节点保存两个值(idx,dst)。其中idx表示顶点k到顶点idx有一条边，dst表示这条边的权值。</span><br />返回值为一个整型数组Array[Int]，该数组保存了最终结果，其长度为size，第k个元素的值表示从顶点start到顶点k的最短距离，如果不能到达，则为Integer.MAX_VALUE。<br /><br />　　其次，看一下这段代码：<br /><pre name="code" class="java">        implicit def t2o(t :(Int,Int)) = new Ordered[(Int,Int)]{
            def compare(that :(Int,Int)) = that._2 - t._2
        }
        val pq = new scala.collection.mutable.PriorityQueue[(Int,Int)]</pre><br />这段代码的功能是new了一个优先队列pq，该优先队列里面保存的数据类型为(idx,dst)。其中idx为顶点的序号，而dst为距离。并且规定了一个顺序Ordered[(Int,Int)]，使得优先队列保持dst最小的在最上面。<br />　　可以计算，上面的Dijkstra实现的时间复杂度为O(e log e)。<br /><br /><br /><span style="color: black"><strong><span style="font-size: medium">解题代码</span></strong></span><br />　　有了上面的介绍，下面直接给出Project Euler这几个问题的代码，可以看到，这几个问题的解题代码非常一致（82题略有不同）<br />81题的解题代码：<br /><pre name="code" class="java">import eastsun.math.Graph._
import scala.io.Source._

object Euler081 extends Application {
    val mtx = fromFile("matrix.txt").getLines.map{ line =>
                  line.split(",").map(_.trim.toInt)
              }.toList.toArray
  
    val LEN = mtx.size
    val SIZE = LEN*LEN
    def lst(n :Int):List[(Int,Int)] = {
        val (x,y) = (n/LEN,n%LEN)
        var ls = Nil:List[(Int,Int)]
        if(x &lt; LEN-1) ls = (n+LEN,mtx(y)(x+1))::ls
        if(y &lt; LEN-1) ls = (n+1,mtx(y+1)(x))::ls
        ls
    }
    println(dijkstra(SIZE,0,lst)(SIZE-1)+mtx(0)(0))
}
</pre><br /><br />82题的解题代码：<br /><pre name="code" class="java">import scala.io.Source._
import eastsun.math.Graph._

object Euler082 extends Application {
    val mtx = fromFile("matrix.txt").getLines.map{ line =>
                  line.split(",").map(_.trim.toInt)
              }.toList.toArray
    val LEN = mtx.size
    val SIZE = LEN*LEN
    def lst(n :Int):List[(Int,Int)] = {
        val (x,y) = (n/LEN,n%LEN)
        var ls = Nil:List[(Int,Int)]
        if(y > 0)     ls = (n-1,mtx(y-1)(x))::ls
        if(y &lt; LEN-1) ls = (n+1,mtx(y+1)(x))::ls
        if(x &lt; LEN-1) ls = (n+LEN,mtx(y)(x+1))::ls
        ls
    }
    val res = 0.until(LEN).map{ n =>
                  dijkstra(SIZE,n,lst).slice(SIZE-LEN,SIZE).reduceLeft(_ min _)+mtx(n)(0)
              }.reduceLeft(_ min _)
    println(res)
}</pre><br /><br />83题的解题代码：<br /><pre name="code" class="java">import scala.io.Source._
import eastsun.math.Graph._

object Euler083 extends Application {
    val mtx = fromFile("matrix.txt").getLines.map{ line =>
                  line.split(",").map(_.trim.toInt)
              }.toList.toArray
    val LEN = mtx.size
    val SIZE = LEN*LEN
    def lst(n :Int):List[(Int,Int)] = {
        val (x,y) = (n/LEN,n%LEN)
        var ls = Nil:List[(Int,Int)]
        if(y > 0)     ls = (n-1,mtx(y-1)(x))::ls
        if(y &lt; LEN-1) ls = (n+1,mtx(y+1)(x))::ls
        if(x > 0)     ls = (n-LEN,mtx(y)(x-1))::ls
        if(x &lt; LEN-1) ls = (n+LEN,mtx(y)(x+1))::ls
        ls
    }

    println(dijkstra(SIZE,0,lst)(SIZE-1)+mtx(0)(0))
}</pre>
          <br/><br/>
          <span style="color:red;">
            <a href="http://www.javaeye.com/topic/248941" style="color:red;">已有 <strong>0</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:56:25 +0800</pubDate>
        <link>http://www.javaeye.com/topic/248941</link>
        <guid>http://www.javaeye.com/topic/248941</guid>
      </item>
          <item>
        <title>MemCached Cache Java Client封装优化历程</title>
        <author>JavaEye网站</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://cenwenchu.javaeye.com">cenwenchu</a>&nbsp;
                    链接：<a href="http://www.javaeye.com/topic/246729" style="color:red;">http://www.javaeye.com/topic/246729</a>&nbsp;
          发表时间: 2008年09月25日
          <br/>
          声明：本文系JavaEye网站发布的原创文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: small;"><span style="color: #000000;"><span lang="EN-US"><span style="font-family: Calibri;">Author</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri;">：文初</span></span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span lang="EN-US"><span style="font-size: small; color: #000000; font-family: Calibri;">Email: </span><a href="mailto:wenchu.cenwc@alibaba-inc.com"><span style="text-decoration: underline;"><span style="font-size: small; color: #0000ff; font-family: Calibri;">wenchu.cenwc@alibaba-inc.com</span></span></a></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span lang="EN-US"><span style="font-size: small; color: #000000; font-family: Calibri;">Blog: </span><a href="http://blog.csdn.net/cenwenchu79/"><span style="text-decoration: underline;"><span style="font-size: small; color: #800080; font-family: Calibri;">http://blog.csdn.net/cenwenchu79/</span></span></a></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span lang="EN-US"><span style="font-size: small; color: #000000; font-family: Calibri;">&nbsp;</span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: small;"><span style="color: #000000;"><span lang="EN-US"><span style="font-family: Calibri;"><span style="mso-tab-count: 1;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>MemCached Cache</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri;">在大型网站被应用得越来越广泛，不同语言的客户端也都在官方网站上有提供，但是</span><span lang="EN-US"><span style="font-family: Calibri;">Java</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri;">的选择并不多。由于现在的</span><span lang="EN-US"><span style="font-family: Calibri;">MemCached Cache</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri;">服务端是用</span><span lang="EN-US"><span style="font-family: Calibri;">C</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri;">写的，因此我这个</span><span lang="EN-US"><span style="font-family: Calibri;">C</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri;">不太熟悉的人也就没有办法去优化它，当然对于它的内存分配机制等细节还是有所了解，因此在使用的时候也会十分注意，这些文章</span><span lang="EN-US"><span style="font-family: Calibri;">Google</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri;">一把应该也有很多了。这里就说说对于</span><span lang="EN-US"><span style="font-family: Calibri;">MemCache Java</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri;">客户端的优化的两个阶段。</span></span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span lang="EN-US"><span style="font-size: small; color: #000000; font-family: Calibri;">&nbsp;</span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span lang="EN-US" style="font-size: 14pt;"><span style="color: #000000;"><span style="font-family: Calibri;">First Stage</span></span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="color: #000000;"><span lang="EN-US" style="font-size: 14pt;"><span style="mso-tab-count: 1;"><span style="font-family: Calibri;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;"><span style="font-size: small;">我也和其他使用</span></span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-size: small; font-family: Calibri;">Memcached Cache</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;"><span style="font-size: small;">的同学一样，看了官方网站的内容，然后去下载了</span></span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-size: small; font-family: Calibri;">whalin memcached Client</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;"><span style="font-size: small;">，后来</span></span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-size: small; font-family: Calibri;">Stat</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;"><span style="font-size: small;">的时候遇到问题，就给作者发了邮件说明了情况，作者让我下载</span></span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-size: small; font-family: Calibri;"> 2.0.1 </span></span><span style="font-size: small;"><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">版本，这个版本也是比较不错的一个版本，后续的封装也是基于这个版本之上。</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"></span></span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: small;"><span style="color: #000000;"><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="mso-tab-count: 1;"><span style="font-family: Calibri;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">第一阶段主要是在</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-family: Calibri;">whalin</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">的客户端作了再次封装。</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"></span></span></span></p>
<p class="MsoListParagraph" style="margin: 0cm 0cm 0pt 39pt; text-indent: -18pt; mso-char-indent-count: 0; mso-list: l1 level1 lfo1;"><span style="color: #000000;"><span lang="EN-US" style="mso-bidi-font-size: 10.5pt; mso-fareast-font-family: Calibri; mso-bidi-font-family: Calibri;"><span style="mso-list: Ignore;"><span style="font-size: small; font-family: Calibri;">1.</span><span style="font: 7pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-size: small; font-family: Calibri;">Cache</span></span><span style="font-size: small;"><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">服务接口化。</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"></span></span></span></p>
<p class="MsoListParagraph" style="margin: 0cm 0cm 0pt 42pt; mso-char-indent-count: 0;"><span style="font-size: small;"><span style="color: #000000;"><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">定义了</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-family: Calibri;">IMemCache</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">接口，在应用部分仅仅只是使用接口，为将来替换</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-family: Calibri;">Cache</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">服务实现提供基础。</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"></span></span></span></p>
<p class="MsoListParagraph" style="margin: 0cm 0cm 0pt 39pt; text-indent: -18pt; mso-char-indent-count: 0; mso-list: l1 level1 lfo1;"><span style="color: #000000;"><span lang="EN-US" style="mso-bidi-font-size: 10.5pt; mso-fareast-font-family: Calibri; mso-bidi-font-family: Calibri;"><span style="mso-list: Ignore;"><span style="font-size: small; font-family: Calibri;">2.</span><span style="font: 7pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="font-size: small;"><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">使用配置代替代码初始化客户端。</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"></span></span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 39pt; text-indent: 21pt;"><span style="font-size: small;"><span style="color: #000000;"><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">通过配置客户端和</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-family: Calibri;">SocketIO Pool</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">属性，直接交管由</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-family: Calibri;">CacheManager</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">来维护</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-family: Calibri;">Cache Client Pool</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">的生命周期，方便实用以及单元测试。</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"></span></span></span></p>
<p class="MsoListParagraph" style="margin: 0cm 0cm 0pt 39pt; text-indent: -18pt; mso-char-indent-count: 0; mso-list: l1 level1 lfo1;"><span style="color: #000000;"><span lang="EN-US" style="mso-bidi-font-size: 10.5pt; mso-fareast-font-family: Calibri; mso-bidi-font-family: Calibri;"><span style="mso-list: Ignore;"><span style="font-size: small; font-family: Calibri;">3.</span><span style="font: 7pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-size: small; font-family: Calibri;">KeySet</span></span><span style="font-size: small;"><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">的实现。</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"></span></span></span></p>
<p class="MsoListParagraph" style="margin: 0cm 0cm 0pt 42pt; mso-char-indent-count: 0;"><span style="font-size: small;"><span style="color: #000000;"><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">对于</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-family: Calibri;">MemCached</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">来说本身是不提供</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-family: Calibri;">KeySet</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">的方法的，在接口封装初期，同事向我提出这个需求的时候，我个人觉得也是没有必要提供，因为</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-family: Calibri;">Cache</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">轮询是比较低效的，同时这类场景，往往可以去数据源获取</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-family: Calibri;">KeySet</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">，而不是从</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-family: Calibri;">MemCached</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">去获取。但是</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-family: Calibri;">SIP</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">的一个场景的出现，让我不得不去实现了</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-family: Calibri;">KeySet</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">。</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"></span></span></span></p>
<p class="MsoListParagraph" style="margin: 0cm 0cm 0pt 42pt; mso-char-indent-count: 0;"><span style="font-size: small;"><span style="color: #000000;"><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-family: Calibri;">SIP</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">在作服务访问频率控制的时候需要记录在控制间隔期内的访问次数和流量，此时由于是集群，因此数据必须放在集中式的存储或者缓存中，数据库肯定是撑不住这样大数据量的更新频率的，因此考虑使用</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-family: Calibri;">Memcached</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">的很出彩的操作，全局计数器（</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-family: Calibri;">storeCounter,getCounter,inc,dec</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">），但是在检查计数器的时候如何去获取当前所有的计数器，曾考虑使用</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-family: Calibri;">DB</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">或者文件，但是效率还是问题，同时如果放在一个字段中并发还是有问题。因此不得不实现了</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-family: Calibri;">KeySet</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">，在使用</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-family: Calibri;">KeySet</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">的时候有一个参数，类型是</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-family: Calibri;">Boolean</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">，这个字段的存在是因为，在</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-family: Calibri;">Memcached</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">中数据的删除并不是直接删除，而是标注一下，这样会导致实现</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-family: Calibri;">keySet</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">的时候取出可能已经删除的数据，如果对于数据严谨性要求低，速度要求高，那么不需要再去验证</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-family: Calibri;">key</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">是否真的有效，如果要求</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-family: Calibri;">key</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">必须正确存在，就需要再多一次的轮询查找。</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"></span></span></span></p>
<p class="MsoListParagraph" style="margin: 0cm 0cm 0pt 39pt; text-indent: -18pt; mso-char-indent-count: 0; mso-list: l1 level1 lfo1;"><span style="color: #000000;"><span lang="EN-US" style="mso-bidi-font-size: 10.5pt; mso-fareast-font-family: Calibri; mso-bidi-font-family: Calibri;"><span style="mso-list: Ignore;"><span style="font-size: small; font-family: Calibri;">4.</span><span style="font: 7pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-size: small; font-family: Calibri;">Cluster</span></span><span style="font-size: small;"><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">的实现。</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"></span></span></span></p>
<p class="MsoListParagraph" style="margin: 0cm 0cm 0pt 42pt; mso-char-indent-count: 0;"><span style="font-size: small;"><span style="color: #000000;"><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-family: Calibri;">Memcached</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">作为集中式</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-family: Calibri;">Cache</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">，就存在着集中式的致命问题：单点问题，</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-family: Calibri;">Memcached</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">支持多</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-family: Calibri;">Instance</span></span><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">分布在多台机器上，仅仅只是解决了数据全部丢失的问题，但是当其中一台机器出错以后，还是会导致部分数据的丢失，一个篮子掉在地上还是会把部分的鸡蛋打破。</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"></span></span></span></p>
<p class="MsoListParagraph" style="margin: 0cm 0cm 0pt 42pt; mso-char-indent-count: 0;"><span style="font-size: small;"><span style="color: #000000;"><span style="font-family: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-size: 10.5pt;">因此就需要实现一个备份机制，能够保证</span><span lang="EN-US" style="mso-bidi-font-size: 10.5pt;"><span style="font-family: Calibri;">Memcached</span></span><span style="font-family: