Struts2高级技术

本文深入解析Struts2框架的核心组件OGNL表达式语言,详细介绍其在数据访问、静态方法调用、集合操作等方面的功能。同时,覆盖Struts2标签库的使用,包括数据标签、控制标签和表单标签的应用技巧。此外,探讨了拦截器的原理和配置方法,以及Struts2配置文件的编写规范。

1、OGNL表达式语言

1.1 认识OGNL

OGNL(Object Graph Navigation Language)是一种强大的表达式语言,它能够自动导航对象的结构并访问和设置对象数据。在OGNL表达式中,它的核心对象为OGNL上下文。OGNL上下文相当于一个Map容器,在Map容器的Value中可以保存任何类型的数据(对象、数组等),通过OGNL上下文可以对容器中的对象进行导航。

OGNL上下文是OGNL的核心,在OGNL上下文中可以存放多个对象。通常情况下,标准的OGNL为设定一个根。

例如,OGNL上下文中包含两个对象,分别为User对象与Book对象,其中User对象为OGNL上下文的根。如果获取User对象与Book对象中的name属性,可以使用以下表达式:

#user.name
#book.name

上述代码就相当于调用了User对象与Book对象的getName()方法,由于User对象为OGNL上下文的根,所以可以将表达式中的“#”去除。

user.name

OGNL表达式语言是非常强大的表达式语言,它要比JSP中的EL表达更加强大,OGNL表达式语言特点如下:

  1. 支持对象方法的调用。
  2. 支持静态方法的调用。
  3. 支持变量的赋值。
  4. 可以操作集合数据。

1.2 Struts2框架中的OGNL

Struts2框架在OGNL的基础上增强,提高了Struts2对数据的访问能力,在Struts2框架之中,OGNL上下文作用于Struts2中的ActionContext对象,ActionContext对象是Struts2框架中的一个核心对象,它的结构如下图所示。

值栈符合与栈的基本特征,对象在栈中的存放是后进先出,它由于Struts2 API中的com.opensymphony.xwork2.ognl.OgnlValueStack类进行实现。在ActionContext中包含了多个对象,值栈是OGNL上下文的根,值栈中的对象可以直接调用。

在Struts2框架中,当接收一个Action请求时,Struts2框架会创建ActionContext对象并实例化值栈等对象。由于OGNL上下文用于ActionContext对象,所以,通过OGNL表达式可以获取ActionContext中的所有对象。

(1)获取值栈中的对象

由于OGNL上下文中的根可以直接获取,所以,值栈中的对象可以直接进行获取,原因是Struts2框架中的值栈就是OGNL上下文的根。其取值方法如下:

${user.name}

(2)获取application中的对象

在ActionContext对象中包含application,由于它并不是OGNL上下文的根,其取值方法如下:

#application.name

#application['name']

上述代码相当于调用:application.getAttribute("name")方法。

(3)获取request中的对象

与application相同,request也不是OGNL上下文的根,其取值方法如下:

#request.name

#request['name']

上述代码相当于调用:request.getAttribute("name")方法。

(4)获取session中的对象

session也不是OGNL上下文的根,其取值方法如下:

#session.name

#session['name']

上述代码相当于调用:session.getAttribute("name")方法。

(5)获取parameters中的值

在Struts2框架中,通过OGNL获取请求参数,其取值方法如下:

#parameters.name

#parameters['name']

上述代码相当于调用:request.getParameter("name")方法。

(6)获取attr中的值

如果不指定范围,可以使用attr来获取属性值,其搜索范围为按page、request、session、application的次序进行搜索,其取值方法如下:

#attr.name

#attr['name']

1.3 操作普通的属性与方法

Struts2框架在OGNL的基础上增强,在Struts2框架中使用OGNL表达式语言需要借助于Struts2标签进行输出。

获取User对象中的name属性,在JSP页面中可以使用以下代码进行获取。

<s:property value="user.id"/>

在OGNL表达式中,获取属性的方法主要有两种方法,除了上面介绍的方法外,也可以通过下面的代码进行获取。

<s:property value="user['id']"/>

OGNL不仅支持属性的调用,同样也支持方法的调用,在JSP页面中,可以使用如下代码,调用User类中的方法。

<s:property value="user.say()"/>

【示例】通过OGNL操作普通的属性与方法。

(1)创建Java Web项目,将Struts2的相关包引用到项目中,并在web.xml文件中注册Struts2提供的StrutsPrepareAndExecuteFilter过滤器。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!-- Struts2过滤器 -->
    <filter>
        <!-- 过滤器名称 -->
        <filter-name>struts2</filter-name>
        <!-- 过滤器类 -->
        <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
    </filter>
    <!-- Struts2过滤器映射 -->
    <filter-mapping>
        <!-- 过滤器名称 -->
        <filter-name>struts2</filter-name>
        <!-- 过滤器映射 -->
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

