在Mac下启动多个Firefox实例方便JsUnit运行

项目中的JsUnit是使用ant脚本运行的,里面需要设置BROWSER_PATH的环境变量来启动浏览器。在本地check in代码的时候,我们会运行一下测试来减少愚蠢错误被提交到代码控制系统。但是在我的mac下Firefox只能启动一个实例,在运行JsUnit test的时候会提醒我已经打开了Firefox,不能打开另外一个实例,这样我必须关闭正在运行的Firefox。而且由于我比较喜欢打开非常多的Tabs来保持浏览状态,所以关闭Firefox让我很不爽,再说,因为重新启动的Firefox里面带了很多的Tabs,所以经常造成实际运行的JsUnit test发生随机性的超时错误,这个就不能容忍了,因为这无法保证我们的信心。

那么,为什么FF不能启动多个实例呢?原因是它们共享同一个Firefox的profile,所以没法多个实例并发访问。但是通过命令行参数是可以创建多个profile给firefox的,简单了。不过遇到的问题是JsUnit的ant任务会检测BROWSER_PATH是否存在,所以如果我把带参数的命令行写到环境变量里面Ant无法检测到这个文件就会报错。那么如果关闭检测可以么?还是不行。因为JsUnit的StandaloneTest里面实际最后会调用DefaultProcessStarter的execute方法,这个方法调用Runtime.getRuntime().exec(command),这个实现非常直接,不过因为parameters如果直接写到命令行里会发生文件无法找到的问题(应该用数组将命令和参数传入)所以没有办法传入,还是无法运行。

放弃hack吧,我可以修改Ant task和JsUnit的方法,但是绝对不好,因为这个hack没有提交回去的意义。
所以换个思路,这样做:我们去写个shell来解决它。

先在终端运行/Applications/Firefox.app/Contents/MacOS/firefox-bin -CreateProfile jsunit,这时候会弹出窗口让你确认创建这个profile,选择一下不使用extensions和各种工具条,这样减少这些设置对测试的不良影响。
然后在你的home目录创建一个firefox.sh,里面写上:

/Applications/Firefox.app/Contents/MacOS/firefox-bin -P jsunit $1

前提是你的Mac使用的是默认的bash,否则修改$1为对应的引用字符。然后chmod firefox.sh 555,让它可以运行。

下面就是修改你的~/.profile:

export BROWSER_PATH=/Users/[User path]/firefox.sh

source ~/.profile让修改生效再运行JsUnit就OK啦。如法炮制想开几个Firefox实例都可以啦。同样方法也适用于让Firefox2和Firefox3共同运行!非常简单。还可以做到开发和浏览分开……以此类推。

回顾一下JsUnit的代码写的不好,如果像Selenium一样能够自动创建一个profile就好了,因为那样可以减少测试之间的影响,还可以让Selenium并行执行。我想,如果有空我可以做一下这个工作:D

Yahoo的“前端优化突破” slide共享

这是一篇很好的讲前端页面优化的slide,当然有不少内容我们都看过很多遍了,我以前做过的:

里面的内容也有一些重复。不过这次我还是很有收获,比如IBM的Page detailer就是个不错的profiler工具。还有使用document.getElementsByTagName(‘*’).length简单判断dom复杂度的方法也是不错的。结果是:

  • 新浪首页是3109
  • 财经首页是4260
  • 新浪邮箱是169
  • 纸条箱251
  • sohu是2k4
  • netease是1k4

很有意思。

结绳记事。

写javascript单元测试是挺爽的事,可惜不要在safari上?

