Struts2知识总结

Struts2知识总结

(一)Struts2会为每一个HTTP请求创建一个新的实例

(二)关于dispatcher、redirect、redirectAction、Chain

  1. dispatcher:解释为转发;只涉及一个线程,数据能够共享,请求request中存放的变量不会失效,就像把两个页面拼到了一起。而且dispatcher是服务跳转,浏览器不知道它所请求的具体资源来源,浏览器的地址栏不会变。

  2. edirect:解释为重定向;设计两个线程,数据不共享,请求request中的变量全部失效。并且刚刚执行的Action(包括action实例,action中的错误消息)会丢失,不再可用。因为action是建立在单线程模型基础上的。传递数据的唯一方式就是通过Session或者可以为Ognl表达式的web参数(url?name=abc)。服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址。所以地址栏显示的是新的URL。

  3. **redirect:**action处理完后重定向到一个视图资源(如:jsp页面),请求参数全部丢失,action处理结果也全部丢失。

  4. **redirect-action:**action处理完后重定向到一个action,请求参数全部丢失,action处理结果也全部丢失。

  5. redirect-action:调用type为redirect的action,直接调用action名。如果调用type为dispatcher的action,调用action名+action后缀名。

  6. redirect:不能调用类型为dispatcher的action;调用action时,方法为action名+action后缀名。

  7. dispatcher:不能调用其他action。

  8. Chain:

    chain 结果类型的基本用途是构成一个 action 链: 前一个 action 把控制权转发给后一个 action, 而前一个 action 的状态在后一个 action 中依然保持
    chain 结果类型接受下面这些参数:(用标签)
    actionName: 指定目标 action 的名字. 它是默认属性
    namespace: 用来指定 “目的地” action 的命名空间. 如果没有配置该参数, Struts 会把当前 action 所在的命名空间作为 “目的地” 的命名空间
    method: 指定目标 action 方法. 默认值为 execute

(三)在 Action 中, 可以通过以下方式访问 web 的 HttpSession, HttpServletRequest, HttpServletResponse 等资源

1)与Servlet API解耦的方式访问

​ ①通过com.opensymphony.xwork2.ActionContext

​ 步骤一:在Action类中获得ActionContext,ActionContext ac = ActionContext.getContext();
步骤二:获取application、session、request、parameter;

ActionContext ac = ActionContext.getContext();
Map<String,Object> applicationMap = ac.getApplication();
Map<String,Object> sessionMap = ac.getSession();
/*
ActionContext 中并没有提供 getRequest 方法来获取 request 对应的 Map
需要手工调用 get() 方法, 传入 request 字符串来获取. 
*/
Map<String,Object> requestMap = (Map<String,Object>)ac.get("request");
/*
获取请求参数对应的 Map, 并获取指定的参数值. 
键: 请求参数的名字, 值: 请求参数的值对应的字符串数组
注意: 
1. getParameters 的返回值为在 Map<String, Object>, 而不是 Map<String, String[]>
 2. parameters 这个 Map 只能读, 不能写入数据, 如果写入, 但不出错, 但也不起作用!
*/
Map<String,Object> parameters = ac.getParameter();
在Action中如何获取parameters,System.out.println(((String[])parameters.get("name"))[0]);

​ ②通过Action实现如下接口:
​ org.apache.struts2.interceptor.ApplicationAware;
​ ……. RequestAware;
​ …….. SessionAware;

​ 步骤一:实现对应XxxAware,implements ApplicationAware,SessionAware,RequestAware,ParameterAware;

​ 步骤二:实现对应的方法。

public void setApplication(Map<String, Object> application) {}
public void setParameters(Map<String, String[]> parameters) {}
public void setRequest(Map<String, Object> request) {}
public void setSession(Map<String, Object> session) {}

​ 步骤三:设置对应的成员变量,然后调用对应的set方法。

Map<String,Object> application;Map<String,String[] parameters;
Map<String,Object> request;Map<String,Object> session;

2)与Servlet API耦合的访问方式

​ ①通过org.apache.struts2.ServletActionContext;

​ 步骤一:

直接访问Servlet API将使Action与Servlet环境耦合在一起,测试时需要Servlet容器,不便于对Action的单元测试,直接获取HttpServletRequest。

​ 步骤二:

直接获取 HttpServletRequest 对象: ServletActionContext.getRequest()

