首页 > 技术文章 > struts2配置细节

xdouby 2017-03-08 23:51 原文

一、前言

  最近想看看 struts2的源码,然后重新复习一下struts2的配置,觉得有些细节还是需要记录一下,所以有此一文~笔者对文章中内容都有亲测,使用的是:struts2-core-2.5.2.jar

二、正文

 2.1 首先,先说下struts2的核心Filter

  在早期的struts2开发中,在web.xml种配置的是:

<filter>
   <filter-name>struts2</filter-name>
   <filter-class>
     org.apache.struts2.dispatcher.FilterDispatcher
   </filter-class>
</filter>
 
<filter-mapping>
   <filter-name>struts2</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

  但是在2.1.3之后建议用:

<filter>
  <filter-name>struts2</filter-name>
  <filter-class>
        org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
  </filter-class>
</filter>
 
<filter-mapping>
   <filter-name>struts2</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

 2.2 修改struts.xml放置的位置

  默认情况下,struts.xml文件放置在src目录下(编译完成之后在xxx/WebRoot/WEB-INF/classes目录中),如果需要修改struts读取的配置文件位置,那么就需要如下配置:

  <filter>
      <filter-name>struts2</filter-name>
      <filter-class>
          org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter
      </filter-class>
      <init-param>
          <param-name>config</param-name>
          <param-value>struts-default.xml,struts-plugin.xml,../struts.xml</param-value>
      </init-param>    
  </filter>
  <filter-mapping>
      <filter-name>struts2</filter-name>
      <url-pattern>/*</url-pattern>
  </filter-mapping>

  这里面需要说明一下,因为设置了<param-name>config</param-name>参数,所以struts-default.xml,struts-plugin.xml等原来struts2默认加载的文件也需要手动指定,否则不会自动加载,如果不在这边指定,也可以在struts.xml里面加上:

<include file="struts-default.xml" />
<include file="struts-plugin.xml" />

  如果存在多个子配置文件,也可以这样引入:

<include file="[directory]/struts-*.xml" />
或直接
<include file="[directory]/*.xml" />

 2.3 package配置(原文:http://www.cnblogs.com/ningvsban/p/3734562.html

  <package>元素可以把逻辑上相关的一组Action、Result、Intercepter等元素封装起来,形成一个独立的模块,package可以继承其他的package,也可以作为父包被其他的package继承,比如“<package name="helloworld"  extends="struts-default">”中,helloworld这个包就继承了struts-default这个包。

  <package>元素有如下属性:

    • name:包的名称。必须配置
    • extends:要继承的包,后面配置的是被继承的包的名称。可选
    • namespace:包的命名空间。可选
    • abstract:定义包为抽象的,也就是不能包含Action的定义。可选

  下面重点讲解一下namespace和abstract属性:

  1)namespace属性

  namespace配置的是包的命名空间,同一个命名空间里面不能有同名的Action,当然不同的命名空间里面是可以有同名的Action的。类似于Java的包的功能,namespace可以有效的防止action重名的冲突,因为配置了namespace后,在访问action的时候就需要添加namespace来作为action的前缀。如果不配置namespace,表示是默认的namespace,那么访问的时候不需要添加namespace前缀。比如HelloWorld的示例,struts.xml的配置如下:

<?xml version="1.0" encoding="UTF-8" ?>  
<!DOCTYPE struts PUBLIC  
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"  
    "http://struts.apache.org/dtds/struts-2.0.dtd">  
  
<struts>  
    <constant name="struts.devMode" value="true" />  
    <constant name="struts.locale" value="zh_CN"/>  
    <constant name="struts.i18n.encoding" value="gb2312"/>  
  
    <package name="helloworld"  extends="struts-default">  
        <action name="helloworldAction" class="cn.javass.hello.struts2impl.action.HelloWorldAction">  
            <result name="toWelcome">/s2impl/welcome.jsp</result>  
        </action>  
</package>  
</struts> 

  观察里面<package>元素的定义,里面是没有配置namespace的,因此在访问的时候,是直接在webcontext下面写要访问的action的名称的,示例如下:

<form action="/helloworld/helloworldAction.action" method="post">  
    ……  
</form> 

  其中“/helloworld”是webcontext。

  如果配置了namespace,那么访问的时候是必须要添加namespace前缀的,配置namespace的时候“/”表示namespace的根。示例如下:

……省略了  
<package name="hello" namespace="/javass" extends="struts-default">  
        <action name="helloworldAction" class="cn.javass.hello.struts2impl.action.HelloWorldAction">  
            <result name="toWelcome">/s2impl/welcome.jsp</result>  
        </action>  
</package>  

  那么访问的时候就需要加上namespace,示例如下:

<form action="/helloworld/javass/helloworldAction.action" method="post">  
    ……  
</form>  

  2)abstract属性

  abstract用来定义包为抽象的,也就是不能包含Action的定义,但是抽象包可以被其他包继承,因此里面可以定义其他包需要的元素,比如ResultType、Interceptor等等。比如上面HelloWorld示例中继承的struts-default包,它就是个抽象的包,定义示例如下:

<package name="struts-default" abstract="true">
        <result-types>
            <result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>
            <result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/>
            <result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/>
            <result-type name="httpheader" class="org.apache.struts2.dispatcher.HttpHeaderResult"/>
            <result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/>
            <result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>
            <result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/>
            <result-type name="velocity" class="org.apache.struts2.dispatcher.VelocityResult"/>
            <result-type name="xslt" class="org.apache.struts2.views.xslt.XSLTResult"/>
            <result-type name="plainText" class="org.apache.struts2.dispatcher.PlainTextResult" />
            <result-type name="postback" class="org.apache.struts2.dispatcher.PostbackResult" />
        </result-types>

        <interceptors>
            <interceptor name="alias" class="com.opensymphony.xwork2.interceptor.AliasInterceptor"/>
            <interceptor name="autowiring" class="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor"/>
            <interceptor name="chain" class="com.opensymphony.xwork2.interceptor.ChainingInterceptor"/>
            <interceptor name="conversionError" class="org.apache.struts2.interceptor.StrutsConversionErrorInterceptor"/>
            <interceptor name="cookie" class="org.apache.struts2.interceptor.CookieInterceptor"/>
            <interceptor name="cookieProvider" class="org.apache.struts2.interceptor.CookieProviderInterceptor"/>
            <interceptor name="clearSession" class="org.apache.struts2.interceptor.ClearSessionInterceptor" />
            <interceptor name="createSession" class="org.apache.struts2.interceptor.CreateSessionInterceptor" />
            <interceptor name="debugging" class="org.apache.struts2.interceptor.debugging.DebuggingInterceptor" />
            <interceptor name="execAndWait" class="org.apache.struts2.interceptor.ExecuteAndWaitInterceptor"/>
            <interceptor name="exception" class="com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor"/>
            <interceptor name="fileUpload" class="org.apache.struts2.interceptor.FileUploadInterceptor"/>
            <interceptor name="i18n" class="com.opensymphony.xwork2.interceptor.I18nInterceptor"/>
            <interceptor name="logger" class="com.opensymphony.xwork2.interceptor.LoggingInterceptor"/>
            <interceptor name="modelDriven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/>
            <interceptor name="scopedModelDriven" class="com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor"/>
            <interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>
            <interceptor name="actionMappingParams" class="org.apache.struts2.interceptor.ActionMappingParametersInteceptor"/>
            <interceptor name="prepare" class="com.opensymphony.xwork2.interceptor.PrepareInterceptor"/>
            <interceptor name="staticParams" class="com.opensymphony.xwork2.interceptor.StaticParametersInterceptor"/>
            <interceptor name="scope" class="org.apache.struts2.interceptor.ScopeInterceptor"/>
            <interceptor name="servletConfig" class="org.apache.struts2.interceptor.ServletConfigInterceptor"/>
            <interceptor name="timer" class="com.opensymphony.xwork2.interceptor.TimerInterceptor"/>
            <interceptor name="token" class="org.apache.struts2.interceptor.TokenInterceptor"/>
            <interceptor name="tokenSession" class="org.apache.struts2.interceptor.TokenSessionStoreInterceptor"/>
            <interceptor name="validation" class="org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor"/>
            <interceptor name="workflow" class="com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor"/>
            <interceptor name="store" class="org.apache.struts2.interceptor.MessageStoreInterceptor" />
            <interceptor name="checkbox" class="org.apache.struts2.interceptor.CheckboxInterceptor" />
            <interceptor name="profiling" class="org.apache.struts2.interceptor.ProfilingActivationInterceptor" />
            <interceptor name="roles" class="org.apache.struts2.interceptor.RolesInterceptor" />
            <interceptor name="annotationWorkflow" class="com.opensymphony.xwork2.interceptor.annotations.AnnotationWorkflowInterceptor" />
            <interceptor name="multiselect" class="org.apache.struts2.interceptor.MultiselectInterceptor" />
            <interceptor name="deprecation" class="org.apache.struts2.interceptor.DeprecationInterceptor" />

            <!-- Basic stack -->
            <interceptor-stack name="basicStack">
                <interceptor-ref name="exception"/>
                <interceptor-ref name="servletConfig"/>
                <interceptor-ref name="prepare"/>
                <interceptor-ref name="checkbox"/>
                <interceptor-ref name="multiselect"/>
                <interceptor-ref name="actionMappingParams"/>
                <interceptor-ref name="params">
                    <param name="excludeParams">^class\..*,^dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,^parameters\..*,^action:.*,^method:.*</param>
                </interceptor-ref>
                <interceptor-ref name="conversionError"/>
                <interceptor-ref name="deprecation"/>
            </interceptor-stack>

            <!-- Sample validation and workflow stack -->
            <interceptor-stack name="validationWorkflowStack">
                <interceptor-ref name="basicStack"/>
                <interceptor-ref name="validation"/>
                <interceptor-ref name="workflow"/>
            </interceptor-stack>

            <!-- Sample file upload stack -->
            <interceptor-stack name="fileUploadStack">
                <interceptor-ref name="fileUpload"/>
                <interceptor-ref name="basicStack"/>
            </interceptor-stack>

            <!-- Sample model-driven stack  -->
            <interceptor-stack name="modelDrivenStack">
                <interceptor-ref name="modelDriven"/>
                <interceptor-ref name="basicStack"/>
            </interceptor-stack>

            <!-- Sample action chaining stack -->
            <interceptor-stack name="chainStack">
                <interceptor-ref name="chain"/>
                <interceptor-ref name="basicStack"/>
            </interceptor-stack>

            <!-- Sample i18n stack -->
            <interceptor-stack name="i18nStack">
                <interceptor-ref name="i18n"/>
                <interceptor-ref name="basicStack"/>
            </interceptor-stack>

            <!-- An example of the paramsPrepareParams trick. This stack
                 is exactly the same as the defaultStack, except that it
                 includes one extra interceptor before the prepare interceptor:
                 the params interceptor.

                 This is useful for when you wish to apply parameters directly
                 to an object that you wish to load externally (such as a DAO
                 or database or service layer), but can't load that object
                 until at least the ID parameter has been loaded. By loading
                 the parameters twice, you can retrieve the object in the
                 prepare() method, allowing the second params interceptor to
                 apply the values on the object. -->
            <interceptor-stack name="paramsPrepareParamsStack">
                <interceptor-ref name="exception"/>
                <interceptor-ref name="alias"/>
                <interceptor-ref name="i18n"/>
                <interceptor-ref name="checkbox"/>
                <interceptor-ref name="multiselect"/>
                <interceptor-ref name="params">
                    <param name="excludeParams">^class\..*,^dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,^parameters\..*,^action:.*,^method:.*</param>
                </interceptor-ref>
                <interceptor-ref name="servletConfig"/>
                <interceptor-ref name="prepare"/>
                <interceptor-ref name="chain"/>
                <interceptor-ref name="modelDriven"/>
                <interceptor-ref name="fileUpload"/>
                <interceptor-ref name="staticParams"/>
                <interceptor-ref name="actionMappingParams"/>
                <interceptor-ref name="params">
                    <param name="excludeParams">^class\..*,^dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,^parameters\..*,^action:.*,^method:.*</param>
                </interceptor-ref>
                <interceptor-ref name="conversionError"/>
                <interceptor-ref name="validation">
                    <param name="excludeMethods">input,back,cancel,browse</param>
                </interceptor-ref>
                <interceptor-ref name="workflow">
                    <param name="excludeMethods">input,back,cancel,browse</param>
                </interceptor-ref>
            </interceptor-stack>

            <!-- A complete stack with all the common interceptors in place.
                 Generally, this stack should be the one you use, though it
                 may do more than you need. Also, the ordering can be
                 switched around (ex: if you wish to have your servlet-related
                 objects applied before prepare() is called, you'd need to move
                 servletConfig interceptor up.

                 This stack also excludes from the normal validation and workflow
                 the method names input, back, and cancel. These typically are
                 associated with requests that should not be validated.
                 -->
            <interceptor-stack name="defaultStack">
                <interceptor-ref name="exception"/>
                <interceptor-ref name="alias"/>
                <interceptor-ref name="servletConfig"/>
                <interceptor-ref name="i18n"/>
                <interceptor-ref name="prepare"/>
                <interceptor-ref name="chain"/>
                <interceptor-ref name="scopedModelDriven"/>
                <interceptor-ref name="modelDriven"/>
                <interceptor-ref name="fileUpload"/>
                <interceptor-ref name="checkbox"/>
                <interceptor-ref name="multiselect"/>
                <interceptor-ref name="staticParams"/>
                <interceptor-ref name="actionMappingParams"/>
                <interceptor-ref name="params">
                    <param name="excludeParams">^class\..*,^dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,^parameters\..*,^action:.*,^method:.*</param>
                </interceptor-ref>
                <interceptor-ref name="conversionError"/>
                <interceptor-ref name="validation">
                    <param name="excludeMethods">input,back,cancel,browse</param>
                </interceptor-ref>
                <interceptor-ref name="workflow">
                    <param name="excludeMethods">input,back,cancel,browse</param>
                </interceptor-ref>
                <interceptor-ref name="debugging"/>
                <interceptor-ref name="deprecation"/>
            </interceptor-stack>

            <!-- The completeStack is here for backwards compatibility for
                 applications that still refer to the defaultStack by the
                 old name -->
            <interceptor-stack name="completeStack">
                <interceptor-ref name="defaultStack"/>
            </interceptor-stack>

            <!-- Sample execute and wait stack.
                 Note: execAndWait should always be the *last* interceptor. -->
            <interceptor-stack name="executeAndWaitStack">
                <interceptor-ref name="execAndWait">
                    <param name="excludeMethods">input,back,cancel</param>
                </interceptor-ref>
                <interceptor-ref name="defaultStack"/>
                <interceptor-ref name="execAndWait">
                    <param name="excludeMethods">input,back,cancel</param>
                </interceptor-ref>
            </interceptor-stack>

       </interceptors>

        <default-interceptor-ref name="defaultStack"/>

        <default-class-ref class="com.opensymphony.xwork2.ActionSupport" />
    </package>

 2.4 动态方法、method属性及通配符和默认Action

   众所周知,默认条件下,在浏览器输入indexAction!execute.action,便会执行indexAction类里的execute方法,这样虽然方便,但可能带来安全隐患,通过url可以执行Action中的任意方法。

  想要禁止调用动态方法,则要在struts.xml中通过constant元素将属性strutsenableDynamicMethodInvocation设置为false,来禁止调用动态方法。

 <constant name="struts.enable.DynamicMethodInvocation" value="false"/>

  小杜比亚注(-----------------begin)

  在struts 2的核心jar包struts2-core中,有一个default.properties的默认配置文件,里面配置了一些全局的信息,比如:

 //但是有些版本的struts2这边设置成false,这种情况下无法使用actionName!method.action形式的访问,需要将其重置成true
 struts.enable.DynamicMethodInvocation = true

  但这个default.properties是不能被更改的。如需要更改里面的配置信息,可以在src根目录下建一个 struts.properties的配置文件,然后重写所要更改的配置信息,当然,也可以在struts.xml里面如下形式重写属性:

 <constant name="strutsenableDynamicMethodInvocation" value="false"/>

  但是很多童鞋估计在试的时候还是会出现访问:actionName!method.action这种格式的时候,出现404错误,报:

 This method: xxx for action xxxxxxx is not allowed! 

  这是为什么呢?对啊,这是为什么呢???这个其实是版本问题,struts2.5之后,对可以使用动态访问的方法做了限制,默认会有这种配置:

 <global-allowed-methods>execute,input,back,cancel,browse,save,delete,list,index</global-allowed-methods>

  如果我们自己的方法不在这个列表中,那么struts过滤的时候,就不认我们方法,那怎么办呢?可以这样:

  (原文链接http://blog.csdn.net/qq_29407009/article/details/51474118

<struts>
    <package name="test" extends="struts-default">
        <global-allowed-methods>error</global-allowed-methods<!--增加自己的方法列表,多个方法之间用英文逗号分隔-->
        <action name="myTest" class="com.struts2.action.PageController">
            <result name="success">success.jsp</result>
            <result name="error">error.jsp</result>
        </action>
    </package>
</struts>    

  小杜比亚注(---------------------end)

 

  method属性:

  假如struts.enable.DynamicMethodInvocation = false,但是我们又要访问除了execute之外的方法, 要如何做?我们可以配置action节点的method属性,其值和action中的方法名一致。

  如,我有一个后台Action,有个error方法:

package com.struts2.action;

import com.opensymphony.xwork2.ActionSupport;

public class PageController extends ActionSupport {
    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    public String execute(){
        return ActionSupport.SUCCESS;
    }
    
    public String error(){
        return ActionSupport.ERROR;
    }
}

  action节点的配置:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN" "http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
    <package name="test" extends="struts-default">
        <action name="page" class="com.struts2.action.PageController" method="error">
            <result name="success">success.jsp</result>
            <result name="error">error.jsp</result>
        </action>
    </package>
</struts>

  以上一个配置便用到了method属性,当请求/page.action时,struts2框架会在PageController中查找error方法并执行,Struts2根据method属性查找方法有两种途径:

      1、查找与method属性值完全一致的方法(这比较常见)
      2、查找doMethod形式的方法(注:原文作者这样写,但是笔者测试的时候发现不行,不知道是不是版本问题
 

  通配符:

  在使用method属性来实现同一个Action的不同方法处理不同的请求时,会发现,随着方法的增多,从而导致大量的Action配置,这时我们就需要通过使用通配符来解决Action配置过多的方法。
  在配置<action.../>元素时,需要指定name、class、method属性。其中name属性可支持通配符,然后可以在class、method属性中使用表达式。通配符用星号 * 表示:
<action name="/page_*" class="com.struts2.action.PageController" method="{1}"> 
  <result name="success">/WEB-INF/jsp/basic/emp_{1}.jsp</result>
</action>

  以上配置表明,当请求/page_error.action时,通配符匹配的是error,这个值将替换{1},最终执行PageController的error方法,方法返回值为error,跳转到error.jsp页面(当然,在struts2.5之前的版本,这个是没有问题的,但是,但是,还是版本问题啊,2.5版本默认禁用通配符,需要开启),所以在2.5或者以上版本,要开启通配符,需要如下配置方能奏效:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN" "http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
    <package name="test" extends="struts-default" strict-method-invocation="false">
        <action name="page_*" class="com.struts2.action.PageController" method="{1}">
            <result name="success">success.jsp</result>
            <result name="error">error.jsp</result>
        </action>
    </package>
</struts>    

 

默认Action:

在浏览器输入一个不存在的Action,页面将呈现404错误,为了网站更友好,我们可以设置一个默认的Action。设置默认Action有两种形式:
  1. 配置每个包的默认Action,如果在相应的namespace下没有一个Action匹配请求,那么将执行该namespace默认的Action,不同的包,可配置不同的默认Action,配置如下:defaultAction为默认Action的name属性值,default语句必须写在首行。
    1. <package name="default" namespace="/emp" extends="struts-default">
               <default-action-ref name="defaultAction"></default-action-ref>
              <action name="defaultAction">
                  <result>/error.jsp</result>
              </action>
        </package>
  2. 在根目录下配置默认Action,不用填写namespace属性
    1. <package name="default" extends="struts-default">
               <default-action-ref name="defaultAction"></default-action-ref>
              <action name="defaultAction">
                  <result>/error.jsp</result>
              </action>
      </package> 

如果声明了第一种,Struts2将会调用当前包下声明的默认Action,忽视全局的默认Action,一般用第二种,统一默认的Action,不论在url中输入哪个目录或包下没有的Action,都显示错误页面

三、链接

http://www.cnblogs.com/ningvsban/p/3734562.html

http://blog.csdn.net/lifuxiangcaohui/article/details/8172956

http://www.tuicool.com/articles/iaiMniJ

http://blog.csdn.net/qq964166471/article/details/19915717

http://www.cnblogs.com/xiaona/p/cd6634b16ea1bd214e6fe232ad987a7e.html

http://blog.csdn.net/qq_29407009/article/details/51474118

http://blog.csdn.net/qq_24059599/article/details/51986761

四、联系本人(声明:只为技术分享,不为任何利益)

  QQ群:261746360,小杜比亚-博客园

 

推荐阅读