(2)创建名称为UserInfo的用户信息类。

package com.pjb.entity;

/**
 * 用户信息实体类
 * @author pan_junbiao
 **/
public class UserInfo
{
    private String userName;  //用户名称
    private String blogUrl;   //博客地址

    public String getUserName()
    {
        return userName;
    }

    public void setUserName(String userName)
    {
        this.userName = userName;
    }

    public String getBlogUrl()
    {
        return blogUrl;
    }

    public void setBlogUrl(String blogUrl)
    {
        this.blogUrl = blogUrl;
    }

    public String say()
    {
        String msg = "您好,欢迎访问 pan_junbiao的博客!";
        return msg;
    }
}

(3)创建名称为OGNLAction的类,该类继承ActionSupport类,是一个Action对象。

package com.pjb.action;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.pjb.entity.UserInfo;
import java.util.Map;

/**
 * OGNL测试Action类
 * @author pan_junbiao
 **/
public class OGNLAction extends ActionSupport
{
    private static final long serialVersionUID = 1L;
    private String name;          //普通属性name
    private UserInfo userInfo;    //用户信息对象
    Map<String,Object> request;   //Map类型的request

    //构造方法
    @SuppressWarnings("unchecked")
    public OGNLAction()
    {
        userInfo = new UserInfo();
        userInfo.setUserName("pan_junbiao的博客");
        userInfo.setBlogUrl("https://blog.youkuaiyun.com/pan_junbiao");
        name = "KevinPan";
        request = (Map<String,Object>)ActionContext.getContext().get("request");
    }

    //请求处理方法
    @Override
    public String execute() throws Exception
    {
        request.put("info","request测试");
        return SUCCESS;
    }

    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }

    public UserInfo getUserInfo()
    {
        return userInfo;
    }

    public void setUserInfo(UserInfo userInfo)
    {
        this.userInfo = userInfo;
    }

    public Map<String, Object> getRequest()
    {
        return request;
    }

    public void setRequest(Map<String, Object> request)
    {
        this.request = request;
    }
}

(4)在Web项目的源码文件夹(src目录)中,创建名称为struts.xml的配置文件,在该文件中配置OGNLAction对象。

<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
        "http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
    <package name="myPackage" extends="struts-default" namespace="/">
        <!-- 定义action -->
        <action name="ognlAction" class="com.pjb.action.OGNLAction">
            <!-- 结果映射 -->
            <result name="success">/success.jsp</result>
        </action>
    </package>
</struts>

(5)创建程序主页index.jsp。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>主页</title>
    <meta name="author" content="pan_junbiao的博客">
</head>
<body>
    <a href="ognlAction.action">OGNL测试</a>
    <hr>
    <b>您好,欢迎访问 pan_junbiao的博客</b>
    <br>
    <b>博客地址:https://blog.youkuaiyun.com/pan_junbiao</b>
<br>
</body>
</html>

(6)创建OGNLAction处理请求后返回页面success.jsp,在该页面中使用OGNL表达式输出OGNLAction对象中设置的属性值。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
    <title>成功页面</title>
    <meta name="author" content="pan_junbiao的博客">
</head>
<body>
    <h1>操作普通属性</h1>
    属性name值:<s:property value="name"/><br>
    <hr>
    用户名称:<s:property value="userInfo.userName"/><br>
    博客地址:<s:property value="userInfo['blogUrl']"/><br>
    say()方法:<s:property value="userInfo.say()"/><br>
    <hr>
    request中的info值:<s:property value="#request['info']"/>
</body>
</html>

执行结果:

(1)程序主页index.jsp

(2)OGNLAction处理请求后返回页面success.jsp

1.4 访问静态方法与属性

Struts2框架中的OGNL表达式支持对象的静态方法与静态属性的调用,它类似于Java语言之中的静态引入,需要使用字符“@”进行标注,其调用静态属性的方法如下:

@com.lyq.bean.Bean@NAME

上述代码就相当于调用了Bean.NAME静态属性,与调用静态属性的方法相同,调用静态方法方式如:

@com.lyq.bean.Bean@greeting()

在Struts2框架中,提供了一个是否允许OGNL调用静态方法的常量,它的名称为struts.ognl.allowStaticMethodAccess,其默认属性为false。也就是说,在默认情况下,Struts2不允许OGNL调用静态方法,所以,如果开发中需要使用OGNL调用静态方法,必须将此常量的值设置为true。更改这个常量的值,可以在struts.xml配置文件中加入如下配置信息:

<!-- 允许OGNL表达式调用静态方法 -->
<constant name="struts.ognl.allowStaticMethodAccess" value="true"/>

说明:关于Struts2的常量,可以通过查看Struts2核心jar包中的org.apache.struts2目录中的default.properties属性文件,得知其配置的默认值。

1.5 访问数组

