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倍,十分可觀。
6、在實際的Web項目中的性能對比:
我使用了我的一個小項目,就是反復調用一個action獲取一個頁面,其中有一個DAO注入。使用了JMeter進行了一個測試:2個線程,間隔0.5秒,循環50次,對比“據和報告中的”Throughput,單位/sec。
使用autowire方式:Avg. 148.34(吞吐量越高越好)
使用prototype方式:Avg. 138.5
也就是說在實際應用中兩者也是有性能差距的,后者大約是前者性能的93%。
具體代碼我不放出了,因為意義不大,大家也可以自己動手試驗一下。
7、后續:
如果理想測試下兩者性能差距4.7倍,實際性能差距在7%左右。這基本上讓我心里有點底了。
但是使用autowire還是有trade off的。失去了Spring的顯示配置,也難使用Spring的AOP支持,這樣Spring的聲明性事務就不容易使用了,像Acegi這樣的安全框架也難以使用。所以這需要自己選擇了。
但
是注意5中結果里面的SpringProxyableObjectFactory性能非常好,我看了下代碼,它將調用過的bean注冊到Spring的
BeanRegistry中,性能還是很不錯的,但是為什么快我還沒有仔細看,如何配置我也不太清楚,希望明白的朋友跟進講解。
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://www.tkk7.com/scud/archive/2005/09/21/13667.html