使用struts2的builder alias機制加載objectFactory

以下代碼基於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
<beantype="com.opensymphony.xwork2.ObjectFactory"name="spring"class="org.apache.struts2.spring.StrutsSpringObjectFactory"/>
 
    <constantname="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
<beanclass="com.opensymphony.xwork2.ObjectFactory"name="xwork"/>
<beantype="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來作爲對象容器了。

From:http://www.iflym.com/index.php/code/201204050001.html

發佈了26 篇原創文章 · 獲贊 71 · 訪問量 85萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章