Struts2学习笔记

知识导入

xml解析

   dom4j解析

反射

   有完整类名,并且含有空参构造函数,如何创建出对象?

//使用反射创建对象
Class clazz = Class.forName("xxx");
clazz.newInstance();

filter过滤器

  • 过滤来自浏览器发送的请求.在请求到达访问资源之前.会先经过过滤器.
  • 可以完成批处理. 还可以根据filterChain来决定是否放行.
  • 也可以使用请求转发,或重定向,不让请求到目标路径.

自己定义一个struts-Mini

struts-mini图解

Struts2基础

框架相关

  • 什么是框架?
      微观:由一个一个类构成的包,包再构成框架
      宏观:半成品的项目,我们需要执行什么业务时,为框架填写业务逻辑即可
  • 三大框架:SSH=>Struts,Spring,Hibernate
      Struts的版本:
       Struts1(已经淘汰)
       Struts2(我们要学的)也叫xwork。
  • 三大框架的作用?
      简化我们的开发,降低开发成本,提高开发效率
     Struts=> WEB层框架:
       优势:
         1.不用侵入性比较高的Servlet.可以直接写普通java类作为处理请求逻辑的处理类.这样更易于测试.
         2.整合了一些之前需要手动来做的功能. 后台验证,表单回显,自动将表单参数封装,防止表单重复提交,如果不够用我们还可以扩展框架的功能.

Struts2的HelloWorld

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
   //---------------------------------
//cn.pinzhi.action.HelloAction
//-----------------------------
package cn.pinzhi.action;
public class HelloAction {
public String execute(){
System.out.println("hello world");
return "success"; // 结果页面命名
}
}
//-----------------------------------------
//src下的struts.xml
//------------------------------------------
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<!-- name:单纯的给包起一个名字,不要与其他包名重复
namespace:为action访问加上一层或多层路径
etends:继承一个其他包,目的是引入其他包的配置
-->
<package name="hello" namespace="/" extends="struts-default">
<!--name:为action类加上一个标识,在访问时填入name名称,就可以找到action
classaction完整类名
method:处理的方法
-->
<action name="hello" class="cn.pinzhi.action.HelloAction" method="execute">
<!--name:返回结果的标识,用于找到结果路径
type:可以决定跳转到结果的方式=》转发,重定向。。。
-->
<result name="success">/success.jsp</result>
</action>
</package>
</struts>
//------------------------------------
//web.xml
//------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
<display-name>helloStruts</display-name>
<!-- 重要:配置struts2的核心过滤器 -->
<!--配置struts2的过滤器,要放到其他过滤器后面,保证其他过滤器可以执行 -->
<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>
</web-app>
//---------------------------

struts2的架构

struts2的架构图解

struts2的架构

struts2中的6个配置文件

  1. default.properties :用于配置struts 常量。例如:编码
  2. struts-default.xml :struts提供的默认核心配置文件,struts大部分功能都在此配置文件中。
  3. struts-plugin.xml : struts插件核心配置文件,struts整合其他框架或工具。
  • 以上三个文件,用户自己不进行修改。
  1. struts.xml :用户自定义核心配置文件。
      struts.xml 可以配置所有内容,包括:常量,如:
       < constant name=”struts.i18n.encoding” value=”GBK”> < /constant> //配置编码
  2. struts.properties : 用于自定义struts常量配置文件。一般不使用。
  3. web.xml :也可以配置struts常量
    @@@注意:如果配置常量,所有的配置文件存在优先级,编号越大优先级越高。