对于javascript来说,通过单元测试,你也可以实现TDD。对你非常有好处,一是减少了js变动带来的代码退化问题,另外一方面是TDD可以改变你设计程序的方式。
举个简单的例子,写javascript很多情况下是和BOM(也就是文档模型)和DOM打交道的,这样可以说javascript程序很容易与dom高度耦合,这样的程序运行起来没有问题,但是应对需求变化的能力会比较地。但是根据人的思维方式,javascript很多时候是先出页面,然后根据页面逐渐调试着写出javascript,对于开发者来说,脑子里并不是真正的清楚自己要什么,而是在想界面的结果……这样产生的高耦合代码不容易测试,也难以面对多变的界面。
所以,换一种方式思考。如果用单元测试的方式去写,你就需要考虑程序的可测试性,这会细化你的程序的模块粒度,因为细粒度的抽象容易单元测试。同时由于js单元测试的页面是mock出来的,所以一般都会尽量的简单,这样会减少程序远对界面的依赖。同时由于界面的可测是性问题,也许会减少对element的style的修改,转而使用语义话的css。例如如果一个元素高亮,你可以element.style[‘font-size’]=’bold’;element.style.color=’red’l….,当然你也可以element.addClassName(‘highlight’),然后那些不好验证的界面的约束条件可以放到css里面去,放给可用性和用户验收测试去验证。这样的单元测试的验证条件(assertion)会简单很多,如果写过单元测试的朋友肯定会有感触的。
那么,常见的Javascript的单元测试框架有JsUnit和scrit.aculo.us的单元测试框架两个。前者方便用ant调用和分析结果,适合使用了ant的项目。而后者的优点就是界面好看,直接运行产生的报告清楚漂亮。所以小型项目我倾向后者,而大型项目我倾向前者。当然,由于js的动态特性,其实做个单元测试框架非常简单,所以自己动手也无妨。关键是要写,而且争取做到测试先行。

前面是个引子,其实写这个的原因是今天上午的一个郁闷的事。
前面的blog entry说道我升级了Firefox3,结果遇到了getElementsByClassName问题。但是今天换到另外一个项目组,没有用那个方法,程序也正常。可是我TDD的写一个新的feature的时候却发现可爱的JsUnit的testRunner在Firefox3里面无法工作,从firebug里面看到了一堆安全性问题。估计是firefox3的新安全模型造成的吧。那么,由于firefox3覆盖了firefox2,所以难道我没法写程序了?当然不可以,还有safari嘛。马上开始去写测试了,写好测试执行测试,发现红条。嗯,很满意,因为TDD的红-绿-红-绿的节奏就是这样的。然后我开始去写实现来满足这个测试……结果忙活了一上午就是不行……而且发现一些原来的测试也无法通过了……我仔细寻找问题,diff修改的内容,可是最后实在没有发现任何让它不能通过的原因,因为手工在firebug里面都已经验证了写的实现是没有问题的呀……崩溃。此时我突然想起来我们的持续集成服务器里面没有跑safari的JsUnit测试……也就是说不能确定在safari下全绿(此时的背景是我们的持续集成显示全部绿色,也就是说所有的测试都可以同过,包括windows和linux平台还有IE及Firefox),那么我可能衰了。马上开动camino(靠,一上午都忘记用它执行JsUnit了,因为我的习惯是Camino里面保存to read list),运行一下全绿。很兴奋,但是感觉刚才寻找问题的1个多小时被无辜的浪费了,心疼呀。

那么,请注意啦,我只是想提醒,jsUnit可能不能在safari下正常工作(大部分测试没有问题,少量在其它浏览器正常的测试在safari下无法工作),我用的是safari3.0.4……

Firefox3 beta2带来的getElementsByClassName问题

