以下代碼基於struts2版本2.1.8.1版本分析。
在使用Struts2的過程中,我們都喜歡使用struts2的spring插件來讓spring作爲struts2的默認對象容器,原理就在於在加載struts2之前先加載spring容器,然後將spring容器加載至applicationContext中,在struts2的objectFactory(稱之爲對象容器)實現中,找到spring容器並進行各項對象創建工作。
在spring插件中,使用了StrutsSpringObjectFactory類來作爲struts2的對象容器,實現過程即除重寫objectFactory的各項buildBean方法以符合spring規範之外,其它則就是根據struts2的各項參數設置spring參數(如autoType等)。之所以使用此類作爲struts2的對象容器,原因就在於在struts2-spring.jar中的struts-plugin.xml中定義瞭如下的聲明:
1
2
3
|
< bean type = "com.opensymphony.xwork2.ObjectFactory" name = "spring" class = "org.apache.struts2.spring.StrutsSpringObjectFactory" /> < constant name = "struts.objectFactory" value = "spring" /> |
第一句是聲明瞭一個實現類objectFactory的對象,這就是spring插件所提供的對象容器,而第二句則聲明屬性struts.objectFactory的值爲spring,即key爲spring的對象將成爲struts2的對象容器。
爲什麼這樣說,我們可以看一下struts2對struts.objectFactory的解釋,以下解釋摘自struts2中的default.properties:
1
2
3
|
###
if specified, the default object factory can be overridden here ###
Note: short-hand notation is supported in some cases, such as "spring" ###
Alternatively, you can provide a com.opensymphony.xwork2.ObjectFactory subclass name here |
即只要設置了此值,則默認的對象容器,將被改寫,那麼spring插件所提供的功能就是定義了一個spring實現的對象容器,並且重寫了struts2定義,將spring容器再成爲struts2的對象容器。
再反過來,我們可以看到struts-default.xml中也定義瞭如下的容器聲明:
1
2
|
< bean class = "com.opensymphony.xwork2.ObjectFactory" name = "xwork" /> < bean type = "com.opensymphony.xwork2.ObjectFactory" name = "struts" class = "org.apache.struts2.impl.StrutsObjectFactory" /> |
這裏定義了兩個objectFactory實現,一個爲原生xwork中的objectFactory,另一個則爲struts所提供的strutsObjectFactory。其中xwork的key爲xwork,而struts的key爲struts。
我們知道,在struts2(實際上就是xwork),所確定最終的objectFactory的實現體是需要找一個類型爲ObjectFactory,key值爲default的實現類,而這2個xml定義中都沒有提到default這個key值,那麼就是在程序內部所實現了的。
首先看,struts2是如何尋找objectFactory實現的,類DefaultConfiguration中第181行至186行,如何上就是如何創建整個struts2容器,並確定對象容器的過程。如下代碼所示:
1
2
3
4
5
6
|
//
Set the bootstrap container for the purposes of factory creation Container
bootstrap = createBootstrapContainer(); setContext(bootstrap); container
= builder.create( false ); setContext(container); objectFactory
= container.getInstance(ObjectFactory. class ); |
上文中的builder.create(false)即是創建容器的過程,而container.getInstance(ObjectFactory.class)則是取得(創建)對象容器的過程。那麼實際上,在builder.create(false)過程中,實際的對象容器已經確定了,即key爲default,類型爲ObjectFactory的類已經注入到容器中了。那麼這個信息是如何注入的呢,這就歸結於builder(即ContainerBuilder)所提供的alias機制了。
什麼叫alias?熟悉spring的就知道,其實就是一個別名嘛,我們可以將一個id的aaBean的對象定義一個別名叫bbBean,那麼在struts2中,我們也可以將key爲spring的對象,定義一個別名叫default!
這就是程序內部實現的原理了,就是找到屬性名爲struts.objectFactory的屬性值,然後將此屬性值定義一個別名爲default,此後就將此對象作爲Struts2容器最終找到的對象了。
那麼終需要一個對象來調用builder的alias方法吧,此類就是BeanSelectionProvider,在初始化struts2容器時,此類被作爲containerProvider提供給containerBuilder,那麼在後面肯定會用到此類了。其實就是在初始化之前提供各項信息給builder,那麼它提供的信息是哪些呢,在方法public void register(ContainerBuilder builder, LocatableProperties props)中實現 如下:
1
2
3
|
alias(ObjectFactory. class ,
StrutsConstants.STRUTS_OBJECTFACTORY, builder, props); alias(XWorkConverter. class ,
StrutsConstants.STRUTS_XWORKCONVERTER, builder, props); alias(TextProvider. class ,
StrutsConstants.STRUTS_XWORKTEXTPROVIDER, builder, props, Scope.DEFAULT); |
我們關注的就是第一行,即這裏一個,其代碼爲:
1
|
alias(ObjectFactory. class ,
“struts.objectFactory”, builder, props); |
需要注意的是,這裏的alias和containerProvider所提供的alias還不一樣。containerProvider的alias是直接將給定的name定義爲別名,而此處的alias是指將給定的name所對應的值定義爲別名,相當於中間處了一次處理。爲什麼處理成這樣,其實就是一個擴展的過程。那麼我們來看一下相應的實現:
1
2
3
4
5
|
if (!builder.contains(type))
{ //相當於builder.contains(type,”default”) String
foundName = props.getProperty(key, DEFAULT_BEAN_NAME); //這裏的DEFAULT_BEAN_NAME爲struts if (builder.contains(type,
foundName)) { builder.alias(type,
foundName, Container.DEFAULT_NAME); …… |
首先尋找類型爲objectFactory,且key爲default的聲明,如前所示,肯定找不到的啦。那麼就在定義文件中尋找聲明爲struts.objectFactory的值,在這裏我們找到了其值爲spring,接下來就將key爲spring的定義加個別名爲default。在後面就找到了相應對象容器實現了。
需要注意的是,props.getProperty(key, DEFAULT_BEAN_NAME);此句意爲在沒有找到值的情況下,就使用默認值,而默認值就是struts,意思是需要不存在spring插件,則直接使用key爲struts所對象的對象,即StrutsObjectFactory來作爲對象容器了。