Java
Java Web框架

Struts2(3)

简介:Struts2是一个基于MVC设计模式的Web应用框架,它本质上相当于一个Servlet,在MVC设计模式中,Struts2作为控制器(Controller)来建立模型与视图的数据交互。Struts2是Struts的下一代产品,是在Struts1和WebWork的技术基础上进行了合并的全新的Struts2框架。

1. 校验进阶

在这一节将介绍一些校验相关的进阶用法。

1.1 跳过指定的校验方法

一个Action中可以存在多个请求处理方法,不同的请求处理方法应该有不同的校验逻辑,所以我们应该为每个请求处理方法提供自己独有的校验方法。而validate()方法是所有请求处理方法公共的校验方法。如果想跳过某个Action方法的校验,在不需要校验的Action方法上加入@SkipValidation

1.2. 指定校验某个方法

  • public修饰的,没有返回值,没有参数(public void xxx());
  • 方法名称前缀为validatepublic void validateXxx());
  • 方法名后缀必须与请求处理方法名相同,例如请求处理方法为login(),那么它的私有校验方法为:public validateLogin()

注:私有校验方法会在公共校验方法(validate())之前被调用。如果要为execute()提供私有校验方法,那么这个方法名为validateExecute()

  • public class Demo2Action extends ActionSupport {
  • public void validateLogin() {
  • System.out.println("validateLogin()...");
  • }
  • public void validateRegist() {
  • System.out.println("validateRegist()...");
  • }
  • public void validate() {
  • System.out.println("validate()...");
  • }
  • public String login() {
  • System.out.println("login()");
  • return NONE;
  • }
  • public String regist() {
  • System.out.println("regist()");
  • return NONE;
  • }
  • }

1.3. XML配置方式校验(了解)

使用XML配置方式是先把常用的校验规则写好,然后在XML配置中指定要使用的校验规则。当然Struts2已经帮我们写好了很多的校验规则。我们只需要指在XML文档中配置当前的请求处理方法需要哪些校验规则。

1.3.1. XML配置方式校验要求

要使用XML配置方式校验,Action类必须实现Validateable接口。ActionSupport类已经实现了Validateable接口,所以我们通常是直接继承ActionSupport类。
为属性提供getXXX()setXXX()方法!代码校验是在Action本类中来完成校验,这说明我们可以直接使用本类的private属性,但如果使用XML配置方式校验,这需要使用校验框架的代码来完成校验工作,那么校验框架需要调用Action的getXXX()方法来获取被校验的属性,所以一定要为被校验的属性提供getXXX()方法。

1.3.2. 创建校验文件

  1. 校验文件的命名必须为:ActionName-validation.xml。例如LoginAction类的校验文件命名为:LoginAction-validation.xml
  2. 校验文件的路径:必须与Action在同包下。
  3. 校验文件的DTD:在xwork-core-x.x.x.jar中找到xwork-validator-x.x.x.dtd,打开它,内部会有一段DTD,我们把它copy过来,放到我们的校验文件中。

1.3.3. 编写校验文件

校验文件的元素结果如下:

  • <validators>
  • <field name="">
  • <field-validator type="">
  • <param name=""></param>
  • <message></message>
  • </field-validator>
  • </field>
  • </validators>

<field>name属性指定要校验的属性,例如<feld name=”username”>,表示要校验的属性是username属性。
<field-validator>type属性指定校验规则,校验规则由Struts2提供,Struts2提供的所有校验规则在xwork-core-2.3.15.1.jar中:

3.1-1.Struts的验证器类.png