Firefox3解决了一些内存泄露问题,或者说主要的是解决了一些长时间运行以后的内存占用问题。由于使用mac的习惯是休眠不关机,所还是在正式版出来之前选择了升级到firefox beta2。
那么作为开发人员肯定遇到firebug不能用的问题,还好,我们可以用firebug的beta版本来解决这个问题,当然这个firebug还是会出现莫名其妙的问题,无所谓啦,不久就会发布正式版本了。
这些都是废话,问题集中在firefox beta2提供了原生的getElementsByClassName方法,而我们大家都很愿意使用的prototype库正巧也给Element封装过getElementsByClassName方法,看起来两者的作用是一样的,但是不巧……其实两者还是有很大差别的,所以造成了如果你的项目正巧用了getElementsByClassName方法,而后你又用习惯的prototype方式便利了它……那么你的程序就不能在Firefox3 beta2工作了……
我们以前的习惯就是不用考虑新浏览器的javascript兼容行问题,只需要考虑向后兼容,但是这次问题还是出现了变化。
其实原因是简单的:
1、Firefox3实现的原生getElementsByClassName方法返回的不是javascript数组(Array),而是html element collection,这个东西可以用标准的方式遍历,但是却与Array没有共同的prototype(这个指javascript里面的原型继承的prototype)。所以,很不幸,如果你用了Prototype库的那个返回Array的方法写程序,然后使用了Array增强方法里面的first()等方法或者Emmerable里面的each()等方法,那么程序一定会出错。此时你可以$A一下这个html element collection,但是这不是好方法。因为其实Prototype在1.6以后就不推荐使用getElementsByClassName方法了。
2、那么解决的方法就是使用select方法代替原来的getElementsByClassName方法,不过记得要给参数的前面加个”.”。比如oneElement.getElementsByClassName(‘someClass’)应该修改为oneElement.select(‘.someClass’)。而其实select方法由于接受css selector语法,可以实现更强大的选择操作。

嗯,非常废话。
那么简单的说:如果你在Firefox3里面发现你的基于Prototype库的js程序的getElementsByClassName以后出现了method null的错误,那么你需要用select方法代替它。且考虑到长时间的不兼容状态,最好完全消除你的程序中的getElementsByClassName同名方法调用,不关你用什么js库。

Pragmatic Programmer的穿着

来到ThoughtWorks以后有一个很有意思的发现,这里可能是我见到的穿赠品衣物最多的公司。赠品衣物就是公司为了宣传而印刷的T-shirt或者大衣,质量一般,上面还有鲜艳的商标或者口号,所以一般大家收到后都是不穿的。可是在一家重视实践的咨询公司里面却有这样有趣的现象,我们来思考一下。

1、好的程序员可能会有一些Geek的特性,他们愿意搞清楚事物运行的方式,但是却忽略了自己。他们的精力愿意用来学习和娱乐,而外貌问题,可能不是非常重要。除非是去找伴侣的时候……所以他们的时间用来上网,写作,玩游戏,但是不会用来逛街买衣服。

2、程序员的实用价值观。从敏捷逻辑上来说要消除浪费,减少预先的需求,而外貌本身不是直接收益的,而是潜在的影响,所以花很多钱在衣服上会被认为是一种需求浪费,因为你过度要求了(类比于过度设计)。所以要在非要不可的最后一刻再解决它。可能程序员在参加重要活动的时候可以穿西服,但是哪一定不是程序员聚会,而是……所以,可以从节省钱的角度来说,赠品是成本最小话却可以满足基本要求的选择。

3、程序员相信自己,胜过别人。所以程序员更以来于自己的工具,电脑和网络……那么减少了砍价这个非常麻烦的人与人沟通的方式,如果我们可以尽量的利用身边的资源,那么……所以你看到参加各种活动的时候程序员真的会非常在意厂商发放的服装……

4、ThoughtWorkers很多都自豪自己的公司和产品,所以他们愿意穿公司产品的宣传服装。比如背包……我看的男女ThoughtWorkers几乎都背的……虽然这样的商标在大街上被认出来的可能是非常小的,但是大家还是愿意背它,原因可能是一种发自内心的认可。

小处见大。我想第四条是非常有意义的,你愿意背出你的IT公司的包么?你的公司值得你自豪么?你是个不在乎着装的程序员么?我们应该不停思考。

非常好的职业建议,来自Scott Adams