常见的struts常量

  1. 用于配置项目编码//struts.i18n.encoding=UTF-8
  2. struts整合spring需要配置,默认注释掉了//struts.objectFactory = spring
  3. struts默认使用文件上传解析工具:apache-commons-fileupload
    struts.multipart.parser=jakarta
  4. 临时文件保存位置//struts.multipart.saveDir=
  5. 上传文件最大大小,默认值:2M,单位字节//struts.multipart.maxSize
  6. 用于设置action请求路径扩展名。默认:action或空。多个值使用逗号分隔//struts.action.extension=action,,
    例如:/hello/userAction 或 /hello/userAction.action
  7. 确定是否使用动态方法调用。默认关闭的。//struts.enable.DynamicMethodInvocation = false
  8. 设置开发模式,默认关闭。如果设置true:将提供更多提示信息,自动加载文件。//struts.devMode = false
  9. 自动重新加载国际化资源文件//struts.i18n.reload = true
  10. 自动冲洗加载xml配置文件。例如:struts.xml。但不会自动加载action类。//struts.configuration.xml.reload = true
  11. 设置struts标签主题,默认值:xhtml。取值:simple。//struts.ui.theme=xhtml
    //开发中常用simple。xhtml存在默认布局,开发中布局使用美工提供页面,布局自定义。

struts.xml详解

  • < constant> 用于配置struts常量
    —-name:struts提供固定常量名称。此名称从 default.properties文件获得
    —-value:常量值
    例如:<constant name=”struts.devMode” value=”true”> </constant>

  • <package> struts用于管理所有action类
    —-name:给当前包进行唯一命名,必填项。用于其他包继承的。
    例如:struts-default.xml//<package name=”struts-default” abstract=”true”> //将struts已经完成的功能,作为一个包存在。名称是固定值struts-default。
    —-namespace:action访问路径前缀。
    例如:”/“
    “/hello”
    “/a/b/c”
    “”
    —-extends:用于继承其他包的。
    例如:<package extends=”struts-default”> 当前action继承struts已经完成功能。
    —-abstract:是否抽象,如果设置为true,表示没有具体action实现功能。(了解)

  • <action> 用于配置action类(请求处理类)
    —-name:action名称,用于在请求路径进行访问
    —-class:action类全限定类名
    —-method:用于确定action类中需要执行的方法,默认值:execute

  • <result> 用于配置结果集
    —-name:用于确定action类中,方法的返回值
    —-text:标签体用于设置jsp路径
    —-type:结果集类型
      —- dispatcher:请求转发,从一个action到jsp页面,默认值。
     —- redirect:重定向到jsp页面
     —- redirectAction:重定向到另一个action
     —- stream:以流的方法发送浏览器,用于文件下载。
     —- chain:链,在一次请求中,从一个action到另一个action

  • <result-types> 用于配置结果集类型的,一般不用,但需要看的懂。

  • <interceptors> 用于配置拦截器
    —- <interceptor> 用于注册拦截器的
    —-<interceptor-stack> 拦截器栈,将已经注册的拦截器组合在一起,形成一个栈。
    例如:<interceptor-stack name=”defaultStack”> 声明一个拦截器栈,名称是:defaultStack

  • <default-interceptor-ref> 用于确定当前默认拦截器的
    例如:<default-interceptor-ref name=”defaultStack”/> 将“defaultStack”拦截器栈,声明默认拦截器栈

  • <default-action-ref> 如果访问的action不存在,默认执行的aciton。
    例如:/a/b/c/d/oneAction 此aciton没有配置,默认情况返回action找不到
    !!!如果配置<default-action-ref> 如果存在404时,不显示action找不到,而是指定action

  • <default-class-ref > 用于配置 默认action实现类
    例如:<default-class-ref class=”com.opensymphony.xwork2.ActionSupport” />
    !!!如果编写配置文件时没有声明class属性,及<action name=””> 将执行ActionSupport类。

  • <include> 将多个struts配置文件组合成一个。
    struts.xml 入口配置文件,大家共用的。用于存放通用模块。
     <include file=”struts-user.xml” />
     <include file=”struts-book.xml” />
    提供子配置文件
     struts-user.xml
     struts-book.xml

action访问

动态方法调用