直接获取 HttpSession 对象ServletActionContext.getRequest().getSession()

直接获取 ServletContext 对象ServletActionContext.getServletContext()

​ ②实现对应的XxxAware接口

​ 通过实现 ServletRequestAware, ServletContextAware 等接口的方式

(四)OGNL(对象图示导航语言)

1)在 JSP 页面上可以可以利用 OGNL(Object-Graph Navigation Language: 对象-图导航语言) 访问到值栈(ValueStack) 里的对象属性.
2)值栈(ValueStack),在Struts2中很重要的概念。
  • ValueStack(值栈): 贯穿整个 Action 的生命周期(每个 Action 类的对象实例都拥有一个 ValueStack 对象). 相当于一个数据的中转站. 在其中保存当前 Action 对象和其他相关对象. Struts 框架把 ValueStack 对象保存在名为 “struts.valueStack” 的请求属性中。
  • 获得ValueStack的方法:
    ValueStack vs = ActionContext.getContext().getValueStack();
    然后调用vs.push(object)方法可以将对象压入栈顶。
3)值栈中有两个逻辑对象:ContextMap、ObjectStack
  • ContextMap:: Struts 把各种各样的映射关系(一些 Map 类型的对象) 压入 ContextMap 中. 实际上就是对 ActionContext 的一个引用
  • Struts 会把下面这些映射压入 ContextMap 中
    parameters: 该 Map 中包含当前请求的请求参数
    request: 该 Map 中包含当前 request 对象中的所有属性
    session: 该 Map 中包含当前 session 对象中的所有属性
    application:该 Map 中包含当前 application 对象中的所有属性
    attr: 该 Map 按如下顺序来检索某个属性: request, session, application
  • ObjectStack:Struts 把 Action 和相关对象压入 ObjectStack 中
  • clipboard
  • 在 ValueStack 对象的内部有两个逻辑部分:
    ObjectStack: Struts 把 Action 和相关对象压入 ObjectStack 中
    ContextMap: Struts 把各种各样的映射关系(一些 Map 类型的对象) 压入 ContextMap 中. 实际上就是对 ActionContext 的一个引用
4)若希望访问值栈中 ContextMap 中的数据, 需要给 OGNL 表达式加上一个前缀字符 #. 如果没有前缀字符 #, 搜索将在 ObjectStack 里进行.

(五)用Struts的property标签来读取值栈中的值。

1)访问ObjectStack的值有三种方式:
一:object.pname 
二:object.'pnam' 
三:object."pname",与property标签一起使用:<s:property value="[1].name"></s:property>
2)访问ContextMap的值有三种方式:
一:#object.pname 
二:#object.'pname' 
三:#object."pname"
3)可以使用EL访问值栈中的属性:
<s:property value="filename"/>,${filename}

(六)ModelDriven和Preparable拦截器

1)Parameters 拦截器将把表单字段映射到 ValueStack 栈的栈顶对象的各个属性中. 如果某个字段在模型里没有匹配的属性, Param 拦截器将尝试 ValueStack 栈中的下一个对象
2)把 Action 和 Model 隔开

​ 在使用 Struts 作为前端的企业级应用程序时把 Action 和 Model 清晰地隔离开是有必要的: 有些 Action 类不代表任何Model 对象, 它们的功能仅限于提供显示服务

3)如果 Action 类实现了 ModelDriven 接口,该拦截器将把 ModelDriven 接口的 getModel() 方法返回的对象置于栈顶

(七)ModelDriven 拦截器

1) 当用户触发 add 请求时, ModelDriven 拦截器将调用 EmployeeAction 对象的 getModel() 方法, 并把返回的模型(Employee实例)压入到 ValueStack 栈.
2)接下来 Parameters 拦截器将把表单字段映射到 ValueStack 栈的栈顶对象的各个属性中. 因为此时 ValueStack 栈的栈顶元素是刚被压入的模型(Employee)对象, 所以该模型将被填充. 如果某个字段在模型里没有匹配的属性, Param 拦截器将尝试 ValueStack 栈中的下一个对象

(八)Preparable 拦截器

1)Struts 2.0 中的 modelDriven 拦截器负责把 Action 类以外的一个对象压入到值栈栈顶,而 prepare 拦截器负责准备为 getModel() 方法准备 model