非常好的职业建议,来自Scott Adams
从一个非常喜欢的人的blog看到的,这个人是Tomcat的作者,现在是Apple的Object C开发者,兼重要的RoR作者,兼资深摄影师,兼Mac大fans,他叫James Duncan Davidson(http://duncandavidson.com/)。
在一篇Blog讲到,Scott Adams说:
Everyone has at least a few areas in which they could be in the top 25% with some effort. In my case, I can draw better than most people, but I’m hardly an artist. And I’m not any funnier than the average standup comedian who never makes it big, but I’m funnier than most people. The magic is that few people can draw well and write jokes. It’s the combination of the two that makes what I do so rare. And when you add in my business background, suddenly I had a topic that few cartoonists could hope to understand without living it.
翻译一下就是说:
每个人都可以找到通过一些努力就可以跻身前25%的行业。例如我,我可以比大多数人画的好,但是我还够不上艺术家的水平。我也没有达到喜剧演员那种好笑的水平,但是我比大多数人好笑。魔法在于没什么人即画得好又会写笑话。所以混合一下我就很稀有了。加上我的商业知识背景,我突然发现我比其它的动画人更能理解它(商务)。

思考一下,其实就是当下流行的mush up。你混合一下你的长处,也许你就找到了你真正的长处,你的职业。
同时James又推荐了Stay Hungry,Stay Foolish,他说他37岁了依然觉得那是篇很好的文章。我很早之前也推荐过这篇文章,大家继续看看。

Fix firefox’s slow problem under Beryl

I’m using Ubuntu 7.04 @ my laptop. And I enable Beryl yesterday, and configure it as MacOSX’s appearance.

But the firefox works slower than before! I must wait for the firefox’s screen turn from gray to white, it waste my time… I hate it!

But after I saw a thread, I solve the problem.

Add this line to your /etc/bash.bashrc:
export MOZ_DISABLE_PANGO=1

and restart the firefox. It’s working faster, like I install Beryl before.

May it help. Good luck!

习惯改变,习惯新的习惯

一个朋友来信问候我,得知他状况不好,他问我如何解脱。而我的朋友D先生前不久也问我和她女朋友之间那种难舍的爱情,和由此所产生出的想要上进的希望,他也问我如何做。其实我也不是很懂,只是最近两年感觉自己还是上进的,可能和zz对我的促进有关吧。我把这个通用的答案总结一下,写下来,以后随时修改,希望对朋友们有用。
这里的主题就是养成一个改变的习惯,不停的改进自己的习惯,并习惯这种新的习惯,这种改善会越来越明显。套用我很喜欢的一个广告语,来自Benz奔驰的广告:“前所未有,因为之前所有!”
的确,你的进步会是前所未有的,而这正是因为你不断改变的习惯的积累。
A、要每天坚持跑步,早晚。
B、学会使用聚合器(或者说RSS阅读器)订阅并阅读技术Blog。
C、申请一个Blog,并将自己的收获记录并分享。
D、使用一个事项安排软件管理自己面临的事情和它们的优先级。
E、花钱和时间购买和阅读好的书籍。
F、花些时间做你爱做的事情,比如游戏。
G、吃复合维生素,早上喝高乐高,每天喝3大杯水。
H、多和朋友家人沟通,了解发展沟通感情。
I、旅行,去想去的地方。不一定是远的地方,博物馆商场或者饕餮之处都是好的选择。
J、维持你的爱好,如果没有请选择一个爱好。爱好会带来一些狂热,它是生活的催化剂。
K、找一个爱人,好好照顾她/他,一起奋斗。
L、定期思考和总结目前的状况,做新的计划。
还有什么?脑子不好使了……

Webwork 2.2的Action是否使用Spring的prototype­获取的性能对比

本文在060216进行了修改,因为发现了测试中的错误!注意5.5和7的内容。

1、引子:
其实是ajoo的这篇“Nuts和Spring 1.2.6 效率对比”和“IoC容器的prototype性能测试 ”,他们在Javaeye上详细讨论了Spring的prototype的缺陷。
Spring的prototype指的就是singleton="false"的bean,具体可以看Spring参考手册“3.2.5. To singleton or not to singleton”介绍。

2、Webwork 2.2的Spring结合问题:
Webwork 2.2已经抛弃自己的IoC,默认使用Spring的IoC。
上在OpenSymphony的官方Wiki,和jscud后来的几篇文章中没有特别提出prototype的问题。但是托他们的福,我们已经顺利的使Spring和Webwork良好的协同工作起来了。
可是而后的一些问题却把prototype的问题搞得神秘起来……
ajoo的测试中指出Spring的prototype性能很差,参见后面参考中的一篇文章和Javaeye的讨论。
而后又发现robbin在Javaeye的Wiki上面的“集成webwork和spring”中的最后注到:
“注意:目前并不推荐使用Spring来管理Webwork Action,因为对于prototype类型的bean来说,Spring创建bean和调用bean的效率是很低的!更进一步信息请看IoC容器的prototype性能测试”
这就使我们常用的Spring+Webwork2.2的连接中使用的prototype的问题被摆出来了。
我现在的项目中使用了prototype的方式将Webwork Action使用Spring进行显示的装配,我担心这个性能的问题会很严重,所以今天花了半天时间具体测试了一下。

3、Prototype VS autowire的解释:
我不知道怎么命名两种方式好,所以这里先做个解释:
spring的配置中Action会有个id,如:

<bean id="someAction" class="com.tin.action.SomeAction" parent="basicActionWithAuthtication" singleton="false">
<property name="someDAO">
<ref bean="someDAO" />
</property>
</bean>

我指的prototype方式就是在xwork中这样配置:

<action name="someAction" class="someAction">

而autowire方式就是指在xwork中这样配置:

<action name="someAction" class="com.tin.action.SomeAction">

看起来相同,但其实不同(我以前发过帖子,其中说这几种方法都可,但是其实它们的机制是不同的。

4、Portotye和autowire在XWork的SpringObjectFactory中是如何运作的:
我们先看一下代码,就能明白两者的区别了:

public Object buildBean(String beanName, Map extraContext) throws Exception {
try {
return appContext.getBean(beanName);
        }
catch (NoSuchBeanDefinitionException e) {
            Class beanClazz 
= getClassInstance(beanName);
return buildBean(beanClazz, extraContext);
        }

    }


public Object buildBean(Class clazz, Map extraContext) throws Exception {
        Object bean;

try {
            bean 
= autoWiringFactory.autowire(clazz, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false);
        }
catch (UnsatisfiedDependencyException e) {
// Fall back
            bean = super.buildBean(clazz, extraContext);
        }


        bean 
= autoWiringFactory.applyBeanPostProcessorsBeforeInitialization(bean, bean.getClass().getName());
// We don’t need to call the init-method since one won’t be registered.
        bean = autoWiringFactory.applyBeanPostProcessorsAfterInitialization(bean, bean.getClass().getName());

return autoWireBean(bean, autoWiringFactory);
    }


public Object autoWireBean(Object bean) {
return autoWireBean(bean, autoWiringFactory);
    }

如果按照autowire配置会使用第二个buildBean方法,而prototype会使用第一个buildBean方法。

5、我的测试,首先测试SpringObjectFactory的理论效率:

public class testSpringObjectFactory extends TestCase {
protected FileSystemXmlApplicationContext appContext;
protected SpringObjectFactory sof = null;
protected Map map = null;
final String[] paths = {
"WebRoot/WEB-INF/applicationContext.xml",
"WebRoot/WEB-INF/spring-daos.xml",
"WebRoot/WEB-INF/spring-actions.xml"
        }
;

protected void setUp() throws Exception {
super.setUp();
        appContext 
= new FileSystemXmlApplicationContext(paths);

        sof 
= new SpringObjectFactory();
        sof.setApplicationContext(appContext);
        sof.setAutowireStrategy(AutowireCapableBeanFactory.AUTOWIRE_BY_NAME);

        map 
= new HashMap();
    }


public void testSpringObjectFacotyWithAutowire() {
long begin = System.currentTimeMillis();

try {
for (int i = 0; i < 100000
; i++{
                sof.buildBean(
"com.wqh.action.XinfangNewsAction", map);
            }

        }
catch (Exception e) {
            e.printStackTrace();
        }


long end = System.currentTimeMillis();
        System.out.println(
"**************************Used time:" +
            (begin 
 end));
    }


public void testSpringObjectFacotyWithPrototype() {
long begin = System.currentTimeMillis();

try {
for (int i = 0; i < 100000; i++{
                sof.buildBean(
"xinfangNewsAction", map);
            }

        }
catch (Exception e) {
            e.printStackTrace();
        }


long end = System.currentTimeMillis();
        System.out.println(
"**************************Used time:" +
            (begin 
 end));
    }


public void testSpringObjectFacotyWithSpringProxyableObjectFactory() {
        sof 
= new SpringProxyableObjectFactory();
        sof.setApplicationContext(appContext);
        sof.setAutowireStrategy(AutowireCapableBeanFactory.AUTOWIRE_BY_NAME);

long begin = System.currentTimeMillis();

try {
for (int i = 0; i < 100000; i++{
                sof.buildBean(
"com.wqh.action.XinfangNewsAction", map);
            }

        }
catch (Exception e) {
            e.printStackTrace();
        }


long end = System.currentTimeMillis();
        System.out.println(
"**************************Used time:" +
            (begin 
 end));
    }

}

重要的是测试结果:
**************************Used time:-16875
**************************Used time:-80500
**************************Used time:-12703(使用SpringProxyableObjectFactory()这个实现)

prototype是autowire运行时间的4.77X倍,十分可观。

5.5 巨大的反差,原来是我搞错了配置,发现了幕后黑手:
第二天,我又重新运行了5里面的测试。但是结果令人吃惊,运行了十多次,结果于昨天反差巨大,prototype方式获得的bean反而性能最快!
摘要两次测量结果
**************************Autowire Used time:-17578
**************************Prototype Used time:-7609
**************************Proxy Used time:-13063
———————————————–
**************************Autowire Used time:-17047
**************************Prototype Used time:-7609
**************************Proxy Used time:-12797

这是为什么呢?我百思不得其解,问题出在哪里呢?后来经过跟踪svn里面的提交纪录。我发现,我在昨天测试以后,把spring配置文件中的<beans default-autowire="autodetect">变成了<beans>。也就是没有打开自动检测的autowire!
而后就真相大白了。我有配置上default-autowire="autodetect"进行测试,结果:
**************************Autowire Used time:-16937
**************************Prototype Used time:-79750
**************************Proxy Used time:-12578

这和昨天的测试结果完全相同。也就是说我昨天写的4.77x的结果其实没有实际意义。倒是说明了Spring和Webwork集成的文章上面说的default-autowire="autodetect"是很坏的实践,即失去了name的灵活性也带来了巨大的性能损失。
而如果使用默认的Spring autowire配置下,prototype的性能已经很好了,实际上它工作起来应该是最快的。

6、在实际的Web项目中的性能对比:
我使用了我的一个小项目,就是反复调用一个action获取一个页面,其中有一个DAO注入。使用了JMeter进行了一个测试:2个线程,间隔0.5秒,循环50次,对比“据和报告中的”Throughput,单位/sec。
使用autowire方式:Avg. 148.34(吞吐量越高越好)
使用prototype方式:Avg. 138.5

也就是说在实际应用中两者也是有性能差距的,后者大约是前者性能的93%。
具体代码我不放出了,因为意义不大,大家也可以自己动手试验一下。
补充说明:
首先注意这个测试是在default-autowire="autodetect"下进行的。
测试的这个Action其实是一个空Action,它没有调用service和DAO,只是直接return SUCCESS,然后dispatcher到一个静态内容的jsp页面。我的本意是为了能够在获取Action占据的时间比例比较高的情况下分析性能区别。但是实际上却间接的夸大了在真正的实际应用中的性能差距。实际应用中如果加上service、DAO等逻辑的执行时间、模板View的渲染时间还有广域网上的网络传输时间,那么获取Action实例的时间差距可能就微乎其微了。

7、后续:
经过今天的思考,可以说完全改变了想法,重新汇总一下:
a、在不使用default-autowire="autodetect"时,Webwork 2.2的xwork中的action class使用spring的bean id配置的理论性能最好。而且,我认为如果不是为了追求配置上的简单,严重推荐关闭spring的default-autowire。
b、在使用default-autowire="autodetect、name、class"时,需要考虑你的需求。如果不使用Spring AOP提供的功能则在Webwork 2.2的xwork中的action class使用class全名比较好。如果使用Spring AOP的功能,则还是使用bean id。
c、在Spring中是否使用default-autowire是个需要慎重考虑的问题。autowire如果打开,命名会受到限制(class则更不推荐,受限更大,参考相关文档),它所带来的配置简化我认为只算是小小的语法糖果,背后却是吃掉它所埋下的隐患。
d、6中的测试还是有些说明意义的。7%的性能差距是在使用了default-autowire的方式下得出的,其中测试的那个action其实没有执行什么逻辑,而是一个直接dispatcher到success view的action,如果有商业逻辑包装,则性能差据估计会更小。因为实际上Action的执行过程、service、DAO等逻辑的执行过程和模板View的渲染过程(网络延迟)才是耗时大户。所以,关于性能应该下的结论是,prototype与否,在实际应用中性能差距是很小的,基本可以忽略不计。我们讨论的更多是编码的更好的实践。
e、autowire不使用Spring AOP相对还是trade off,因为虽然配置简单一点,但是对于使用Spring的声明性事务等内容会带来麻烦。虽然XML不那么好,但是显示配置带来的好处还是很多的。
f、谢谢robbin的提示。关于事务我也是无奈,放弃Action事务后难道给DAO多封装一层事务?如何没有事务依然使用HibernateDAOSurpport?Acegi的确不适合Web,使用WW的Inteceptor可以实现更舒适的解决方案。
g、SpringProxyableObjectFactory的问题……使用上难道只能改代码?找了半天没有这个东西的介绍。看来还是需要看看代码。不过发现现在Webwork和Xwork的代码又变动了很多……
h、我的测试是在Webwork2.2+Spring 1.2.6环境下测试的

8、参考资源:
Nuts和Spring 1.2.6 效率对比
http://www.javaeye.com/pages/viewpage.action?pageId=786
IoC容器的prototype性能测试
http://forum.javaeye.com/viewtopic.php?t=17622&postdays=0&postorder=asc&start=0
JavaEye的Wiki:集成webwork和spring
http://www.javaeye.com/pages/viewpage.action?pageId=860
WebWork – Spring官方Wiki
http://www.opensymphony.com/webwork/wikidocs/Spring.html
webwork2 + spring 结合的几种方法的小结
http://forum.javaeye.com/viewtopic.php?t=9990
WebWork2.2中结合Spring:"新的方式"
http://www.blogjava.net/scud/archive/2005/09/21/13667.html
我为什么不推荐对Action进行事务控制
http://www.javaeye.com/pages/viewpage.action?pageId=1205
我为什么不推荐使用Acegi
http://www.javaeye.com/pages/viewpage.action?pageId=1199

关于存储过程和直接执行SQL对比

关于存储过程和直接执行SQL:
需要注意以下几点:
1、从数据库角度,存储过程总是要比它所对应的纯SQL要慢。
2、存储过程目的在于简化特别复杂的SQL复合应用的场景。
3、但是对于一个拥有多条SQL的存储过程来说,它可以提升效率。因为减少了SQL传输的网络延迟。所以说SQL复杂时,存储过程可以增加实际的运行效率。注意对比第1条,第一条是对应服务器调用,这条对应实际的网络环境。
4、还有就是存储过程很容易减少多条SQL之间数据传递的麻烦(有可能带来没有实际意义的中间变量),可以在服务器端把它们隐藏。
所以我想应该考虑这几点来选择存储过程:
1、拥有复杂的数据操作,需要SQL复合。
2、中间传递的临时数据过多或者过大的时候。
使用存储过程的Java代码:JDBC call Stored Procedure
CallableStatement cstmt = conn.prepareCall("{ ? = call md5( ? ) }");
// CallableStatement cstmt = conn.prepareCall("begin ? := md5( ? ); end;"); // oracle syntax
cstmt.registerOutParameter(1, Types.VARCHAR); // set out parameters
cstmt.setString(2, "idea"); // set in parameters
cstmt.execute();
String md5Str = cstmt.getString(1); // Getting OUT Parameter Values
cstmt.close();