使用struts2的builder alias机制加载objectFactory

来源:互联网 发布:vscode好看的字体 编辑:程序博客网 时间:2024/05/16 06:48

以下代码基于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

原创粉丝点击