格式: action名称!方法名称.action
例如:bookAction!add.action

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-------- jsp文件配置action入口  ---------->
<a href="${pageContext.request.contextPath}/bookAction!add.action">动态方法调用--add</a> <br/>
<!-------- struts.xml 配置 ---------->
<!-- 注意:默认情况动态方法调用关闭的,必须通过struts常量进行配置。 -->
<!-- 设置动态方法调用 -->
<constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>
<!-- 案例2:动态方法调用 -->
<action name="bookAction" class="cn.itcast.b_demo.BookAction">
<result name="success">/b_demo/success.jsp</result>
</action>
<!-- action类,提供两个方法
位置:/struts_day01/src/cn/itcast/b_demo/BookAction.java
-->
public class BookAction {
public String execute(){
System.out.println("bookAction execute()");
return "success";
}
public String add(){
System.out.println("bookAction add()");
return "success";
}
}

通配符

<action name="userAction"> , action.name可以使用通配符星号(*),在action.class、aciton.method、result.name 处可以使用{n}方式匹配星号。

例如:

  • userAction_* 将可以通过{1}方法获得第一个星号匹配到内容。
    请求路径/userAction_add ,
    <action name="userAction_*" method="{1}">
    {1} 匹配的内容是add,将执行add方法

  • userAction_*_*{1} 匹配第一个星;{2}匹配第二星
    请求路径 /userAction_add_success ,
    <action name="userAction_*_*" method="{1}"><resutl name="{2}">
    {1}匹配add方法,{2}匹配返回值的名称success

  • *_*_* 将采用多个分别描述不同的内容,一次{1}{2}{3}获得
    请求路径 /UserAction_add_success
    <action name="*_*_*" class="cn.itcast.action.{1}" method="{2}"> <result name="{3}">/pages/{3}.jsp

1
2
3
4
5
<!-- 案例3:通配符 -->
<action name="personAction_*" class="cn.itcast.c_demo.PersonAction" method="{1}">
<result name="add">/c_demo/add.jsp</result>
<result name="update">/c_demo/update.jsp</result>
</action>

action访问路径

  当默认访问一个action时,package.namespace = /a/b/c

  • 优先从”/a/b/c”namespace获得相应的aciton,
  • 如果没有获得,将从”/a/b”中获得
  • 如果没有获得,将从”/a”中获得
  • 如果没有获得,将从”/“中获得
  • 如果没有获得,将从””中获得

Action类与servlet API

Action类

实现方法

  1. POJO类

  2. 实现Action接口
      规范接口,将success (表示成功了),none(没有返回值。相当方法void。没有返回值表示没有result,常用与ajax操作。使用response发送数据),error(服务器异常),input(表示用户输入错误),login(表示需要权限)等字符串封装成相应的常量

  1. 继承ActionSupport类
      ActionSupport类已经默认实现了Action(规范接口),Validateable(数据校验),ValidationAware(错误信息),TextPrivider(国际化)等接口

方法定义

1
2
3
public String execute() throws Exception{
return SUCCESS;
}
  1. 必须是public
  2. 建议有返回值,类型必须String
  3. 方法名称自定义
  4. 没有参数
  5. 需要throw Exception
  6. 非静态的
    注意:可以没有返回值,一般情况都有,可以使用return “none” 表示没有返回。

Struts.xml中struts-default包中的默认配置

  • 如果不手动配置Action 默认Action如下配置:
    <default-class-ref class="com.opensymphony.xwork2.ActionSupport" />

  • 如果不配置结果的type属性,默认type属性如下配置:
    <result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/>

访问Servlet api

完全解耦,但只能操作作用域

  • 不使用曾经学习过的servlet知识,可以去操作三个作用域
  • ActionContext 工具类,action上下文对象
    获得实例:ActionContext.getContext()
    -api:
      ac.put(key,value) , 相当于操作request作用域。request.setAttribute(“key”,value)
      ac.getSession().put(key,value) ,相当于操作session作用域。session.setAttribute(“key”,value)
      ac.getApplication().put(key,value) ,相当于操作application作用域。servletContext.setAtt…
1
2
3
4
5
6
7
8
9
10
   @Override
public String execute() throws Exception {
//1 request作用域
ActionContext.getContext().put("ds", "屌丝_request");
//2 session作用域
ActionContext.getContext().getSession().put("ds", "屌丝_session");
//3 application作用域
ActionContext.getContext().getApplication().put("ds", "屌丝——application");
return SUCCESS;
}

