本篇文章让我们来详细探讨一下Struts2的配置文件的结构、配置文件的各个节点和每个节点中元素的使用方式。
总揽
Struts2的配置文件是以XML的形式出现的。不过它的XML的语义比较简单,下面是我抽取了位于struts2-core-2.0.14.jar内部的struts-default.xml的片段:
在这个配置文件中,我们可以看到,Struts2的XML自身所支持的节点和子节点并不是很多,大致来说,这些节点可以分成 基本配置定义和 Runtime配置定义。
基本配置定义
基本配置定义,主要是针对在Struts2内部所使用的各种元素的声明。这些声明往往规定了Struts2内部的一些行为特征。
例如,配置文件中的节点,被用于定义Struts2中所使用的接口和实现类,通过Struts2内部实现的IoC,你就可以在不同的实现类之间进行切换。
再例如,配置文件中的节点和节点。他们用于定义Struts2中所支持的所有的Result类型和拦截器,这些定义和声明,将在Runtime的配置定义中被引用。
我之所以把配置文件中的这些节点单独列出来,作为一个种类,是因为 这些节点是不可省略的,也是无法简化的。所以,如果我们试图在Struts2中简化配置,我们就需要在Runtime配置定义中下功夫,而这些基本配置定义,我们可以认为是Runtime配置定义的基础。
Runtime配置定义
Runtime配置定义,主要指的的是对Struts2运行过程中,具体的某个Action的行为的指定。这些指定主要通过节点中的节点来完成。
仔细翻阅节点,我们可以发现,它是URL与Action之间沟通的桥梁,也就是说,它定义了URL与Action之间的对应关系。同时,它还指定了Action在执行过程中的具体行为,包括Action执行的时候使用什么样的拦截器、Action执行完毕后,转向到什么样的Result等等。
Runtime配置定义是可以简化的,Struts2中提供了很多种简化配置的方式,这个在之后的文章中会详细提到。
模块化管理配置文件
一旦项目变得很大,项目中同时也并不采取什么简化配置的措施,那么在默认情况下,配置文件就会变得很大而不易于维护。这个时候,对于配置文件的模块化管理的需求就显现出来。Struts2提供了两种方式对配置文件进行模块化管理。
plugin机制
Struts2有plugin的机制,有关plugin的具体的知识,请参考我的另外一篇专栏文章:《深入plugin》 ——
http://www.javaeye.com/wiki/struts2/1333-deep-into-plugin。在这里,我也就不详细介绍了。
在每个plugin中,都会有一个叫做struts-plugin.xml的配置文件,这个配置文件的格式与struts-default.xml的格式是相同的。可以在其中做出任何的Struts2的定义和配置。我们知道,Struts2的配置文件的加载顺序,是按照以下的顺序来:
Struts2 Referece 写道1. struts-default.xml (bundled in the Core JAR)
2. struts-plugin.xml (as many as can be found in other JARs)
3. struts.xml (provided by your application)
所以,struts-plugin.xml中的配置的效果实际上与struts-default.xml的效果是相同的。这样,通过各种各样不同的plugin,就等于将Struts2的配置,按照plugin的功能不同而分开了。从而起到了配置文件模块化管理的效果。
使用include节点
plugin中的配置文件,实际上是位于classpath的JAR包中的,那么我们在项目中,如何对一个庞大的配置文件进行拆分呢?在Struts2中,可以使用include节点对所有的Struts2配置文件进行拆分和模块化管理。例如:
其中,file所指定的文件是相对于classpath的相对目录中的文件。而每个配置文件的格式与struts-default.xml的格式也是相同的。
通过include节点,我们就可以对一个比较大的配置文件按照功能和模块进行拆分,这在一个大型的团队开发中,是相当有意义的。简单的IoC
在基本配置定义中,有两个很常用的节点:和。在系统启动的时候,Struts2会根据配置文件中这些和节点的定义进行加载,并初始化成为Struts2的默认行为。这种初始化的行为,非常类似于Spring中的依赖注入(IoC),从而使得你不再需要担心这些对象在运行时的创建和销毁,所有的工作都由Struts2内部的机制实现。接下来我们就来看看Struts2是如何实现IoC的。
Struts2 Reference 写道Internally, the framework uses its own dependency injection container that is very similar to Google Guice (both were originally developed by Bob Lee)
这是来自于Struts2的Reference对它自身的IoC的描述。如果熟悉Guice的朋友一定知道,Guice的实现使用了Annotation的方式进行,而整个依赖注入的实现,是通过一个内部的容器类进行的。Struts2的依赖注入,与Guice的机制完全一致。根据注入的内容的不同,Struts2的IoC可以对容器中的对象的依赖关系进行管理,也可以注入一些静态变量。
bean注入
对于bean的注入,对应于XML中的bean的节点声明。我把其中的机制分成了3个部分:
1. 容器中对象的声明
这点没什么好说的,在struts.xml中,你可以为某个接口声明它所对应的实现类。
name属性
你可以声明多个实现类,使用name属性进行区分。在注入的时候,将使用这个属性的值作为接口实现类的选择。
required属性
你还可以通过required属性,来指定是否在运行时必不可少的注入。如果reqired被设置成false,那么当不存在相应的接口定义时,注入将被忽略。
static属性
在XML的定义中,还可以使用static属性。如果static属性被设置成true,那么注入将针对bean中的static方法和static属性进行。
2. 在代码中使用Annotation进行注入
在代码中,使用@Inject这样一个Annotation进行对象依赖注入。在上面的例子中,我们可以看到, @Inject这个Annotation,可以作用在属性上,也可以作用在方法上,甚至可以作用在方法的参数上。
在默认情况下,如果@Inject不指定value,那么XML配置定义中的 name="default"或者 name=""的实现类定义将被注入。
那么,在struts-default.xml中,Struts2到底选择了那些实现类,作为Struts2或者XWork内部接口的默认实现类呢?默认情况下,struts-default.xml中定义的bean的 name="struts"的将被作为默认的接口实现类被注入。这些默认行为,是由org.apache.struts2.config.BeanSelectionProvider所决定的,有兴趣的读者可以参阅这个类的源码。
3. 内部的Container机制完成一切背后工作
上面看到的,是现象。在内部,Struts2通过一个Container来实现所有的注入机制。
目 录 [ - ]
总揽 
Struts2的配置文件是以XML的形式出现的。不过它的XML的语义比较简单,下面是我抽取了位于struts2-core-2.0.14.jar内部的struts-default.xml的片段:
dojo\..*
input,back,cancel,browse
input,back,cancel,browse
在这个配置文件中,我们可以看到,Struts2的XML自身所支持的节点和子节点并不是很多,大致来说,这些节点可以分成 基本配置定义和 Runtime配置定义。
基本配置定义
基本配置定义,主要是针对在Struts2内部所使用的各种元素的声明。这些声明往往规定了Struts2内部的一些行为特征。
例如,配置文件中的节点,被用于定义Struts2中所使用的接口和实现类,通过Struts2内部实现的IoC,你就可以在不同的实现类之间进行切换。
再例如,配置文件中的节点和节点。他们用于定义Struts2中所支持的所有的Result类型和拦截器,这些定义和声明,将在Runtime的配置定义中被引用。
我之所以把配置文件中的这些节点单独列出来,作为一个种类,是因为 这些节点是不可省略的,也是无法简化的。所以,如果我们试图在Struts2中简化配置,我们就需要在Runtime配置定义中下功夫,而这些基本配置定义,我们可以认为是Runtime配置定义的基础。
Runtime配置定义
Runtime配置定义,主要指的的是对Struts2运行过程中,具体的某个Action的行为的指定。这些指定主要通过节点中的节点来完成。
仔细翻阅节点,我们可以发现,它是URL与Action之间沟通的桥梁,也就是说,它定义了URL与Action之间的对应关系。同时,它还指定了Action在执行过程中的具体行为,包括Action执行的时候使用什么样的拦截器、Action执行完毕后,转向到什么样的Result等等。
Runtime配置定义是可以简化的,Struts2中提供了很多种简化配置的方式,这个在之后的文章中会详细提到。
模块化管理配置文件 
一旦项目变得很大,项目中同时也并不采取什么简化配置的措施,那么在默认情况下,配置文件就会变得很大而不易于维护。这个时候,对于配置文件的模块化管理的需求就显现出来。Struts2提供了两种方式对配置文件进行模块化管理。
plugin机制
Struts2有plugin的机制,有关plugin的具体的知识,请参考我的另外一篇专栏文章:《深入plugin》 ——
在每个plugin中,都会有一个叫做struts-plugin.xml的配置文件,这个配置文件的格式与struts-default.xml的格式是相同的。可以在其中做出任何的Struts2的定义和配置。我们知道,Struts2的配置文件的加载顺序,是按照以下的顺序来:
Struts2 Referece 写道1. struts-default.xml (bundled in the Core JAR)
2. struts-plugin.xml (as many as can be found in other JARs)
3. struts.xml (provided by your application)
所以,struts-plugin.xml中的配置的效果实际上与struts-default.xml的效果是相同的。这样,通过各种各样不同的plugin,就等于将Struts2的配置,按照plugin的功能不同而分开了。从而起到了配置文件模块化管理的效果。
使用include节点
plugin中的配置文件,实际上是位于classpath的JAR包中的,那么我们在项目中,如何对一个庞大的配置文件进行拆分呢?在Struts2中,可以使用include节点对所有的Struts2配置文件进行拆分和模块化管理。例如:
其中,file所指定的文件是相对于classpath的相对目录中的文件。而每个配置文件的格式与struts-default.xml的格式也是相同的。
通过include节点,我们就可以对一个比较大的配置文件按照功能和模块进行拆分,这在一个大型的团队开发中,是相当有意义的。
简单的IoC 
在基本配置定义中,有两个很常用的节点:和。在系统启动的时候,Struts2会根据配置文件中这些和节点的定义进行加载,并初始化成为Struts2的默认行为。这种初始化的行为,非常类似于Spring中的依赖注入(IoC),从而使得你不再需要担心这些对象在运行时的创建和销毁,所有的工作都由Struts2内部的机制实现。接下来我们就来看看Struts2是如何实现IoC的。
Struts2 Reference 写道Internally, the framework uses its own dependency injection container that is very similar to Google Guice (both were originally developed by Bob Lee)
这是来自于Struts2的Reference对它自身的IoC的描述。如果熟悉Guice的朋友一定知道,Guice的实现使用了Annotation的方式进行,而整个依赖注入的实现,是通过一个内部的容器类进行的。Struts2的依赖注入,与Guice的机制完全一致。根据注入的内容的不同,Struts2的IoC可以对容器中的对象的依赖关系进行管理,也可以注入一些静态变量。
bean注入
对于bean的注入,对应于XML中的bean的节点声明。我把其中的机制分成了3个部分:
1. 容器中对象的声明
这点没什么好说的,在struts.xml中,你可以为某个接口声明它所对应的实现类。
name属性
你可以声明多个实现类,使用name属性进行区分。在注入的时候,将使用这个属性的值作为接口实现类的选择。
required属性
你还可以通过required属性,来指定是否在运行时必不可少的注入。如果reqired被设置成false,那么当不存在相应的接口定义时,注入将被忽略。
static属性
在XML的定义中,还可以使用static属性。如果static属性被设置成true,那么注入将针对bean中的static方法和static属性进行。
2. 在代码中使用Annotation进行注入
@Inject("xwork") protected ObjectFactory objectFactory; public LightURLUnknownHandler(@Inject ObjectFactory objectFactory) { this.objectFactory = objectFactory; } @Inject public void setObjectFactory(ObjectFactory factory) { this.objectFactory = factory; } @Inject(required=false) public void setUnknownHandler(UnknownHandler handler) { this.unknownHandler = handler; }
在代码中,使用@Inject这样一个Annotation进行对象依赖注入。在上面的例子中,我们可以看到, @Inject这个Annotation,可以作用在属性上,也可以作用在方法上,甚至可以作用在方法的参数上。
在默认情况下,如果@Inject不指定value,那么XML配置定义中的 name="default"或者 name=""的实现类定义将被注入。
那么,在struts-default.xml中,Struts2到底选择了那些实现类,作为Struts2或者XWork内部接口的默认实现类呢?默认情况下,struts-default.xml中定义的bean的 name="struts"的将被作为默认的接口实现类被注入。这些默认行为,是由org.apache.struts2.config.BeanSelectionProvider所决定的,有兴趣的读者可以参阅这个类的源码。
3. 内部的Container机制完成一切背后工作
上面看到的,是现象。在内部,Struts2通过一个Container来实现所有的注入机制。
public interface Container extends Serializable { String DEFAULT_NAME = "default"; void inject(Object o); T inject(Class implementation); T getInstance(Class type, String name); T getInstance(Class type); Set getInstanceNames(Class