该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2006-02-14
本文在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
prototype是autowire运行时间的4.77X倍,十分可观。
**************************Used time:-80500 **************************Used time:-12703(使用SpringProxyableObjectFactory()这个实现) 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(吞吐量越高越好)
也就是说在实际应用中两者也是有性能差距的,后者大约是前者性能的93%。
使用prototype方式:Avg. 138.5 具体代码我不放出了,因为意义不大,大家也可以自己动手试验一下。 补充说明: 首先注意这个测试是在default-autowire="autodetect"下进行的。 测试的这个Action其实是一个空Action,它没有调用service和DAO,只是直接return SUCCESS,然后dispatcher到一个静态内容的jsp页面。我的本意是为了能够在获取Action占据的时间比例比较高的情况下分析性能区别。但是实际上却间接的夸大了在真正的实际应用中的性能差距。实际应用中如果加上service、DAO等逻辑的执行时间、模板View的渲染时间还有广域网上的网络传输时间,那么获取Action实例的时间差距可能就微乎其微了。 7、后续: 引用 如果理想测试下两者性能差距4.7倍,实际性能差距在7%左右。这基本上让我心里有点底了。 但是使用autowire还是有trade off的。失去了Spring的显示配置,也难使用Spring的AOP支持,这样Spring的声明性事务就不容易使用了,像Acegi这样的安全框架也难以使用。所以这需要自己选择了。 但是注意5中结果里面的SpringProxyableObjectFactory性能非常好,我看了下代码,它将调用过的bean注册到Spring的BeanRegistry中,性能还是很不错的,但是为什么快我还没有仔细看,如何配置我也不太清楚,希望明白的朋友跟进讲解。 上面是我昨天的结论,非常粗糙,经过今天的思考,可以说完全改变了想法,重新汇总一下: 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.iteye.com/pages/viewpage.action?pageId=786 IoC容器的prototype性能测试 http://forum.iteye.com/viewtopic.php?t=17622&postdays=0&postorder=asc&start=0 JavaEye的Wiki:集成webwork和spring http://www.iteye.com/pages/viewpage.action?pageId=860 WebWork - Spring官方Wiki http://www.opensymphony.com/webwork/wikidocs/Spring.html webwork2 + spring 结合的几种方法的小结 http://forum.iteye.com/viewtopic.php?t=9990 WebWork2.2中结合Spring:"新的方式" http://www.blogjava.net/scud/archive/2005/09/21/13667.html 我为什么不推荐对Action进行事务控制 http://www.iteye.com/pages/viewpage.action?pageId=1205 我为什么不推荐使用Acegi http://www.iteye.com/pages/viewpage.action?pageId=1199 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2006-02-15
引用 如果理想测试下两者性能差距4.7倍,实际性能差距在7%左右。这基本上让我心里有点底了。
autowire方式Webwork Action是new出来的,spring方式是Spring框架创建的,你测试实际性能差距不大可能是因为整个系统运行的时候,主要的瓶颈不在action的创建上。 引用 但是使用autowire还是有trade off的。失去了Spring的显示配置,也难使用Spring的AOP支持,这样Spring的声明性事务就不容易使用了,像Acegi这样的安全框架也难以使用。所以这需要自己选择了。
http://www.iteye.com/pages/viewpage.action?pageId=1205 http://www.iteye.com/pages/viewpage.action?pageId=1199 为什么要用spring去配置webwork的action呢? |
|
返回顶楼 | |
发表时间:2006-02-15
robbin 写道 为什么要用spring去配置webwork的action呢? 使用spring配置webwork的action可以很方便的将action里面要用到的service直接注入进去。 就这一点就可以让我换成spring管理action了,不知道robbin现在是怎样在action里面获取service的呢? |
|
返回顶楼 | |
发表时间:2006-02-15
lingcm 写道 robbin 写道 为什么要用spring去配置webwork的action呢? 使用spring配置webwork的action可以很方便的将action里面要用到的service直接注入进去。 就这一点就可以让我换成spring管理action了,不知道robbin现在是怎样在action里面获取service的呢? 看来你还不知道有更简单的用法: 在webwork.properties中加入如下配置内容 webwork.objectFactory=spring 然后就OK了! Action需要什么Service,定义一个set方法,就自动拿到了。 |
|
返回顶楼 | |
发表时间:2006-02-15
robbin 写道 lingcm 写道 robbin 写道 为什么要用spring去配置webwork的action呢? 使用spring配置webwork的action可以很方便的将action里面要用到的service直接注入进去。 就这一点就可以让我换成spring管理action了,不知道robbin现在是怎样在action里面获取service的呢? 看来你还不知道有更简单的用法: 在webwork.properties中加入如下配置内容 然后就OK了! Action需要什么Service,定义一个set方法,就自动拿到了。 robbin的意思是设置了webwork.objectFactory=spring后,webwork的action不要交给spring 的 bean容器管理就可以拿到service了? 也就是所有的action还是直接在xwork.xml里面定义?还是我理解上面的偏差呢? 我现在的做法是: 1. webwork.properties里面设置 webwork.objectFactory=spring 2. 在spring配置文件里面定义好相关webwork的action的bean,记住bean的id号 3. 在webwork的xwork.xml里面定义action,不过action的class属性是直接填写spring里面定义号的bean的id。 是不是还有更简单的方法? |
|
返回顶楼 | |
发表时间:2006-02-15
引用 robbin的意思是设置了webwork.objectFactory=spring后,webwork的action不要交给spring 的 bean容器管理就可以拿到service了? 也就是所有的action还是直接在xwork.xml里面定义?还是我理解上面的偏差呢?
对的,不需要在spring里面做任何配置。action直接定义在xwork.xml里面照旧,只要加一句webwork.objectFactory=spring,Service就自己来啦~ |
|
返回顶楼 | |
发表时间:2006-02-15
太cool了,今天回去试一下,以前action里面每多使用一个service就得改一下配置文件,这样解决就太完美了。
|
|
返回顶楼 | |
发表时间:2006-02-15
这个是WebWork 2.2才有的吧?
|
|
返回顶楼 | |
发表时间:2006-02-15
不好意思,先看看robbin推荐的文章。
不过,我想问一下: 我使用了Spring的HibernateDaoSupport,如果不给bean配置一个transaction就会报Not able to obtain connection错误: http://forums.hibernate.org/viewtopic.php?t=945337&highlight=&sid=cff435dde0e8b0287c67ff436d894445 因为我们现在在做的Web项目没有service层,所以如果不把transaction配置到action,则只能配置到DAO了,这样似乎也没有意义。因为Hibernate已经有了transaction控制了。 而且,除了这两个地方,没有用到Spring AOP的地方了么? Acegi我倒是不在乎,因为Webwork有Inteceptor,方便作security。 |
|
返回顶楼 | |
发表时间:2006-02-15
hongliang 写道 这个是WebWork 2.2才有的吧?
恩,2.2才有的。2.2已经默认这个IoC了。 |
|
返回顶楼 | |