打开default.xml文件,内部如下:

  • <?xml version="1.0" encoding="UTF-8"?>
  • <!DOCTYPE validators PUBLIC
  • "-//Apache Struts//XWork Validator Definition 1.0//EN"
  • "http://struts.apache.org/dtds/xwork-validator-definition-1.0.dtd">
  • <!-- START SNIPPET: validators-default -->
  • <validators>
  • <validator name="required" class="com.opensymphony.xwork2.validator.validators.RequiredFieldValidator"/>
  • <validator name="requiredstring" class="com.opensymphony.xwork2.validator.validators.RequiredStringValidator"/>
  • <validator name="int" class="com.opensymphony.xwork2.validator.validators.IntRangeFieldValidator"/>
  • <validator name="long" class="com.opensymphony.xwork2.validator.validators.LongRangeFieldValidator"/>
  • <validator name="short" class="com.opensymphony.xwork2.validator.validators.ShortRangeFieldValidator"/>
  • <validator name="double" class="com.opensymphony.xwork2.validator.validators.DoubleRangeFieldValidator"/>
  • <validator name="date" class="com.opensymphony.xwork2.validator.validators.DateRangeFieldValidator"/>
  • <validator name="expression" class="com.opensymphony.xwork2.validator.validators.ExpressionValidator"/>
  • <validator name="fieldexpression" class="com.opensymphony.xwork2.validator.validators.FieldExpressionValidator"/>
  • <validator name="email" class="com.opensymphony.xwork2.validator.validators.EmailValidator"/>
  • <validator name="url" class="com.opensymphony.xwork2.validator.validators.URLValidator"/>
  • <validator name="visitor" class="com.opensymphony.xwork2.validator.validators.VisitorFieldValidator"/>
  • <validator name="conversion" class="com.opensymphony.xwork2.validator.validators.ConversionErrorFieldValidator"/>
  • <validator name="stringlength" class="com.opensymphony.xwork2.validator.validators.StringLengthFieldValidator"/>
  • <validator name="regex" class="com.opensymphony.xwork2.validator.validators.RegexFieldValidator"/>
  • <validator name="conditionalvisitor" class="com.opensymphony.xwork2.validator.validators.ConditionalVisitorFieldValidator"/>
  • </validators>

上面文件中每个<validator>元素都是一个校验规则,校验规则对应一些已经写好的方法,他们有校验属性非空的规则,有校验字符串属性长度的规则,有校验int属性范围的规则等等。通常我们不需要自己来编写校验规范,因为上面的校验规则已经足够了。

每个规则都有自己的名字,校验文件中<field-validator>type就是用来指定校验规则的名称。例如下面的代码是对username属性的非空校验:

  • <validators>
  • <field name="username">
  • <field-validator type="requiredString">
  • ……
  • </field-validator>
  • </field>
  • </validators>

其中type=”requiredString”是校验规则的名称,它必须对应defualt.xml文件中<validator>元素的name属性值。requiredString校验规则是校验字符串属性是否长度为0,如果长度为0,它会向fieldError中添加错误信息。

<message>元素指定的是错误信息,例如:

  • <validators>
  • <field name="username">
  • <field-validator type="requiredstring">
  • <message>用户名不能为空</message>
  • </field-validator>
  • </field>
  • </validators>

每个校验规则还都有自己的参数,如果想知道每个校验规则有什么参数,那么最好的方法是去查看校验规则的源代码。例如requiredstring校验规则有一个trim参数,它是boolean类型的参数,当trimtrue时,requiredString校验器会先调用属性的trim()方法去掉前后空白,然后再校验长度是否为0。trim参数的默认值就是true

  • public class RequiredStringValidator extends FieldValidatorSupport {
  • private boolean doTrim = true;
  • public void setTrim(boolean trim) {
  • doTrim = trim;
  • }
  • public boolean getTrim() {
  • return doTrim;
  • }
  • public void validate(Object object) throws ValidationException {
  • String fieldName = getFieldName();
  • Object value = this.getFieldValue(fieldName, object);
  • if (!(value instanceof String)) {
  • addFieldError(fieldName, object);
  • } else {
  • String s = (String) value;
  • if (doTrim) {
  • s = s.trim();
  • }
  • if (s.length() == 0) {
  • addFieldError(fieldName, object);
  • }
  • }
  • }
  • }

下面是完整的LoginActoin类的校验文件:

  • <validators>
  • <field name="username">
  • <field-validator type="requiredstring">
  • <!-- 调用校验器的setTrim()方法传递参数false -->
  • <param name="trim">false</param>
  • <message>用户名不能为空</message>
  • </field-validator>
  • <field-validator type="stringlength">
  • <!-- 调用校验器的setMaxLength()方法传递参数16 -->
  • <param name="maxLength">16</param>
  • <!-- 调用校验器的setMinLength()方法传递参数3 -->
  • <param name="minLength">3</param>
  • <message>用户名长度必须在3~16之间</message>
  • </field-validator>
  • </field>
  • <field name="password">
  • <field-validator type="requiredstring">
  • <param name="trim">false</param>
  • <message>密码不能为空</message>
  • </field-validator>
  • </field>
  • </validators>