操作servlet对象

 ServletActionContext 工具类,获得需要servlet对象

1
2
3
4
5
6
7
8
9
-api:	 
// 获得request对象
HttpServletRequest request = ServletActionContext.getRequest();
//2 获得response对象
HttpServletResponse response = ServletActionContext.getResponse();
//3获得servletContext对象
ServletContext servletContext = ServletActionContext.getServletContext();
//4 获得session
HttpSession session = request.getSession();

通过实现接口,struts注入

  • 需要实现指定的接口,此接口都提供setter,struts在执行action方法之前,将调用setter方法进行赋值。
     ServletRequestAware //获得HttpServletRequest对象
     ServletResponseAware //获得HttpServletResponse对象
     ServletContextAware// 获得ServletContext对象
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public class Demo5Action3 extends ActionSupport implements ServletRequestAware , ServletResponseAware ,ServletContextAware{
    private HttpServletRequest request;
    private HttpServletResponse response;
    private ServletContext servletContext;
    private HttpSession session;
    @Override
    public void setServletRequest(HttpServletRequest request) {
    this.session = request.getSession();
    this.request = request;
    }
    @Override
    public void setServletResponse(HttpServletResponse response) {
    this.response = response;
    }
    @Override
    public void setServletContext(ServletContext context) {
    this.servletContext = context;
    }

结果集与参数控制

结果集类型 Result

1
2
3
4
5
6
7
8
9
10
11
12
    <!-- 案例 6.1 :结果集类型,重定向jsp -->
<action name="demo6Action" class="cn.itcast.f_demo.Demo6Action">
<result type="redirect">/f_demo/type.jsp</result>
</action>
<!-- 案例 6.2 :结果集类型,重定向action -->
<action name="demo6Action2" class="cn.itcast.f_demo.Demo6Action2">
<result type="redirectAction">demo6Action3</result>
</action>
<action name="demo6Action3" class="cn.itcast.f_demo.Demo6Action3">
<!-- 请求转发 ,默认值-->
<result type="dispatcher">/f_demo/type.jsp</result>
</action>

封装请求参数

  1. 属性封装

    • 只要在Action中提供与参数对应的set方法即可自动封装
  2. 自动类型封装

    • struts会自动转换8大基本数据类型和对应包装类.以及Date类型,其中date类型对数据提交格式有要求: yyyy-MM-dd
  3. 容器数据封装

    • 容器:数组、List、Set、Map等
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- Jsp表单 -->
<form action="${pageContext.request.contextPath}/demo2Action" method="post">
Map<br/>
<input type="text" name="userMap['u001'].userName"/> <br/>
<input type="text" name="userMap['u001'].userPwd"/> <br/>
<input type="text" name="userMap['u002'].userName"/> <br/>
<input type="text" name="userMap['u002'].userPwd"/> <br/>
List<br/>
<input type="text" name="userList[0].userName"/> <br/>
<input type="text" name="userList[0].userPwd"/> <br/>
<input type="text" name="userList[1].userName"/> <br/>
<input type="text" name="userList[1].userPwd"/> <br/>
Array<br/>
<%-- request.getParameterValues("username") --%>
<input type="checkbox" name="hobby" value="抽烟"/>
<input type="checkbox" name="hobby" value="喝酒"/>
<input type="checkbox" name="hobby" value="烫头"/> <br/>
<input type="submit" value="提交"/>
</form>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//Action类
public class Demo2Action extends ActionSupport {
//封装到Map ,必须提供getter,所有封装需要使用同一个Map对象
private Map<String,User> userMap;
public void setUserMap(Map<String, User> userMap) {
this.userMap = userMap;
}
public Map<String, User> getUserMap() {
return userMap;
}
//封装到List,必须提供getter,,所有封装需要使用同一个List对象
// * List<User> 和 User[] 等效的
private List<User> userList;
public void setUserList(List<User> userList) {
this.userList = userList;
}
public List<User> getUserList() {
return userList;
}
//数组,不需要提供getter,直接一次性封装数据,使用String[] request.getParameterValues("hobby")
private String[] hobby;
public void setHobby(String[] hobby) {
this.hobby = hobby;
}
@Override
public String execute() throws Exception {
System.out.println(userMap);
System.out.println(userList);
System.out.println(hobby);
System.out.println(Arrays.toString(hobby));
return "none";
}
}

类型转换

默认支持类型转换

  • 字符串 与 指定类型 之间转换
    字符串 转成 指定类型:表单提交,浏览器发送服务器
    指定类型 转成 字符串:标签回显,服务器发送浏览器

  • 指定类型
    8个基本类型、以及包装类
    时间 Date,字符串有格式要求:yyyy-MM-dd 或 yyyy-MM-dd HH:mm:ss
    数组、List、Map

自定义类型转换器

实现类

  • 方案1:实现接口:TypeConverter,有一个方法,但参数过多。
  • 方案2:继承默认实现类:DefaultTypeConverter 。提供简洁方法convertValue(Object , Class)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    convertValue(Object value ,  Class toType)
    #1 表单提交,浏览器发送到服务器。浏览器发送的肯定字符串String,需要转换成指定的类型。例如Date类型
    -参数1:value,表示浏览器发送的数据。类型是String[] ,底层使用request.getParameterValues("...")
    -参数2:toType,表示需要转换的类型,java.uilt.Date类型
    具体操作
    // 如果toType是 Date类型,表示希望将 字符串转成 时间
    if(toType == java.util.Date.class){
    //获得数据
    String[] params = (String[])value;
    //转成成时间
    }

    #2 标签回显,服务器发送 浏览器,类型之前已经从字符串转成时间,现在希望将时间再转换成 字符串。
    -参数1:value,表示服务器已经转成好的时间。类型Date。
    -参数2:toType,表示需要转换的类型,String类型
    具体操作
    if(toType == String.class){
    // 将数据强转时间
    Date date = (Date)value;
    // 格式化
    }

转换器执行流程

注册转换器

  • 局部转换器:只对当前Action类有效。
    —限制:只能对action类的属性进行转化,不能对javabean中属性转换
  • 全局转换器:对所有的Action类有效。

注意:自定义转换器很少使用,一般情况使用默认就可以。多读

  1. 局部转换器的创建
    —位置:Action类同包
    —名称:action类的类名-conversion.properties
    —内容:属性=转换器//如birthday=cn.itcast.XXXCoverter

  2. 全局转换器的创建
    —位置:src
    —名称:xwork-conversion.properties
    —内容:需要转换的类=转换器//如java.util.Data=cn.itcast.XXXConveter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//-----转换器实现-----
public Object convertValue(Object value, Class toType) {
try {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
//1 浏览器发送服务器--表单提交
// * value : String[]
// * toType : Date
if (toType == java.util.Date.class) {
// 1.1 强转,获得数据
String[] paramValues = (String[]) value;
// 1.2 转成时间,返回 , 默认使用第一个
return dateFormat.parse(paramValues[0]);
}
//2 服务器发送浏览器--标签回显
// * value : 服务器已经转换好的数据 Date
// * toType : 需要类型 String
if(toType == String.class){
// 获得时间数据
java.util.Date date = (java.util.Date)value;
// 格式化
return dateFormat.format(date);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
//执行其他
return null;
}

自定义错误信息

  我们发现Struts2打印的类型转换错误信息是英文的,这说明我们需要自定义错误信息。自定义错误信息需要在Action所在目录下创建ActionName.properties文件(与Action同名的properties文件),然后在该文件中给出:invalid.fieldvalue.属性名=错误信息,其中invalid.fieldvalue是固定的。(注:若已使用前端校验,可不必配置此项
  例如:invalid.fieldvalue.person=无法将请求参数转换成Person类型!

校验器与拦截器

校验

数据校验

  • 校验分类
    -浏览器端校验:javaScript,但不安全
    -服务器端校验:struts校验

  • struts校验

    • 手动校验:编写代码,适用于需要与数据库交互
    • xml校验:编写配置文件,通用校验,逻辑简单。如:不能为空,长度为10等

手动校验

  若需要手动校验,必须实现接口:validateable,它提供了一个方法validate()

  1. 校验action中的所有方法
    —实现接口,并实现validate()方法

  2. 校验action中的单个方法
    -实现接口并编写方法 validate方法() , 此处“方法”表示执行的方法名称,首字母大写。
    ——例如:add() 执行前需要校验,必须编写 validateAdd()

注意:先执行“单个方法”校验,再执行“所有方法”校验

  • 提供错误提示,阻止目标方法的执行
    this.addFieldError("", "")给指定的字段设置提示信息,<s:fielderror>jsp显示错误
    this.addActionMessage(aMessage) action提示提示信息,<s:actionmessage/>jsp显示错误
    this.addActionError(anErrorMessage)action错误, <s:actionerror/>jsp显示错误

xml校验

  • 单个方法校验
    —位置:action类同包
    —名称:actionClass-actionName-validation.xml

        &emsp;&emsp;actionClass :表示action类名
        &emsp;&emsp;actionName:表示action访问名称,及<action name="...">
        &emsp;&emsp;validation.xml :固定后缀
    

    —内容:xml必须提供约束(DTD、schema[命名空间])

      &emsp;&emsp;dtd文件位置:xwork-core-2.3.15.3.jar!/xwork-validator-1.0.3.dtd
      &emsp;&emsp;dtd文件内容:
    
    1
    2
    3
    <!DOCTYPE validators PUBLIC
    "-//Apache Struts//XWork Validator 1.0.3//EN"
    "http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">
  • 所有方法校验
    —位置:action类同包
    —名称:actionClass-validation.xml
    —内容:同上

  • struts提供的校验器
    —校验配置文件:xwork-core-2.3.15.3.jar!/com/opensymphony/xwork2/validator/validators/default.xml

输入校验

  1. 什么是输入校验?
      在Action封装了请求参数后,还需要对其进行校验。例如name不能为空,age只能在18~60之间等等!我们一定要搞清楚,输入校验是在类型转换成功之后,才可能执行的。
    校验分类:
      —JavaScript客户端校验(改善用户体验);
      — 服务器端校验(保证安全性),即使用Struts2输入校验。

  2. Struts2输入校验的分类
      编程式校验;
       配置校验:
        XML配置校验(了解);

编程式的输入校验

  覆盖ActionSupport类的validate()方法,在这个方法中完成对参数的校验。validate()方法会在参数封装之后,在execute()方法之前执行。如果validate()方法中向fieldError中添加了错误信息,那么就不会再执行execute()方法,而是跳转到input结果码对应的页面。
拦截器栈执行流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//---jsp文件中---
<s:fielderror />
<form action="<c:url value='/Demo1Action.action'/>" >
用户名:<input type="text" name="username"/><br/>
密 码:<input type="password" name="password"/><br/>
<input type="submit" value="提交"/>
</form>

//---action.java中---
public class Demo1Action extends ActionSupport {
private String username;
private String password;
@Override
public void validate() {
if(username == null || username.trim().length() == 0) {
this.addFieldError("username", "用户名不能为空");
}
if(password == null || password.trim().length() == 0) {
this.addFieldError("password", "密码不能为空");
}
}
public String execute() {
System.out.println(username + ", " + password);
return NONE;
}
}
  • 在validate方法中

    • 使用addFieldError(fieldName, errorMessage)方法存入字段的错误信息.
    • 使用addActionError(anErrorMessage)方法存入action的错误信息
    • 使用addActionMessage(aMessage)方法存入action提示信息
  • 在页面

    • 使用取出字段的错误信息
    • 使用取出action的错误信息
    • 使用取出action提示信息
    • 当然如果你的页面使用了struts2提供的表单标签.表单标签会自动显示字段的错误信息

xml配置方式校验(了解)

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

校验要求

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

校验文件的创建
  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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="UTF-8"?>

<!--
XWork Validators DTD.
Used the following DOCTYPE.

<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0.3//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">
-->


<validators>
<field name="">
<!--
name属性指定要校验的属性,例如<feld name=”username”>,表示要校验的属性是username属性。
-->
<field-validator type="">
<!--
type属性指定校验规则,校验规则由Struts2提供,
Struts2提供的所有校验规则在xwork-core包下的validator包中的default.xml内
-->
<param name=""></param>
<message></message>
</field-validator>
</field>
</validators>

  struts2提供的校验规则(即上文提到的default.xml):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?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属性的非空校验:

1
2
3
4
5
6
7
8
9
10
11
12
13
<validators>
<field name="username">
<field-validator type="requiredString">
<message>用户名不能为空</message>
……
</field-validator>
</field>
....
</validators>
////解释如下////
其中type=”requiredString”是校验规则的名称,它必须对应defualt.xml文件中<validator>元素的name属性值。
requiredString校验规则是校验字符串属性是否长度为0,如果长度为0,它会向fieldError中添加错误信息,
<message>元素指定的是错误信息。

常用的校验规则
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
	required:当属性为null时校验失败;
 requiredstring:当字符串属性长度为0时校验失败:
参数trim:默认值为true,表示去除前后空白后再校验长度。
 stringlength:当字符串长度不在指定范围内时校验失败:
 minLength:指定字符串的最小长度;
 maxLength:指定字符串的最大长度。
 regex:属性不能匹配正则表达式时校验失败:
 expression:指定正则表达式;
 caseSensitive:默认值为true,表示不忽略大小写。
 int:当int属性不在指定范围内校验失败:
 min:最小值;
 max:最大值。
 double:当double属性不在指定范围内校验失败:
 min:最小值;
 max:最大值。
 fieldexpression:属性必须是OGNL表达式:
 expression:用来校验的ONGL表达式,例如pass == repass,其中pass和repass是两个属性名称,
当这两个属性的值相等时校验通过,否则失败。
 email:属性必须是合法的邮件地址;
 url:属性必须是合法的网址;
 date:属性必须是合法的日期格式。

校验进阶

跳过指定的校验方法

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

指定校验某个方法
  • public的,没有返回值,没有参数(public void xxx());
  • 方法名称前缀为validate(public void validateXxx());
  • 方法名后缀必须与请求处理方法名相同,例如请求处理方法为login(),那么它的私有校验方法为:public validateLogin()。
    注意,私有校验方法会在公共校验方法(validate())之前被调用。如果你要为execute()提供私有校验方法,那么这个方法名为validateExecute()。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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;
}
}

拦截器

  struts提供拦截器,对action类进行增强的。struts已经实现多个拦截器,完成不同的功能。
例如:文件上传、数据校验、类型转换、参数封装等

默认拦截器栈

  struts-default.xml提供struts所有拦截器,也提供默认拦截器栈

  • <default-interceptor-ref name="defaultStack"/> 所有的action默认使用那个拦截器栈
  • <interceptor-stack name="defaultStack">声明一个拦截器栈,名称为“defaultStack”,通常称为:默认拦截器栈
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<interceptor-stack name="defaultStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="servletConfig"/>
<!-- 用于给action类注入Servlet api。
例如:action类实现ServletRequestAware就可以被struts框架注入HttpServletRequest对象
-->
<interceptor-ref name="i18n"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="scopedModelDriven"/>
<interceptor-ref name="modelDriven"/>
<!-- 调用action类的getModel()方法,获得javabean实例,如果没为null,将交予struts。 -->
<interceptor-ref name="fileUpload"/>
<!-- struts默认支持文件上传 -->
<interceptor-ref name="checkbox"/>
<interceptor-ref name="multiselect"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="actionMappingParams"/>
<interceptor-ref name="params">
<!-- 给action类进行数据封装。如果使用ModelDriven,就给javabean封装数据 -->
<param name="excludeParams">dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*</param>
</interceptor-ref>
<interceptor-ref name="conversionError"/>
<!-- 将转换错误添加到action类的错误提示信息中。
将执行 action.addFieldError("属性","错误提示");
-->
<interceptor-ref name="validation">
<!-- 将执行action所有校验。先执行注解校验,再执行单个方法校验,最后所有方法的校验 -->
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<!-- 从action类获得添加的错误信息,如果没有发行。如果有返回“input”
方式1:默认情况,如果存在错误,返回值“input”
方式2:实现接口ValidationWorkflowAware,修改整个action的错误返回结果集名称,将执行方法 getInputResultName()
方式3:通过@InputConfig注解,给指定的方法配置出现错误时,返回结果result的名称。
-->
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="debugging"/>
</interceptor-stack>

关于workflow拦截器的补充:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//方法2:实现ValidationWorkflowAware接口
public class Demo8Action extends ActionSupport implements ValidationWorkflowAware{
@Override
public String getInputResultName(){
return "xxx";//出现错误不再返回input,而是xxx,所有的方法都使用
}
}

//方法3:通过@InputConfig注解
@InputConfig(resultName="loginInput") //如果登录出现异常,将返回不是input,而是loginInput
public String login(){
return SUCCESS;
}

@InputConfig(resultName="registerInput") //如果注册出现异常,将返回不是input,而是registerInput
public String register(){
return SUCCESS;
}

自定义拦截器

  • 实现接口:com.opensymphony.xwork2.interceptor.Interceptor

    1
    2
    3
    4
    5
    6
    7
    8
    9
    //初始化方法
    public void init() { }
    //拦截方法
    public abstract String intercept(ActionInvocation invocation) throws Exception{
    invocation.getAction() 获得当前action类实例
    invacation.invoke() 放行
    }
    //销毁方法
    public void destroy() { }
  • 继承父类:com.opensymphony.xwork2.interceptor.MethodFilterInterceptor
    在使用自定义拦截器,可以对指定的方法进行操作(哪些方法不拦截,哪些必须拦截)
    设置属性includeMethods,确定哪些方法进行拦截
    设置属性excludeMethods,确定哪些方法不进行拦截

  • <default-interceptor-ref name="xxx">将指定的拦截器,声明成默认的。
    注意:如果使用自定义xxx,“defaultStack”将被覆盖。
    注意:拦截器只拦截action类,不拦截jsp文件。

使用拦截器进行表单参数校验

  • 校验流程:
      Validation拦截器负责判断Action是否实现Validateable接口.如果实现就调用validate方法.
      workflow拦截器判断Action是否实现ValidationAware接口. 如果实现就会判断Action中是否包含错误信息.如果包含错误信息, 停止拦截器递归调用,返回结果到名为input的结果页面中.

  • 编程式校验
      实现Validateable接口.接口中只有一个方法validate方法.我们可以在该方法中添加错误信息.
      添加错误信息需要实现ValidationAware接口.该接口中有一些判断是否含有错误消息.获得错误消息.添加错误消息的方法.

拦截器的注册

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

  1. 在package中声明拦截器
  2. 在action中引用拦截器
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    <package name="s8" namespace="/" extends="struts-default">
    <interceptors>
    <!--
    在<aciton>元素中引用拦截器的顺序决定了拦截器的执行顺序,
    例中会先执行defaultStack中的所有拦截器,再执行MyInterceptor拦截器
    -->
    <interceptor name="MyInterceptor" class="cn.itcast.interceptor.MyInterceptor" />
    <interceptor-stack name="myStack">
    <interceptor-ref name="defaultStack" />
    <!--
    struts2中,一旦为Action指定了拦截器,那么就不会再为这个Action执行默认拦截器了
    以我们需要在这个<action>元素中再引用defaultStack拦截器栈。
    -->
    <interceptor-ref name="MyInterceptor" />
    </interceptor-stack>
    </interceptors>
    <default-interceptor-ref name="myStack" />
    <action name="LoginAction">
    <result>/index.jsp</result>
    <result name="input">/login.jsp</result>
    <!--若没有将创建的拦截器指定为默认拦截器,需要加入以下语句:
    <interceptor-ref name="defaultStack" />
    <interceptor-ref name="MyInterceptor" />
    -->
    </action>
    </package>

OGNL与struts标签

未完待续

感谢你的阅读!