(九)PrepareInterceptor拦截器用方法

1)若 Action 实现 Preparable 接口,则 Action 方法需实现 prepare() 方法
2)PrepareInterceptor 拦截器将调用 prepare() 方法,prepareActionMethodName()方法 或 prepareDoActionMethodName ()方法
3)PrepareInterceptor 拦截器根据 firstCallPrepareDo 属性决定获取 prepareActionMethodName 、prepareDoActionMethodName的顺序。默认情况下先获取 prepareActionMethodName (), 如果没有该方法,就寻找prepareDoActionMethodName()。如果找到对应的方法就调用该方法
4)PrepareInterceptor 拦截器会根据 alwaysInvokePrepare 属性决定是否执行prepare()方法

(十)使用 paramsPrepareParamsStack 拦截器栈

1)paramsPrepareParamsStack 从字面上理解来说, 这个stack的拦截器调用的顺序为:首先 params,然后 prepare,接下来 modelDriven,最后再 params
2)Struts 2.0的设计上要求 modelDriven 在 params 之前调用,而业务中prepare要负责准备model,准备model又需要参数,这就需要在 prepare之前运行params拦截器设置相关参数,这个也就是创建paramsPrepareParamsStack的原因。
3)流程如下:
  1. params拦截器首先给action中的相关参数赋值,如id

    1. prepare拦截器执行prepare方法,prepare方法中会根据参数,如id,去调用业务逻 辑,设置model对象
  2. modelDriven拦截器将model对象压入value stack,这里的model对象就是在prepare 中创建的

  3. params拦截器再将参数赋值给model对象

  4. action的业务逻辑执行

    clipboard

(十一)类型转换

1)从一个 HTML 表单到一个 Action 对象, 类型转换是从字符串到非字符串.
2)HTTP 没有 “类型” 的概念. 每一项表单输入只可能是一个字符串或一个字符串数组. 在服务器端, 必须把 String 转换为特定的数据类型
3)在 struts2 中, 把请求参数映射到 action 属性的工作由 Parameters 拦截器负责, 它是默认的 defaultStack 拦截器中的一员. Parameters 拦截器可以自动完成字符串和基本数据类型之间转换.

(十二)类型转换错误

1)如果类型转换失败:

若 Action 类没有实现 ValidationAware 接口: Struts 在遇到类型转换错误时仍会继续调用其 Action 方法, 就好像什么都没发生一样.

若 Action 类实现 ValidationAware 接口:Struts 在遇到类型转换错误时将不会继续调用其 Action 方法: Struts 将检查相关 action 元素的声明是否包含着一个 name=input 的 result. 如果有, Struts 将把控制权转交给那个 result 元素; 若没有 input 结果, Struts 将抛出一个异常

(十三)类型转换错误消息的定制

1)ConversionError拦截器

作为默认的 default 拦截器的一员, ConversionError 拦截器负责添 加与类型转换有关的出错消息(前提: Action 类必须实现了 ValidationAware 接口)和保存各请求参数的原始值.

2)若字段标签使用的不是 simple 主题, 则非法输入字段将导致一条有着以下格式的出错消息:
Invalid field value for field fieldName.
3)覆盖默认的出错消息

​ 在对应的 Action 类所在的包中新建 ActionClassName.properties 文件, ClassName 即为包含着输入字段的 Action 类的类名

在属性文件中添加如下键值对: invalid.fieldvalue.fieldName=Custom error message
4)定制出错消息的样式:

每一条出错消息都被打包在一个 HTML span 元素里, 可以通过覆盖其行标为 errorMessage 的那个 css 样式来改变出错消息的格式.

5)显示错误消息

如果是 simple 主题, 可以通过

<s:fielderror fieldName=“filedname”></s:fielderror> 标签显示错误消息

(十四)Struts2声明式框架

1)Struts2声明式框架-helloworld
step1:创建TestValidationAction.java类
public class TestValidationAction extends ActionSupport {
    private static final long serialVersionUID = 1L;
    private Integer age;
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    @Override
    public String execute() throws Exception {
        System.out.println("age: " + age);
        return SUCCESS;
    }
}
step2:success.jsp,就一个<h1>Success!</h1>
step3:编写TestValidationAction-validation.xml文件
<!DOCTYPE validators PUBLIC
        "-//Apache Struts//XWork Validator 1.0.2//EN"
        "http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<validators>
    <!-- 针对age属性进行验证,基于字段的验证 -->
    <field name="age">
        <field-validator type="int">
            <param name="min">20</param>
            <param name="max">50</param>
            <message>年龄必须在20岁和50岁之间</message>
     <!--<message key="error.int"></message>-->
        </field-validator>
    </field>
