SSH框架之Struts2进阶数据封装与结果页面的配置(二)

第一节:Struts2访问Servlet的API

考虑这么一种情况:如果表单中有参数,通过Action如何进行接收又或者需要向页面保存一些数据,又该如何操作呢?我们可以通过学习Struts2访问Servlet的API来实现这样的功能。
在Struts2中,Action并没有直接和Servlet API进行耦合,也就是说在Struts2的Action中不能直接访问Servlet API。虽然Struts2中的Action访问Servlet API麻烦一些,但是这却是Struts2中Action的重要改良之一,方便Action进行单元测试。
尽管Action和Servlet API解耦会带来很多好处,然而在Action中完全不访问Servlet API几乎是不可能的,在实现业务逻辑时,经常要访问Servlet中的对象,如 session、request和application等。在Struts2中,访问Servlet API有3种方法,具体如下:

1.1 通过ActionContext类访问

Struts2框架提供了ActionContext类来访问Servlet API,ActionContext是Action执行的上下文对象,在ActionContext中保存了Action执行所需要的所有对象,包括parameters、request、session、application等。下面列举ActionContext类访问Servlet API的几个常用方法:


image.png

具体使用,请参照如下案例:

  • 访问页面的代码
<%--
  Created by IntelliJ IDEA.
  User: F
  Date: 2019/4/5
  Time: 19:38
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>ActionContext访问Servlet API 实例</title>
</head>
<body>

<form action="${pageContext.request.contextPath}/RequestAction_login.action">
    用户名:<input type="text" name="name"/></br>
    密码:<input type="password" name="password"/></br>
    <input type="submit" value="提交"/>
</form>
</body>
</html>
  • Action的处理逻辑
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.dispatcher.HttpParameters;
import org.apache.struts2.dispatcher.Parameter;

public class RequestActionDemo extends ActionSupport {

    public String login() throws Exception{
        //接收表单的参数
        //使用actionContext来获取参数
        ActionContext context = ActionContext.getContext();
        //接收参数
        HttpParameters parameters = context.getParameters();
        Parameter name = parameters.get("name");
        String nameValue = name.getValue();
        Parameter password = parameters.get("password");
        String passwordValue = password.getValue();
        System.out.println(nameValue +"=====" + passwordValue);

        //设置参数
        context.put("requestName","张三");
        context.getSession().put("sessionName","李四");
        context.getApplication().put("applicationName","王五");

        return SUCCESS;
    }
  • struts2.xml中对Action的配置,使用通配符来实现方法的调用。
 <!--通过ActionContext访问Servlet API-->
        <action name="RequestAction_*" class="com.seapp.struts2.action.RequestActionDemo" method="{1}">
                <result name="success">/demo01.jsp</result>
        </action>
  • 通过jsp界面对Action传入参数的获取
<%--获取action中传递过来的参数--%>
${requestName}</br>
${sessionName}</br>
${applicationName}</br>

1.2 通过特定接口访问:

Struts2框架提供了ActionContext类来访问Servlet API,虽然这种方法可以访问Servlet API,但是无法直接获得Servlet API实例。为了在Action中直接访问Servlet API,Struts2还提供了一系列接口,具体如下:

  • ServletRequestAware:实现该接口的Action可以直接访问Web应用的HttpServletRequest实例。
  • ServletResponseAware:实现该接口的Action可以直接访问Web应用的HttpServletResponse实例。
  • SessionAware:实现该接口的Action可以直接访问Web应用的HttpSession实例。
  • ServletContextAware:实现该接口的Action可以之间访问Web应用的ServletContext实例。
    下面以ServleRequestAware为例,讲解如何在Action中访问HttpServletRequest实例:
  • 定义Action继承ActionSupport实现ServleRequestAware接口:
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.interceptor.ServletRequestAware;

import javax.servlet.http.HttpServletRequest;

public class RequestActionDemo02 extends ActionSupport implements ServletRequestAware {

    HttpServletRequest httpServletRequest;

    @Override
    public void setServletRequest(HttpServletRequest httpServletRequest) {
        this.httpServletRequest = httpServletRequest;
    }

