四、Struts程序详解

从上一节课的第一个Struts程序的执行过程可知:Struts 的中心控制器接受所有来自客户端的请求,并根据系统的配置(struts.xml)路由HTTP请求到其它Action对象。在这些Action对象中会完成所有的业务操作,比如用户登录验证等。处理完毕后,由再由Struts的Controller Servlet根据配置转向到适当的JSP页面,将处理结果显示给用户。
因此,这过程中有两个节点是非常重要的:
一个是:struts.xml的配置文件;
另一个:Action控制类实现业务;
因此,我们本章重点来说说这两个部分。并通过例子,实现一个基于MVC模式的用户登录功能。

  • 1.struts.xml
    1.1.<constant>标签:定义常量;(建议:在struts.xml中定义常量)
    1.2.<package>标签:使用包配置来声明Action和拦截器;
    1.3.<include>标签:按功能拆分structs.xml配置文件;
  • 2.Action控制类的实现
    2.1.action控制器的实现(建议:继承ActionSupport类)
    2.2.action控制器在structs.xml中的声明;
    2.5.action返回的结果类型(dispatcher表示转发到;redirect表示重定向到)
    2.3.action访问Servlet API(即:获取上下文数据)(建议:通过ActionContext类访问Servlet)
    2.4.action如何从View中获取参数
  • 3.案例:基于MVC模式的用户登录功能实现

1.struts.xml配置文件

struts.xml是Struts2的核心配置文件,主要用来配置Action和请求的对应关系,以及配置逻辑视图和物理视图资源的对应关系。
通常放在项目的WEB-INF/classes目录下,可以被Struts2框架自动加载。

1.1.定义常量

在写程序时,有些变量是基本不会变且需要经常使用,此时我们就把它们统一成为常量,方便项目中的各个地方使用,例如:链接字符串、PI值等。
在Struts中要定义常量有三种方法,具体如下:

  • [v] 在struts.xml文件中,通过<constant>元素来配置常量;
  • [ ] 在struts.properties文件中,通过key-value键值对配置常量;
  • [ ] 在web.xml文件中,通过<filter>元素的<init-param>子元素指定一个Struts2常量。

1.2.包配置

Struts框架的核心组件是由Action和拦截器组成,为了有效和组织管理,Struts引入了包配置<package>标签来进行声明和管理所有的Action和拦截器。
其主要的属性如下:

<package>标签的主要属性

1.3.包含配置

由于Struts框架是通过配置文件的形式来定义路由,一旦项目规模较大,所涉及对象就会很多,为了更好的进行组织和管理这些路由,Struts允许把配置文件进行拆分,即:将一个配置文件按业务或模块可分解成多个配置文件。
但Struts2默认只加载WEB-INF/classes下的struts.xml文件,因此,我们需要在struts.xml文件中通过<include>标签引入所有子配置文件。如下图所示:

分解成多个配置文件

2.Action控制类

2.1.Action控制类的实现方式

Action实现对用户请求的处理,因此也被称为:业务逻辑控制器
在Struts2中,提供了3种方式实现Action控制类:

方式一:POJO(自定义简单的Java对象)

  • POJO,直接自定义一个简单的Java对象进行实现,这是最原始的方式。编写Action类需要提供getter/getter方法和公共的默认无参数构造方法以及一个execute()方法。

方式二:实现Action接口

  • 实现Action接口,通过编写Action类实现Action接口,复写execute()方法。Action接口定义了5个字符串常量,它的作用是统一excecute()方法的返回值。

方式三:继承ActionSupport类 (推荐)

  • 实现Action接口,通过继承ActionSupport类,重写execute()方法。ActionSupport实现Action等多个接口。所以提供了许多默认的方法,如国际化、数据校验、参数封装等。
import com.opensymphony.xwork2.ActionSupport;

public class UserLoginAction extends ActionSupport {
public String execute() throws Exception {
            return super.execute();
        }
    }
}

2.2.action控制器在structs.xml中的声明;

实现了Action后,就要告诉Web项目,怎么样的请求该丢给哪个Action来处理,这就是需要Struts.xml中对Action进行配置声明了。
配置Action映射将一个请求的URL映射到一个Action类,即把一个URL请求对应到一个Action类,以便在Action类中实现请求的业务处理。
在struts.xml文件中,通过<action>标签进行声明,其属性如下:

<action>标签的主要属性

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>
    <!-- 动态方法调用 ,值为trues时表示启用,false表示不启用,该值默认为false -->
    <!-- <constant name="struts.enable.DynamicMethodInvocation" value="false" 
        /> -->
    <!-- struts2的开发模式 ,默认为false,值设置为true时表示开启 -->
    <!-- <constant name="struts.devMode" value="true" /> -->
    <!-- 1.通过包package来管理Action和拦截器 -->
    <package name="default" extends="struts-default">
        <!-- 2.URL映射到Action类 -->
        <action name="login" class="cn.itcast.action.LoginAction">
            <!-- 3.Action结果的JSP映射 -->
            <result name="success" type="redirect">/success.jsp</result>
            <result name="error" type="dispatcher">/error.jsp</result>
        </action>
        <action name="aware" class="cn.itcast.action.AwareAction">
            <result name="success">/message.jsp</result>
        </action>
        <action name="message" class="cn.itcast.action.MessageAction">
            <result name="success">/message.jsp</result>
        </action>
    </package>
</struts>

2.5.action返回的结果类型

在struts.xml文件中,使用<result>元素来配置Result逻辑视图与物理视图之间的映射。例如:

<action name="loginAction" class="cn.itcast.action.LoginAction">
     <result name="success" type="dispatcher">
            <param name="location">/success.jsp</param>
     </result>