数组是一组元素的集合,每一个元素都有一个下标(索引)值,访问其中的一个元素通过其下标进行访问。在OGNL表达式中,访问数组中的数据与在Java之中的访问方法相似,也是通过下标对其进行访问,如定义了一个名称为arr的数组,使用OGNL表达式的访问方法如下:

arr[0]

OGNL不仅可以访问到数组中的每一个元素,还可以获取数组的长度,其获取方法如下:

arr.length

1.6 访问List、Set、Map集合

集合是Java开发之中经常用到的对象,OGNL对集合数据的访问也同样提供了方法,由于不同的集合对象,其数据存储结构不同,所以,它的访问方式也存在差异。

(1)List集合是JDK API封装的一个有序集合,使用OGNL访问List集合,可以通过下标值对其进行访问,其访问方式如下:

list[0]

(2)Set集合是JDK API封装的一个无序集合对象,由于对象在Set集合中的存储方式是无序的,所以,不能通过下标值的方式访问Set集合中的数据。

(3)Map集合也是JDK API是封装一个集合对象,它的数组存储结构以Key、Value的方式进行存储,使用OGNL访问Map集合,可以通过获取Key值来访问Value,其访问方式如下:

map.key

map[‘key’]

Map对象是包含一组Key与Value的集合,针对Map集合对象OGNL提供了获取Map对象所有Key与Value的方法,它返回Key或Value的数组。其获取方式如下:

map.keys    //获取所有Key
map.values  //获取所有Value

在OGNL表达式中,针对集合对象还提供了两个通用的方法,用于判断集合中的元素是否为空和获取集合的长度。

collection.isEmpty   //判断集合元素是否为空
collection.size()    //获取集合的长度

1.7 投影与选择

OGNL表达式对数据以及对象的操作功能是十分强大的,对于集合类型的对象,OGNL还支持投影操作与选择操作,可以轻松的获取集合中的某一列数据的集合,也可以通过条件轻松的过滤掉集合中的某些行。

(1)投影是对集合中列的操作,所谓投影,就是指将集合中的某一列数据都抽取出来形成一个集合,这一列数据就是原集合中的投影。如在一个集合中包含多个学生对象,获取这个集合中的所有学生的姓名,就是投影操作,在OGNL表达式中通过如下代码进行实现:

list.{name}

(2)选择是对集合中行的操作,所谓选择,就是通过一定的条件获取集合中满足这一条件的数据,所获取的行就是对集合中数据的选择操作。如在一个集合中包含多个学生对象,获取这个集合中学生年龄大于10的所有学生,就是选择操作,在OGNL表达式中通过如下代码进行实现:

list.{?#this.age > 10}

注意:上述代码针对的是值栈中的对象,如果是非值栈中的对象,还需要加上“#”,如:

#list.{?#this.age > 10}

在Java语言中包含this关键字,OGNL中同样包含了此关键字,与Java语言相同,它代表当前对象。上述代码中“?”代表获取满足这一条件的所有对象。

对于选择操作,在OGNL表达式中主要提供了3个操作符号:

符号说明
?获取满足指定条件的所有元素。
^获取满足指定条件的所有元素中的第一个元素。
$获取满足指定条件的所有元素中的最后一个元素。

 

2、Struts2标签库

在使用Struts2标签库之前,必须在JSP页面的顶部使用<%@ taglib%>指令定义引用的标签库和访问前缀。

使用Struts2标签库的taglib指令格式如下:

<%@ taglib prefix="s" uri="/struts-tags" %>

2.1 数据标签的应用

2.1.1 property标签

property标签是一个非常常用的标签,它的作用是获取数据值,并将数据值直接输出到页面之中。

property标签的属性及说明:

名称是否必须名称是否必须
default可选escapeJavaScript可选
escape可选value可选

2.1.2 set标签

set标签用于定义一个变量,通过此标签可以为所定义的变量赋值,及设置变量的作用域(application、request、session)。在默认情况下,通过set标签所定义的变量被放置到值栈中。

set标签的属性及说明:

名称是否必须类型说明
scope可选String设置变量的作用域,它的值可以是application、request、session、page或action,默认值为action。
value可选String设置变量的值。
var可选String定义变量的名称。

set标签的使用方法非常简单,其使用方式如下:

<s:set var="userName" value="pan_junbiao的博客" scope="request"></s:set>
<s:property default="没有数据" value="#request.userName" />

2.1.3 a标签

a标签用于构建一个超链接,其最终的构建效果将形成一个HTML中的超链接。

a标签的属性及说明:

名称是否必须类型说明
action可选String将超链接的地址指向action。
href可选String超链接地址。
id可选String设置HTML中的属性名称。
method可选String如果超链接的地址指向action,method同时可以为action声明所调用的方法。
namespace可选String如果超链接的地址指向action,namespace可以为action声明名称空间。

2.1.4 param标签

param标签主要用于对参数赋值,它可以当做其它标签的子标签。

param标签的属性及说明:

名称是否必须类型说明
name可选String设置参数名称。
value可选Object设置参数值。

2.1.5 action标签

action标签是一个非常常用的标签,它用于执行一个Action请求。当在一个JSP页面中通过action标签执行Action请求时,可以将Action的返回结果输出到当前页面中,也可以不输出。

action标签的属性及说明:

名称是否必须类型说明
executeResult可选Boolean是否使Action返回到执行结果,默认值为false。
flush可选Boolean输出结果是否刷新,默认值为false。
ignoreContextParams可选Boolean是否将页面请求参数传入被调用的Action,默认值为false。
name必须StringAction对象所映射的名称,也就是struts.xml中配置的名称。
namespace可选String指定名称空间的名称。
var可选String引用此action的名称。

2.1.6 push标签

push标签用于将对象或值压入到值栈之中并放置到顶部。因为值栈中的对象是可以直接调用的,所以,push标签主要起到一种简化操作的作用。它的属性只有一个,名称为value,用于声明压入值栈中的对象,其使用方法如下:

<s:push value="#request.student"></s:push>

2.1.7 date标签

date标签用于格式化日期时间,可以通过指定的格式化样式对日期时间值进行格式化。

date标签的属性及说明:

名称是否必须类型说明
format可选String设置格式化日期的样式。
name必须String日期值。
nice可选Boolean是否输出给定日期与当前日期之间的时差,默认值为false,不输出时差。
var可选String格式化时间的名称变量,通过该变量可以对其进行引用。

2.1.8 include标签

Struts2框架中的include标签的作用类似于JSP中的<include>动作标签,也用于包含一个页面,但Struts2框架中的include标签的功能更加强大,它可以向目标页面中通过param标签传递请求参数。

include标签只有一个value属性,该属性是必选的属性,用于包含一个JSP页面或Servlet,其使用方法如下:

<s:include value="index.jsp"/>

2.1.9 url标签

在Struts2框架之中,一个Action对象的URL映射地址包含名称空间、调用方法等多个参数,这样的URL可以直接进行编写,也可以使用Struts2框架提供的url标签自动生成URL地址。

url标签的属性及说明:

名称是否必须类型说明
action可选StringAction对象映射URL,也就是Action对象的访问地址。
anchor可选String此URL的锚点。
encode可选Boolean是否对参数进行编码,默认值为true。
escapeAmp可选String是否将“&”转义成为“&amp”。
forceAddSchemeHostAndPort可选Boolean是否添加URL的主机地址及端口,默认值为false。
includeContext可选Boolean生成的URL是否包含上下文路径,默认值为true。
includeParams可选String是否包含可选参数,它的可选值为none、get、all,默认值为none。
method可选String指定请求Action对象所调用的方法。
namespace可选String指定请求Action对象映射地址的名称空间。
scheme可选String指定生成URL所使用的协议。
value可选String指定生成URL的地址值。
var可选String定义生成URL变量名称,可以通过此名称引用URL。

2.2 控制标签的应用

2.2.1 if标签

if标签是Struts2框架提供的一个流程控制标签,针对某一逻辑的多种条件进行处理,通常表现为“如果满足某种条件,就进行某种处理,否则就进行另一种处理”,与Java语言相同,Struts2框架的标签同样支持if、else if、else的语句判断,其使用方法如下:

<s:if test="表达式(布尔值)">
    输出结果...
</s:if>
<s:elseif test="表达式(布尔值)">
    输出结果...
</s:elseif>
<s:else>
    输出结果...
</s:else>

<s:if>标签与<s:elseif>标签都有一个名称为test的属性,该属性用于设置标签的判断条件,它的值是一个布尔类型的条件表达式。

2.2.2 iterator标签

iterator标签是Struts2提供的一个迭代数据的标签,它可以根据循环条件,遍历数组和集合类中的所有或部分数据,也可以指定迭代数据的起始位置、步长以及终止位置来迭代集合或数组中的部分数据。

iterator标签的属性及说明:

名称是否必须类型说明
begin可选Integer指定迭代数组或集合的起始位置,默认值为0。
end可选Integer指定迭代数组或集合的结束位置,默认值为数组或集合的长度。
status可选String迭代过程中的状态。
step可选Integer设置迭代的步长,如果指定此值,那么,每一次迭代后,索引值将在原索引值的基础上增加step值,默认值为1。
value可选String指定迭代的集合或数组对象。
var可选String设置迭代元素的变量,如果指定此属性,那么所迭代的变量将压入到值栈中。

iterator标签的属性中,status属性用于获取迭代过程中的状态信息。在Struts2框架的内部结构中,status属性实质上是获取了Struts2封装的一个迭代状态的对象,该对象为org.apache.struts2.views.jsp.IteratorStatus,通过该对象可以获取迭代过程中的如下信息:

信息说明
元素数IteratorStatus对象提供了getCount()方法来获取迭代集合或数组的元素数,如果status属性设置为st,那么就可以通过 st.count 获取元素数。
是否为第一个元素IteratorStatus对象提供了isFirst()方法来判断当前元素是否为第一个元素,如果status属性设置为st,那么就可以通过 st.first 判断当前元素是否为第一个元素。
是否为最后一个元素IteratorStatus对象提供了isLast()方法来判断当前元素是否为最后一个元素,如果status属性设置为st,那么就可以通过 st.lase 判断当前元素是否为最后一个元素。
当前索引值IteratorStatus对象提供了getIndex()方法来获取迭代集合或数组的当前索引值,如果status属性设置为st,那么就可以通过 st.index 获取当前索引值。
索引值是否为偶数IteratorStatus对象提供了isEven()方法来判断当前索引值是否为偶数,如果status属性设置为st,那么就可以通过 st.even 判断当前索引值是否为偶数。
索引值是否为奇数IteratorStatus对象提供了isOdd()方法来判断当前索引值是否为奇数,如果status属性设置为st,那么就可以通过 st.odd 判断当前索引值是否为奇数。

2.3 表单标签的应用

2.3.1 常用的表单标签与通用属性

在Struts2框架中,Struts2提供了一套表单标签,这些标签用于生成表单以及表单中的元素,如文本框、密码框、选择框等。由于这些标签由Struts2提供,它们能够与Struts2 API进行很好的交互。

常用的表单标签及说明如下:

名称说明
form标签用于生成一个form表单
hidden标签 用于生成一个HTML中的隐藏表单元素,相当于使用了HTML代码:<input type="hidden">
textfield标签用于生成一个HTML中的文本框元素,相当于使用了HTML代码:<input type="text">
password标签用于生成一个HTML中的密码框元素,相当于使用了HTML代码:<input type="password">
radio标签用于生成一个HTML中的单选按钮元素,相当于使用了HTML代码:<input type="radio">
select标签用于生成一个HTML中的下拉列表元素,相当于使用了HTML代码:<select><option></option></select>
textarea标签用于生成一个HTML中的文本域元素,相当于使用了HTML代码:<textarea></textarea>
checkbox标签用于生成一个HTML中的选择框元素,相当于使用了HTML代码:<input type="checkbox">
checkboxlist标签用于生成一个或多个HTML中的选择框元素,相当于使用了HTML代码:<input type="text">
submit标签用于生成一个HTML中的提交按钮元素,相当于使用了HTML代码:<input type="submit">
reset标签用于生成一个HTML中的重置按钮元素,相当于使用了HTML代码:<input type="reset">

在HTML语言中,表单中的元素拥有一些通用的属性,例如id属性、name属性以及JavaScript中的事件,这些元素在HTML表单元素中几乎都会存在。与HTML中相同,Struts2提供的表单标签也存在通用的属性,而且这些属性比较多。

通用的属性及说明:

名称说明
name指定表单元素的name属性。
title指定表单元素的title属性。
cssStyle指定表单元素的style属性。
cssClass指定表单元素的class属性。
required用于在lable上添加“*”,它的值为布尔类型,如果为true,则添加“*”。
disabled指定表单元素的disable属性。
value指定表单元素的value属性。
labelposition用于指定表单元素lable的位置,它的默认值为left
requiredPosition用于指定表单元素lable上添加“*”的位置,默认值为right。

2.3.2 更改默认的主题样式

Struts2框架提供的表单标签应用了内置的主题样式,主题是Struts2框架提供一项功能,主题样式的设置可以应用于Struts2框架中的表单标签与UI标签上。默认情况下,Struts2提供了以下4中主题样式。

主题说明
simplesimple主题的功能较弱,它只提供了简单的HTML输出。
xhtmlxhtml主题是在simple上的扩展,它提供了简单的布局样式,可以将元素应用到表格布局中,同时,也提供了lable的支持。Struts2应用的默认主题。
css_xhtmlcss_xhtml主题是在xhtml主题基础上进行扩展,它在功能上强化了xhtml主题在CSS上的样式控制。
ajaxajax主题是在css_xhtml主题上扩展,它在功能上强化了css_xhtml主题在Ajax方面的应用。

说明:Struts2框架的主题样式是基于模板语言进行设计,它要求程序员了解模板语言,目前,它的应用并不是很广泛。

修改主题样式的方法,可以通过表单元素上的theme属性进行修改。

【示例】使用Struts2框架的表单标签,并修改表单主题样式为css_xhtml。

<h2>用户注册</h2>
<s:form action="userAction" method="POST" theme="css_xhtml">
    <s:textfield name="name" label="用户名"></s:textfield>
    <s:password name="password" label="密码"></s:password>
    <s:radio name="sex" list="#{1:'男',0:'女'}" label="性别"></s:radio>
    <s:submit value="注册"></s:submit>
</s:form>

 

3、拦截器的使用

3.1 了解拦截器

拦截器(Interceptor)是Struts2框架中一个非常重要的核心对象,它可以动态增强Action对象的功能,在Struts2框架中,很多重要的功能都是通过拦截器进行实现的。比如:在使用Struts2框架中,我们发现了Struts2与Servlet API进行解耦,Action对请求的处理不依赖于Servlet API,但Struts2的Action却具有着更加强大的请求处理功能,那么,这个功能的实现就是拦截器对Action的增强,可见拦截器的重要性。此外,Struts2框架中的表单重负提交、对象类型转换、文件上传、还有ModelDriven的操作,都离不开拦截器幕后的操作,Struts2的拦截器的处理机制是Struts2框架的核心。

3.2 拦截器API

在Struts2 API中,存在一个名称为“com.opensymphony.xwork2.interceptor”的包,此包中的对象是Struts2内置的一些拦截器对象,它们都具有不同的功能。在这些对象中,Interceptor接口是Struts2框架中定义的拦截器对象,其它的拦截器都直接或间接的实现于此接口。
在拦截器Interceptor中,包含了三个方法,其代码如下:

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

Interceptor接口中的方法说明:

方法说明
destroy()方法指示拦截器的生命周期结束,它在拦截器被销毁前调用,用于释放拦截器在初始化时所占用的资源。
init()方法用于对拦截器进行初始化操作,该方法在拦截器被实例化后、intercept()方法执行前调用。
intercept()方法拦截器的主要方法,用于执行Action对象中的请求处理方法以及在Action的前后进行操作,动态地增强Action的功能。

说明:只有调用了intercept()方法的invocation参数的invoke()方法,才可以执行Action对象中的请求处理方法。

Struts2提供了拦截器对象Interceptor是一个接口,如果要通过该接口创建拦截器对象,就需要实现Interceptor接口提供的3个方法。但实际开发中用到的方法主要是intercept()方法,如果用不到init()与destroy()方法也必须对其进行空实现,这种创建拦截器的方法似乎有些不太方便。

为了简化程序开发,创建拦截器对象也可以通过Struts2 API中的AbstractInterceptor对象进行创建,它对Interceptor接口进行了实现。在创建拦截器时可以通过继承AbstractInterceptor对象进行创建,在继承AbstractInterceptor对象后,创建拦截器的方法就更加简单。除了重写必需的intercept()方法外,如果没有用到init()与destroy()方法,就不需要对着两个方法进行实现了。

在Struts2 API中还提供了MethodFilterInterceptor对象,它继承了AbstractInterceptor对象。在实际开发中推荐使用MethodFilterInterceptor对象,当拦截器继承MethodFilterInterceptor对象后,需要重写doIntercept()方法。

3.3 使用拦截器

在Struts2框架中,如果创建了一个拦截器对象,需要对拦截器进行配置才可以应用到Action对象上,其中拦截器的创建比较简单,可以通过继承AbstractInterceptor对象进行创建,而它的配置使用<interceptor-ref>标签进行配置,下面就通过一个实例来学习一下。

【示例】使用拦截器,判断用户是否登录。

(1)创建类名为AuthorizationInterceptor的授权验证拦截器,该类继承MethodFilterInterceptor对象。

package com.pjb.interceptor;

import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;

import java.util.Map;

/**
 * 授权验证拦截器
 * @author pan_junbiao
 **/
public class AuthorizationInterceptor extends MethodFilterInterceptor
{
    @Override
    public String doIntercept(ActionInvocation var1) throws Exception
    {
        //获取ActionContext上下文对象
        ActionContext context = var1.getInvocationContext();
        //获取Session对象
        Map<String, Object> session = context.getSession();
        //从Session对象中获取用户信息
        String user = (String) session.get("user");
        //判断是否登录
        if(user!=null && user.length()>0)
        {
            //已登录,程序继续执行
            return var1.invoke();
        }
        else
        {
            //未登录,转至登录页面
            return Action.LOGIN;
        }
    }
}

(2)在struts.xml配置文件中,添加拦截器的相关配置信息。

<!-- 声明包 -->
<package name="myPackage" extends="struts-default" >
    <!-- 拦截器的配置 包括拦截器+拦截器栈 -->
    <interceptors>
        <interceptor name="authorizationInterceptor" class="com.pjb.interceptor.AuthorizationInterceptor"></interceptor>
        <!-- 如果想让自定义的拦截器起作用,就必须从新配置拦截器栈,并加上原先默认的拦截器栈 -->
        <interceptor-stack name="MyInterceptorStack">
           <interceptor-ref name="defaultStack"></interceptor-ref>
            <interceptor-ref name="authorizationInterceptor">
                <!-- 拦截器排除的方法 -->
                <param name="excludeMethods">login</param>
            </interceptor-ref>
        </interceptor-stack>
    </interceptors>
    <!-- 使用拦截器:将自定义的拦截器栈应用到项目上,项目上所有的请求都会经过该拦截器栈 -->
    <default-interceptor-ref name="MyInterceptorStack"></default-interceptor-ref>

    <!-- 全局结果视图 -->
    <global-results>
        <!-- 当返回login视图名时,转入/login.jsp页面 -->
        <result name="login" type="redirect">/login.jsp</result>
    </global-results>
</package>

注意:拦截器的配置信息必须放置在<action>节点的上面。

配置说明:

excludeMethods属性:表示排除指定的方法,即不对标记为excludeMethods的方法进行拦截,

includeMethods属性:表示包含指定的方法,即对标记为includeMethods的方法进行拦截,

Struts2拦截器属性excludeMethods、includeMethods配置无效的原因:

拦截器如果通过实现Interceptor接口或继承AbstractInterceptor对象,属性excludeMethods、includeMethods配置无效。拦截器如果通过继承MethodFilterInterceptor类的话,属性excludeMethods、includeMethods配置有效。

说明:Struts2提供了拦截器对象与Servlet过滤器(Filter)区别是挺大的。拦截器只值针对Action对象,而无法拦截JSP页面等文件;Servlet过滤器(Filter)可以过滤JSP、Javasctipt、CSS等请求文件。所以上述的示例是无法很好的满足Java Web项目的授权判断功能的。

 

4、Struts2的配置文件

4.1 手动验证的实现

在Struts2的API中,ActionSupport类对Validateable接口进行了实现,但对validate()方法的实现却是一个空实现,通常情况下,我们所创建的Action对象,都是通过继承ActionSupport类进行创建,所以,在继承ActionSupport类的情况下,如果通过validate()方法验证数据的有效性,直接重写validate()方法就可以了。

使用validate()方法可以对用户请求的多个Action方法的进行验证,但其验证的逻辑是相同的,如果在一个Action类中编写了多个请求处理方法,而此Action重写了validate()方法,那么,默认情况下,在执行每一个请求方法的过程中,都会经过validate()方法的验证处理。

4.2 验证文件的命名规则

使用Struts2验证框架,验证文件的名称需要遵循一定的命名规则,其验证文件的名称必须为“ActionName-validation.xml”或“ActionName-AliasName-validation.xml”的形式,其中ActionName是Action对象的名称,AliasName为Action配置中的名称,也就是struts.xml配置文件中Action元素对应name属性的名称。

(1)以“ActionName-validation.xml”方式命名

在这种命名方式中,数据的验证会作用于整个Action对象中,并验证Action对象的请求业务处理方法,如果Action对象中只存在单一的处理方法或在多个请求处理方法中,验证处理的规则都相同,可以应用此种命名方式。

(2)以“ActionName-AliasName-validation.xml”方式命名

与上一种命名方式相比较,以“ActionName-AliasName-validation.xml”方式命名更加的灵活,如果一个Action对象中包含多个请求处理方法,而又没有必要对每一个方法进行验证处理,只需要对Action对象中的特定方法进行处理,就可以使用此种命名方式。

4.3 验证文件的编写风格

(1)字段验证器编写风格

<validators>
	<!-- 验证用户名 -->
	<field name="username">
		<field-validator type="requiredstring">
			<message>请输入用户名</message>
		</field-validator>
	</field>
	<!-- 验证密码 -->
	<field name="password">
		<field-validator type="requiredstring">
			<message>请输入密码</message>
		</field-validator>
	</field>
</validators>

(2)非字段验证器编写风格

<validators>
	<validator type="requiredstring">
		<param name="fieldName">password</param>	<!-- 验证密码字段 -->	
		<param name="fieldName">username</param>	<!-- 验证用户名字段 -->
		<message>请输入内容</message>
	</validator>
</validators>

 

5、典型案例

5.1 Struts2标签下的用户注册

【示例】通过Struts2框架提供的表单标签编写用户注册表单,将用户的注册信息输出到JSP页面中。

(1)创建Java Web项目,将Struts2的相关包引用到项目中,并在web.xml文件中注册Struts2提供的StrutsPrepareAndExecuteFilter过滤器。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!-- Struts2过滤器 -->
    <filter>
        <!-- 过滤器名称 -->
        <filter-name>struts2</filter-name>
        <!-- 过滤器类 -->
        <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
    </filter>
    <!-- Struts2过滤器映射 -->
    <filter-mapping>
        <!-- 过滤器名称 -->
        <filter-name>struts2</filter-name>
        <!-- 过滤器映射 -->
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

(2)创建名称为UserInfo的用户信息实体类。

package com.pjb.entity;

/**
 * 用户信息实体类
 * @author pan_junbiao
 **/
public class UserInfo
{
    private String userName;  //用户名称
    private String blogUrl;   //博客地址
    private String password;  //密码
    private String sex;       //性别
    private String province;  //省份
    private String[] bobby;   //爱好
    private String descript;  //描述

    public String getUserName()
    {
        return userName;
    }

    public void setUserName(String userName)
    {
        this.userName = userName;
    }

    public String getBlogUrl()
    {
        return blogUrl;
    }

    public void setBlogUrl(String blogUrl)
    {
        this.blogUrl = blogUrl;
    }

    public String getPassword()
    {
        return password;
    }

    public void setPassword(String password)
    {
        this.password = password;
    }

    public String getSex()
    {
        return sex;
    }

    public void setSex(String sex)
    {
        this.sex = sex;
    }

    public String getProvince()
    {
        return province;
    }

    public void setProvince(String province)
    {
        this.province = province;
    }

    public String[] getBobby()
    {
        return bobby;
    }

    public void setBobby(String[] bobby)
    {
        this.bobby = bobby;
    }

    public String getDescript()
    {
        return descript;
    }

    public void setDescript(String descript)
    {
        this.descript = descript;
    }
}

(3)创建名称为UserAction类(用户Action类),该类继承ActionSupport对象,并实现ModelDriven接口。

package com.pjb.action;
import com.opensymphony.xwork2.ModelDriven;
import com.opensymphony.xwork2.ActionSupport;
import com.pjb.entity.UserInfo;

/**
 * 用户Action
 * @author pan_junbiao
 **/
public class UserAction extends ActionSupport implements ModelDriven<UserInfo>
{
    private static final long serialVersionUID = 1L;
    private UserInfo userInfo;

    public UserAction()
    {
        //初始化UserInfo对象
        userInfo = new UserInfo();
    }

    //用户注册
    public String register() throws Exception
    {
        return SUCCESS;
    }

    @Override
    public UserInfo getModel()
    {
        return userInfo;
    }
}

(4)创建名称register.jsp的用户注册页面。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
    <title>用户注册</title>
    <meta name="author" content="pan_junbiao的博客">
    <style>
        .txtBox{
            padding: 3px;
            width: 300px;
            font-size: 16px;
        }
    </style>
</head>
<body>
    <h2>用户注册</h2>
    <s:form action="userAction!register" method="POST">
        <s:textfield name="userName" label="用户名称" cssClass="txtBox" required="true" requiredPosition="left" value="pan_junbiao的博客"></s:textfield>
        <s:textfield name="blogUrl" label="博客地址" cssClass="txtBox" required="true" requiredPosition="left" value="https://blog.youkuaiyun.com/pan_junbiao"></s:textfield>
        <s:password name="password" label="密码" cssClass="txtBox" required="true" requiredPosition="left"></s:password>
        <s:radio name="sex" list="#{1:'男',0:'女'}" label="性别" required="true" requiredPosition="left"></s:radio>
        <s:select name="province" list="{'请选择省份','广东省','广西省','山东省','河南省' }" label="省份"></s:select>
        <s:checkboxlist name="bobby" list="{'篮球','足球','羽毛球','乒乓球','游泳'}" label="爱好"></s:checkboxlist>
        <s:textarea name="descript" cols="40" rows="5" cssStyle="font-size: 16px" label="描述"></s:textarea>
        <s:submit value="注册"></s:submit>
        <s:reset value="重置"></s:reset>
    </s:form>
</body>
</html>

(5)创建名称为regsuccess.jsp的注册成功页面。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
    <title>注册成功</title>
    <meta name="author" content="pan_junbiao的博客">
</head>
<body>
    <h2 style="color: red">注册成功</h2>
    <ul>
        <li>用户名称:<s:property value="userName"/></li>
        <li>博客地址:<s:property value="blogUrl"/></li>
        <li>密码:<s:property value="password"/></li>
        <li>性别:<s:if test="sex==1">男</s:if><s:else>女</s:else></li>
        <li>省份:<s:property value="province"/></li>
        <li>爱好:<s:property value="bobby"/></li>
        <li>描述:<s:property value="descript"/></li>
    </ul>
</body>
</html>

(6)配置struts.xml文件。

<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
        "http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
    <!--设置开启动态Action -->
    <constant name="struts.enable.DynamicMethodInvocation" value="true"/>
    <!-- 声明包 -->
    <package name="myPackage" extends="struts-default" >
        <!--设置允许调用动态Action中的方法 -->
        <global-allowed-methods>regex:.*</global-allowed-methods>
        <!-- 定义action -->
        <action name="userAction" class="com.pjb.action.UserAction">
            <result name="success">/regsuccess.jsp</result>
        </action>
    </package>
</struts>

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

pan_junbiao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值