    public String login() throws Exception {
        String username = httpServletRequest.getParameter("name");
        String password = httpServletRequest.getParameter("password");
        System.out.println(username + "======" + password);
        httpServletRequest.setAttribute("message","通过ServletRequestAware接口来访问Servlet API");
        return SUCCESS;
    }
}

需要注意的是,上述类必须实现setServletRequest()方法和execute()方法,
通过setServletRequest()方法,可以得到HttpServletRequest的实例。这是在调用execute()方法或其他自定义的方法之前就调用了的。

  • 修改struts.xml配置文件,添加上述Action的访问配置。
  <!--通过ServleRequestAware访问Servlet API-->
        <action name="RequestAction02_*" class="com.seapp.struts2.action.RequestActionDemo02" method="{1}">
                <result name="success">/demo01.jsp</result>
        </action>
  • 修改提交界面的url地址,并在成功返回界面获取Action中返回的数据
<%--获取HttpServletRequest返回的数据--%>
${requestScope.message}
  • 结果界面如下:


1.3 通过ServletActionContext访问

为了直接访问Servlet API,Struts2框架还提供了ServletActionContext类,该类包含了几个常用的静态方法,具体如下:

  • static HttpServletRequest getRequest():获取web应用的HttpServletRequest对象。
  • static HttpServletResponse getResponse():获取Web应用的HttpServletResponse对象。
  • static ServletContext getServletContext():获取Web应用的ServletContext对象。
  • static PageContext getPageContext():获取Web应用的PageContext对象。
    接下来,实现如何通过ServeltActionContext来访问Servlet API:
  • 实现Action测试类。具体代码如下:
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.ServletActionContext;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Map;

public class RequestActionDemo03 extends ActionSupport {
    
    
    public String login() throws Exception{
        
        //1.通过ServletActionContext对象获取request对象
        HttpServletRequest request = ServletActionContext.getRequest();
        //2.通过request对象来获取参数
        Map<String, String[]> parameterMap = request.getParameterMap();
        for (String key:parameterMap.keySet()) {
            String[] value = parameterMap.get(key);
            System.out.println(Arrays.toString(value));
        }

        String name = request.getParameter("name");
        String password = request.getParameter("password");
        System.out.println(name + "=====" + password);

        //3.设置参数
        request.setAttribute("requestName","张三");//向request域中存值
        request.getSession().setAttribute("sessionName","李四");//向session域中存值
        ServletActionContext.getServletContext()
                .setAttribute("applicationName","王五");//向application域中存值
        
        return SUCCESS;
    }   
}
  • 配置struts.xml
<!--通过ServleActionContext访问Servlet API-->
        <action name="RequestAction03_*" class="com.seapp.struts2.action.RequestActionDemo03" method="{1}">
                <result name="success">/demo01.jsp</result>
        </action>
  • 成功返回界面的代码及内容展示
<%--获取action中传递过来的参数(通过ServletActionContext传入)--%>
${requestName}</br>
${sessionName}</br>
${applicationName}</br>

第二节:结果页面的配置

在struts.xml文件中,Result的配置非常简单,使用<result>元素来配置Result逻辑视图与物理视图之间的映射,<result>元素可以有name和type属性,但这两种属性都不是必选的。

  • name属性:指定逻辑视图的名称,默认值是success;
  • type属性:指定返回的视图资源的类型,不同的类型代表不同的结果输出,默认值是dispatcher;
    struts.xml文件中的<result>元素配置代码如下所示:
  <action name="RequestAction03_*" class="com.seapp.struts2.action.RequestActionDemo03" method="{1}">
                <result name="success" type="dispatcher">/demo01.jsp</result>
        </action>

注意:name属性的值可以是jsp页面,也可以是一个Action的name值。
在结果界面的配置中,Struts2有两种配置的方式,一种称为全局结果页面,一种称为局部结构页面。全局结果是指在这个包下的所有返回相同字符串的值,都可以向这个页面进行跳转。局部结果是指在某个Action中返回的字符串的值,会向这个页面跳转。

2.1 全局结果页面:

全局结果页面是指在同一个包下面配置的Action中返回相同的字符串的值,都可以跳转到该页面,需要通过<global-result>进行配置:

 <!--配置全局的结果页面返回数据-->
        <global-results>
            <result name="success" type="dispatcher">/demo01.jsp</result>
        </global-results>

2.2 局部结果页面:

局部结果页面是指在某个Action中根据该字符串的值进行页面的跳转。只对这个Action有效。