</validators>
step4:编写struts.xml文件
<struts>
                    <!-- 导入国际资源文件-i18n.properties,在xx-validation.xml文件中配合<message key=""></message>一起使用过 -->
    <constant name="struts.custom.i18n.resources" value="i18n"></constant>

    <package name="default" namespace="/" extends="struts-default">
        <action name="testValidation" class="com.atguigu.struts2.validation.app.TestValidationAction">
            <result>/success.jsp</result>

            <!-- 若验证失败 -->
            <result name="input">/validation.jsp</result>
        </action>
    </package>
</struts>
step5:配置国际资源文件i18n.properties
error.int=${getText(fieldName)} needs to be between ${min} and ${max}

需要注意的地方:
若使用的是非 simple, 则自动显示错误消息.
若使用的是 simple 主题, 则需要 s:fielderror 标签或直接使用 EL 表达式(使用 OGNL)
${fieldErrors.age[0] } OR<s:fielderror fieldName="age"></s:fielderror>

(十五)使用一个声明式验证程序需要 3 个步骤:

  1. 确定哪些 Action 字段需要验证

  2. 编写一个验证程序配置文件. 它的文件名必须是以下两种格式之一

    若一个 Action 类的多个 action 使用同样的验证规则: ActionClassName-validation.xml
    若一个 Action 类的多个 action 使用不同的验证规则: ActionClass-alias-validation.xml, 例如 UserAction-User_create-validation.xml

    示例代码:

    validataion2.jsp文件:
    <s:form action="testValidation2" theme="simple">
           Age: <s:textfield name="age" label="age"></s:textfield>
           ${fieldErrors.age[0]}
           <s:fielderror fieldName="age"></s:fielderror>
           <s:submit></s:submit>
    </s:form>
    struts.xml文件:
    <action name="testValidation2" class="com.atguigu.struts2.validation.app.TestValidationAction">
               <result>/success.jsp</result>
               <result name="input">/validation.jsp</result>
           </action>

    clipboard2

    TestValidation-testValidation2-validation.xml文件:
    <validators>
       <!-- 针对age属性进行验证,基于字段的验证 -->
       <field name="age">
           <field-validator type="int">
               <param name="min">1</param>
               <param name="max">130</param>
               <message key="error.int"></message>
           </field-validator>
       </field>
    </validators>

  3. 确定验证失败时的响应页面: 在 struts.xml 文件中定义一个

    <result name=“input”>

    的元素.

(十六)声明式验证框架的原理

1)Struts2 默认的拦截器栈中提供了一个 validation 拦截器
2)Struts2提供有多个验证器

每个具体的验证规则都会对应具体的一个验证器. 有一个配置文件把验证规则名称和验证器关联起来了. 而实际上验证的是那个验证器.

3)该文件位于 com.opensymphony.xwork2.validator.validators 下的 default.xml
<validator name="required"  class="com.opensymphony.xwork2.validator.validators.RequiredFieldValidator"/>

(十七)短路验证

1)什么是短路验证?

若对一个字段使用多个验证器, 默认情况下会执行所有的验证. 若希望前面的验证器验证没有通过, 后面的就不再验证, 可以使用短路验证:

<!-- 设置短路验证: 若当前验证没有通过, 则不再进行下面的验证 -->
        <field-validator type="conversion" short-circuit="true">
            <message>^Conversion Error Occurred</message>
        </field-validator>

        <field-validator type="int">
            <param name="min">20</param>
            <param name="max">60</param>
            <message key="error.int"></message>
        </field-validator>  

(十八)若类型转换失败,如何停止执行后面的拦截器?

1)如何停止执行后面的拦截器

若类型转换失败, 默认情况下还会执行后面的拦截器, 还会进行 验证.可以通过修改 ConversionErrorInterceptor(com.opensymphony.xwork2.interceptor.ConversionErrorInterceptor) 源代码的方式使

当类型转换失败时, 不再执行后续的验证拦截器, 而直接返回 input 的 result。