</action>

也可简化为:

<action name="loginAction" class="cn.itcast.action.LoginAction">
     <result>/success.jsp</result>
</action>

注意:在Result配置中,
使用绝对地址:使用斜杠 / 开头,例如:<result>/success.jsp</result>,表示当前Web项目的上下文路径。
使用相对地址:不使用 / 开头,例如:<result>success.jsp</result>,表示当前Action同级目录下的。
Struts2中预定义了一些常用的ResultType,如下图所示:

Struts2中预定义的ResultType类型

其中,
dispatcher结果类型用于表示转发,因此在用户的整个请求/响应过程中,都会保持是同一个请求对象。反映为:URL地址不跳转到JSP页面。
redirect结果类型用于重定向到指定的结果资源(可以是JSP,也可以是另一个Action)。反映为:URL地址跳转到新的URL上。

2.3.action访问Servlet API(即:获取上下文数据)

在Struts2中,Action不直接访问Servlet API,这为前后端解耦合,实现分工带来很大好处,例如:前后段独立开发,可以对单个Action进行单元测试等。

如果想获取上下文数据(例如:Session),那该如何处理呢?

方法一:通过ActionContext上下文对象(推荐)

通过实例化ActionContext对象,可以实现包括parameter、request、session、application的获取。其常用方法如下图所示:


ActionContext上下文对象常用方法

例如,使用ActionContext对象向请求request、session、Application中都分别写入一个name参数,代码如下:

import com.opensymphony.xwork2.ActionContext;
public class UserLoginAction extends ActionSupport {
    public String execute() throws Exception {
        //获取Context对象
        ActionContext context = ActionContext.getContext();
        //将姓名name作为参数写入request请求中
        context.put("name", "用户1");
        //将姓名name作为参数写入session中
        context.getSession().put("name", "用户1");
        //将姓名name作为参数写入Application中
        context.getApplication().put("name", "用户1");
        return 0;
    }
}

方法二:实现特定接口,从而获取Servlet API的实例

Struts2中还提供了一系列接口,通过实现这些接口从而获取Servlet API实例,如:

(1)ServletRequestAware:实现该接口的Action可以直接访问Web应用的HttpServletRequest实例。
(2)ServletResponseAware:实现该接口的Action可以直接访问Web应用的HttpServletResponse实例。
(3)SessionAware:实现该接口的Action可以直接访问Web应用的HttpSession实例。
(4)ServletContextAware:实现该接口的Action可以直接访问Web应用的ServletContext实例。

方法三:ServletActionContext类的静态方法直接获取

Struts2框架还提供了ServletActionContext类实现直接访问Servlet API的直接访问,其提供的主要几个静态方法如下:

  • static HttpServletRequest getRequest():获取Web应用的HttpServletRequest对象。
  • static HttpServletResponse getResponse():获取Web应用的HttpServletResponse对象。
  • static ServletContext getServletContext():获取Web应用的ServletContext对象。
  • static PageContext getPageContext():获取Web应用的PageContext对象。

2.4.action如何从View中获取参数

JSP页面发送请求,经过struts.xml中的配置信息,把URL映射到对应的Action对象中进行处理。

那么Action对象如何获取从JSP页面中传来的参数呢?
Struts框架为我们提供了两种方式获取JSP页面传来的参数:

方法一:字段驱动(属性驱动)的方式:

在Action类定义时,在其内部定义与JSP页面参数对应的名称的属性成员。当请求匹配到该Action时,会自动把JSP页面参数自动匹配到对应名称的属性成员中。因此,属性成员的数据类型必须与JSP页面参数的数据类型一致。例如:

import com.opensymphony.xwork2.ActionSupport;
public class LoginAction extends ActionSupport {
    //与表单中传输参数同名的username
    private String username;   
    //与表单中传输参数同名的password
    private String password;
    @Override
    public String execute() throws Exception {
        return 0;
    }
}

方法二(改良版):JavaBean驱动

既然Action中的属性成员与JSP页面中的传输参数一一对应,能否再进一步,把这些对应的数据封装称为一个实体类对象,专门用来作为传输数据的载体,这就是属性驱动的改良版——JavaBean驱动。

以JavaBean来实现,所封装的属性和表单的属性一一对应,JavaBean将成为数据传递的载体来进行数据的传递。
跟上面方法一一样,我们需要传输登录时用户所输入的用户名username和密码password两个参数,为此我们定义一个实体类对象User,专门用于存储传输的数据。具体如下:
User实体类:

package cn.demo.domain;
public class User {
    private String username;
    private String password;
    // username的getter和setter方法
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    // password的getter和setter方法
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}

Action中访问实体类对象,从而获取页面传来的数据:

import cn.demo.domain.User;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class UserLoginAction extends ActionSupport {
    private User user; //定义User实体类对象
    // user属性的getter和setter方法
    public User getUser() {
        return user;
    }
    public void setUser(User user) {
        this.user = user;
    }
    public String execute() throws Exception {
        //获取Context对象
        ActionContext context = ActionContext.getContext();
        if ("itcast".equals(user.getUsername())
                && "123".equals(user.getPassword())) {
            //将用户名和密码放入session中
            context.getSession().put("username", user.getUsername());
            context.getSession().put("password", user.getPassword());
            return SUCCESS;
        } else {
            context.getSession().put("error", "用户登录失败!");
            return ERROR;
        }
    }
}

方法三:模型驱动(通过接口,实现泛化的模型)

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


作者: 肖sir@ZHBIT
2018 年 09月 27日


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

推荐阅读更多精彩内容