 <action name="RequestAction03_*" class="com.seapp.struts2.action.RequestActionDemo03" method="{1}">
                <result name="success" type="dispatcher">/demo01.jsp</result>
        </action>

在Struts2中,当框架调用Action对请求进行处理后,就要向用户呈现一个结果视图。在Struts2中,预定义了多种ResultType,其实就是定义了多种展示结果的技术。
一个结果类型就是实现了com.opensymphony.xwork.Result接口的类,Struts2把内置的<result-type>都放在struts-default包中,struts-default就是配置包的父包。每个<result-type>元素都是一种视图技术或者跳转方向的封装,其中name属性指出在<result>元素中如何引用这种视图技术或跳转方式,对应着<result>元素的type属性。Struts2中预定义的ResultType如表所示:



其中红色的几个值比较常用,需要重点记忆。

第三节:Struts2中数据的封装

在很多实际开发的场景中:页面提交请求参数到Action,在Action中接收参数并且对请求参数进行数据封装。封装到一个JavaBean中,然后将JavaBean传递给业务层。在Struts2中将数据的封装分成两大类,一类被称为属性驱动,一类被称为模型驱动。

3.1 属性驱动(提供属性的set方法)

属性驱动又可以细分为两种,一种只需要提供属性的set方法即可。另一种可以通过表达式方法直接封装到对象中。

  • 定义jsp页面,建立表单提交数据与需要封装的数据对应
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>struts2中的数据封装</title>
</head>
<body>

    <form action="${pageContext.request.contextPath}/RequestAction04_submit" method="post">

        姓名:<input type="text" name="name"/><br/>
        年龄:<input type="text" name="age"/><br/>
        生日:<input type="date" name="birthday"/><br/>
        工资:<input type="text" name="salary"/><br/>
        <input type="submit" value="提交" />

    </form>

</body>
</html>
  • 定义Action实现需要封装数据的set方法
public class RequestActionDemo04 extends ActionSupport {

    private String name;
    private int age;
    private Date birthday;
    private Double salary;

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