1.3.4. 校验规则介绍

  1. required:当属性为null时校验失败;

  2. requiredstring:当字符串属性长度为0时校验失败:
    - 参数trim:默认值为true,表示去除前后空白后再校验长度。

  3. stringlength:当字符串长度不在指定范围内时校验失败:
    - minLength:指定字符串的最小长度;
    - maxLength:指定字符串的最大长度。

  4. regex:属性不能匹配正则表达式时校验失败:
    - expression:指定正则表达式;
    - caseSensitive:默认值为true,表示不忽略大小写。

  5. int:当int属性不在指定范围内校验失败:
    - min:最小值;
    - max:最大值。

  6. double:当double属性不在指定范围内校验失败:
    - min:最小值;
    - max:最大值。

  7. fieldexpression:属性必须是OGNL表达式:
    - expression:用来校验的ONGL表达式,例如pass == repass,其中passrepass是两个属性名称,当这两个属性的值相等时校验通过,否则失败。

  8. email:属性必须是合法的邮件地址;

  9. url:属性必须是合法的网址;

  10. date:属性必须是合法的日期格式。

2. 拦截器

拦截器来自AOP(面向切面编程)思想。它看起来与JavaWeb中的Filter极其相似。我们已经知道请求会先过一系列拦截器,最终到达Action,或者中途中断。也就是说,每个拦截器都有中断请求的能力。在Struts2中定义了很多拦截器,可以去struts-default.xml文件中查看。

2.1. 自定义拦截器

我们也可以自定义拦截器,Struts2要求所有拦截器必须实现Interceptor接口:

  • public interface Interceptor extends Serializable {
  • void destroy();
  • void init();
  • String intercept(ActionInvocation invocation) throws Exception;
  • }

Struts2还提供了一个Interceptor接口的实现类:AbstractInterceptor,通常我们自定义拦截器都是通过继承AbstractInterceptor类,而不是实现Interceptor接口:

  • public abstract class AbstractInterceptor implements Interceptor {
  • public void init() {}
  • public void destroy() {}
  • public abstract String intercept(ActionInvocation invocation) throws Exception;
  • }

继承AbstractInterceptor类时,不需要“被迫”实现init()和destroy()方法,而只需要关注intercept()方法即可。下面我们自定义一个拦截器:

  • package com.coderap.interceptor;
  • import com.opensymphony.xwork2.ActionContext;
  • import com.opensymphony.xwork2.ActionInvocation;
  • import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
  • public class HelloInterceptor extends AbstractInterceptor {
  • @Override
  • public String intercept(ActionInvocation ai) throws Exception {
  • System.out.println("hello interceptor!");
  • // 放行到下一个拦截器的方法
  • return ai.invoke();
  • }
  • }

2.2. 注册拦截器

注册拦截器一共分为两步:

  1. <package>中声明拦截器;
  2. <action>中引用拦截器。
  • <package name="interceptor" namespace="/" extends="struts-default">
  • <interceptors>
  • <interceptor name="HelloInterceptor" class="com.coderap.interceptor.HelloInterceptor" />
  • </interceptors>
  • <action name="Demo1Action" class="com.coderap.interceptor.Demo1Action" >
  • <result name="success" type="dispatcher" >/index.jsp</result>
  • <!-- 如果配置了Action自己要走的拦截器,那么默认拦截器栈就不会走了,需要我们自己配置 -->
  • <interceptor-ref name="defaultStack"></interceptor-ref>
  • <!-- Demo1Action 经过HelloInterceptor拦截器的拦截-->
  • <interceptor-ref name="HelloInterceptor"></interceptor-ref>
  • </action>
  • </package>