2)修改关键代码:
return invocation.invoke();方法前执行关键代码。

Object action = invocation.getAction();
        if (action instanceof ValidationAware) {
            ValidationAware va = (ValidationAware) action;
            //如果有错,就在此打住,不再显示其他错,然后就跳到input.jsp页面
            if(va.hasFieldErrors() || va.hasActionErrors()){
                return "input";
            }
        }

(十九)如何在不同的字段使用同样的验证规则,而且使用同样的相应消息?

1)解决方案:
在配置文件i18n.properties中:
error.int=${getText(fieldName)} needs to be between ${min} and ${max}
age=\u5E74\u9F84
count=\u6570\u91CF 

使用${getText(fieldName)}的原因是:

在IntRangeFieldValidator中,有fieldName这个属性,所以可以通过${fieldName}获得该属性的值,又因为fieldName(这里是age和count)有text值,所以可以通过getText()方法获得相应的值。

(二十)文件的上传和下载

1)文件上传
  1. Struts2 的文件上传实际上使用的是 Commons FileUpload 组件, 所以需要导入

    commons-fileupload-1.3.jar
    commons-io-2.0.1.jar

  2. Struts2 进行文件上传需要使用 FileUpload 拦截器

  3. 基本的文件的上传: 直接在 Action 中定义如下 3 个属性, 并提供对应的 getter 和 setter

    //文件对应的 File 对象
    private File [fileFieldName];
    //文件类型
    private String [fileFieldName]ContentType;
    //文件名
    private String [fileFieldName]FileName;
  4. 使用 IO 流进行文件的上传即可.

  5. 一次传多个文件怎么办 ?

    若传递多个文件, 则上述的 3 个属性, 可以改为 List 类型! 多个文件域的 name 属性值需要一致.

  6. 对上传的文件进行限制,如扩展名、文件类型、文件大小

    可以通过配置 FileUploadInterceptor 拦截器的参数的方式来进行限制

    maximumSize (optional) - 默认的最大值为 2M. 上传的单个文件的最大值

    allowedTypes (optional) - 允许的上传文件的类型. 多个使用 , 分割

    allowedExtensions (optional) - 允许的上传文件的扩展名. 多个使用 , 分割.

  7. 定制文件上传错误消息

    定制错误消息. 可以在国际化资源文件中定义如下的消息:

    struts.messages.error.uploading - 文件上传出错的消息

    struts.messages.error.file.too.large - 文件超过最大值的消息

    struts.messages.error.content.type.not.allowed - 文件内容类型不合法的消息

    struts.messages.error.file.extension.not.allowed - 文件扩展名不合法的消息

    问题: 此种方式定制的消息并不完善. 可以参考 org.apache.struts2 下的 struts-messages.properties, 可以提供更多的定制信息.

2)文件下载

(二十一)表单的重定向提交问题

1)表单的重复提交的几种情况
> 多次点击提交按钮
> 已经提交成功,按“回退”之后,再点击提交按钮
> 在控制器响应页面的形式为转发情况下,已经提交成功,然后点击刷新,实际就是刷新该Action

​ 注意:

​ 1.若刷新表单页面,再提交表单不算重复提交

​ 2.若使用的是redirect的响应类型,已经提交成功后,再点击“刷新”,不算表单的重复提交。

2)表单重复提交的危害

浪费内存

3)Struts2解决表单的重复提交问题
  1. 在 s:form 中添加 s:token 子标签

  2. 使用 Token 或 TokenSession 拦截器.

    这两个拦截器均不在默认的拦截器栈中, 所以需要手工配置一下
    若使用 Token 拦截器, 则需要配置一个 token.valid 的 result
    若使用 TokenSession 拦截器, 则不需要配置任何其它的 result

  3. Token VS TokenSession

    都是解决表单重复提交问题的
    使用 token 拦截器会转到 token.valid 这个 result
    使用 tokenSession 拦截器则还会响应那个目标页面, 但不会执行 tokenSession 的后续拦截器. 就像什么都没发生过一样!

  4. 可以使用 s:actionerror 标签来显示重复提交的错误消息.
    该错误消息可以在国际化资源文件中覆盖. 该消息可以在 struts-messages.properties 文件中找到

    struts.messages.invalid.token=^^The form has already been processed or no token was supplied, please try again.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值