    public void setAge(int age) {
        this.age = age;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public void setSalary(Double salary) {
        this.salary = salary;
    }

    public String submit() throws Exception{

        System.out.println(name);
        System.out.println(age);
        System.out.println(birthday);
        System.out.println(salary);

        return NONE;
    }
  • 在struts.xml中对Action进行配置;
  <action name="RequestAction04_*" class="com.seapp.struts2.action.RequestActionDemo04" method="{1}">
                
        </action>

以上这种方式需要通过在Action中定义属性,并且提供属性的set方法来完成。这种方式只需要提供set方法即可。但若需要传入的数据很多的话,那么Action的属性也会变得很多。再加上属性有对应的getter/setter方法,Action类的代码会变的很膨大。在Action里编写业务代码时,会使得Acction非常臃肿。那么如何解决这个问题呢?具体如下:
把属性和相应的getter/setter方法从Action里提取出来,单独作为一个值对象,这个对象就是用来封装这些数据的,在相应的Action里直接使用这个对象,而且可以在多个Action里使用。采用这种方式,一般以JavaBean来实现,所封装的属性和表单的属性一一对应,JavaBean将成为数据传递的载体。

3.2 属性驱动(页面提供表达式方式):

  • 编写jsp,在表单中添加对应的表达式(OGNL)
 <h2>通过页面表达式实现数据的封装</h2>
    <form action="${pageContext.request.contextPath}/RequestAction05_submit" method="post">

        姓名:<input type="text" name="user.name"/><br/>
        年龄:<input type="text" name="user.age"/><br/>
        生日:<input type="date" name="user.birthday"/><br/>
        工资:<input type="text" name="user.salary"/><br/>
        <input type="submit" value="提交" />
    </form>
  • Action的代码实现:
public class RequestActionDemo05 extends ActionSupport {

    private User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public String submit() throws Exception{

        System.out.println(user);

        return NONE;
    }

}
  • 实现User的JavaBean对象
package com.seapp.struts2.domain;

import java.util.Date;

public class User {

    private String name;
    private int age;
    private Date birthday;
    private Double salary;


    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public Double getSalary() {
        return salary;
    }

    public void setSalary(Double salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", birthday=" + birthday +
                ", salary=" + salary +
                '}';
    }
}

注意:以上这种方式在Action中需要提供User对象的get方法,如果没有get方法,在Struts2的底层就没有办法获得该对象,那么在User中只会有一个属性被封装进去,而其他的属性都是null;

3.3 模型驱动:

在Struts2中模型驱动(ModelDriven)通过实现ModelDriven接口来接收请求参数,并且要重新getModel()方法,这个方法返回的就是Action所使用的数据模型对象。
模型驱动方式通过JavaBean模型进行数据传递。只要是普通的JavaBean就可以充当模型部分。采用这种方式,JavaBean所封装的属性与表单的属性一一对应,JavaBean将成为数据传递的载体。

  • 编写jsp页面进行数据传递
 <h2>通过驱动模型的方法实现数据的封装</h2>
    <form action="${pageContext.request.contextPath}/RequestAction06_submit" method="post">

        姓名:<input type="text" name="name"/><br/>
        年龄:<input type="text" name="age"/><br/>
        生日:<input type="date" name="birthday"/><br/>
        工资:<input type="text" name="salary"/><br/>
        <input type="submit" value="提交" />

    </form>
  • 新建Action并实现ModelDriVen,通过数据模型来获取传递过来的数据
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import com.seapp.struts2.domain.User;

public class RequestActionDemo06 extends ActionSupport implements ModelDriven<User> {

    private User user = new User();

    @Override
    public User getModel() {
        return user;
    }

    public String submit() throws Exception{
        System.out.println(user);
        return NONE;
    }
}

大部分我们会优先使用模型驱动的方式,因为Struts2内部有很多结果是围绕模型驱动设计的。但如果页面向多个对象中封装,那么就需要使用属性驱动的方式二了。

第四节:Struts2中封装集合类型的数据

4.1 Struts2中List集合数据的封装。

  • jsp中页面编写,提供list集合中的提交测试数据
<h2>实现List集合数据的封装</h2>
    <form action="${pageContext.request.contextPath}/RequestAction07_submit" method="post">

        姓名:<input type="text" name="list[0].name"/><br/>
        年龄:<input type="text" name="list[0].age"/><br/>
        生日:<input type="date" name="list[0].birthday"/><br/>
        工资:<input type="text" name="list[0].salary"/><br/>
        姓名:<input type="text" name="list[1].name"/><br/>
        年龄:<input type="text" name="list[1].age"/><br/>
        生日:<input type="date" name="list[1].birthday"/><br/>
        工资:<input type="text" name="list[1].salary"/><br/>
        <input type="submit" value="提交" />
    </form>
  • Action中对该list设置getter/setter方法,获取数据并打印
public class RequestActionDemo07 extends ActionSupport {

    private List<User> list;

    public List<User> getList() {
        return list;
    }

    public void setList(List<User> list) {
        this.list = list;
    }

    public String submit() throws Exception{
        for(int i =0;i<list.size();i++){
            System.out.println(list.get(i));
        }
        return NONE;
    }
}

4.2 Struts2中Map集合数据的封装:

  • 编写jsp ,提供封装Map所需要的测试数据
 <h2>实现Map集合数据的封装</h2>
    <form action="${pageContext.request.contextPath}/RequestAction08_submit" method="post">

        姓名:<input type="text" name="map['one'].name"/><br/>
        年龄:<input type="text" name="map['one'].age"/><br/>
        生日:<input type="date" name="map['one'].birthday"/><br/>
        工资:<input type="text" name="map['one'].salary"/><br/>
        姓名:<input type="text" name="map['two'].name"/><br/>
        年龄:<input type="text" name="map['two'].age"/><br/>
        生日:<input type="date" name="map['two'].birthday"/><br/>
        工资:<input type="text" name="map['two'].salary"/><br/>
        <input type="submit" value="提交" />
    </form>
  • 新建Action,实现对Map集合数据的封装以及打印
public class RequestActionDemo08 extends ActionSupport {

    private Map<String,User> map;

    public Map<String, User> getMap() {
        return map;
    }

    public void setMap(Map<String, User> map) {
        this.map = map;
    }
    
    public String submit() throws Exception{

        for (String key:map.keySet()) {
            System.out.println(key + "======"+map.get(key));
        }
        
        return NONE;
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容