上面的代码虽然可以执行HelloInterceptor了,但因为Struts2有这么一种机制,一旦为Action指定了拦截器,那么就不会再为这个Action执行默认拦截器了,即defaultStack这个拦截器栈中的拦截器都不会执行,所以我们需要使用<interceptor-ref name="defaultStack"></interceptor-ref>在这个<action>元素中再引用defaultStack拦截器栈。

<aciton>元素中引用拦截器的顺序决定了拦截器的执行顺序,上例中会先执行defaultStack中的所有拦截器,再执行HelloInterceptor拦截器。

上面的方式虽然可以注册拦截器,但比较麻烦。因为如果当前包中所有<action>都需要执行MyInterceptor拦截器,那么就需要在每个<action>元素中引入拦截器。其实还有另一种方式,就是为当前包指定默认拦截器栈!
我们都知道,因为我们的包继承了struts-default包,所以默认的拦截器栈是defaultStack,但没有为<action>元素指定拦截器时,那么就会执行defaultStack拦截器栈。我们可以在<package>中声明一个拦截器栈,然后在去替换默认拦截器栈即可。

  • <package name="interceptor" namespace="/" extends="struts-default">
  • <interceptors>
  • <interceptor name="HelloInterceptor" class="com.coderap.interceptor.HelloInterceptor" />
  • <interceptor-stack name="myStack">
  • <interceptor-ref name="defaultStack" />
  • <interceptor-ref name="HelloInterceptor" />
  • </interceptor-stack>
  • </interceptors>
  • <!-- 将自己配置的拦截器栈myDefaultStack设置为默认拦截器栈,在该包下再创建Action不需要加以配置就默认走该拦截器栈;如果想要在其他package也应用当前包的拦截器设置,只需要让其他包去继承该package即可。-->
  • <default-interceptor-ref name="myDefaultStack"></default-interceptor-ref>
  • <action name="Demo1Action" class="com.coderap.interceptor.Demo1Action" >
  • <result name="success" type="dispatcher" >/index.jsp</result>
  • </action>
  • </package>

2.3. 拦截器进阶(MethodFilterInterceptor)

我们可以配置特定的拦截器,拦截Action的指定方法。首先创建自定义的指定方法拦截器:

  • package com.coderap.Interceptor;
  • import com.opensymphony.xwork2.ActionInvocation;
  • import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
  • public class MyMethodInterceptor extends MethodFilterInterceptor {
  • @Override
  • protected String doIntercept(ActionInvocation invocation) throws Exception {
  • System.out.println("Hello MyMethodInterceptor!");
  • return invocation.invoke();
  • }
  • }

然后在xml中编写相应的拦截器配置项:

  • <package name="interceptor" namespace="/" extends="struts-default">
  • <interceptors>
  • <interceptor name="HelloInterceptor" class="com.coderap.interceptor.HelloInterceptor" />
  • <interceptor name="MyMethodInterceptor" class="com.coderap.interceptor.MyMethodInterceptor" />
  • <interceptor-stack name="myStack">
  • <interceptor-ref name="defaultStack" />
  • <interceptor-ref name="HelloInterceptor" />
  • <interceptor-ref name="MyMethodInterceptor">
  • <param name="excludeMethods">find</param>
  • </interceptor-ref>
  • </interceptor-stack>
  • </interceptors>
  • <default-interceptor-ref name="myDefaultStack"></default-interceptor-ref>
  • <action name="Demo2Action" class="com.coderap.interceptor.Demo2Action" >
  • <result name="success" type="dispatcher" >/index.jsp</result>
  • </action>
  • </package>

相应的Action类的代码如下:

  • package com.coderap.interceptor;
  • import org.apache.struts2.interceptor.validation.SkipValidation;
  • import com.opensymphony.xwork2.ActionSupport;
  • public class Demo2Action extends ActionSupport {
  • public String add(){
  • return "success";
  • }
  • public String find(){
  • return "success";
  • }
  • }

由于我们配置了:

  • <interceptor-ref name="MyMethodInterceptor">
  • <param name="excludeMethods">find</param>
  • </interceptor-ref>

所以在Demo2Action中,find()方法被排除来拦截器拦截的方法之外,访问相应的Action的方法,会发现find()方法并没有被拦截,而add()会被拦截。