Struts2基础篇之Action
/0x00 参考
本笔记直接参考或引自如下链接文章:
http://c.biancheng.net/struts2/
https://www.w3cschool.cn/struts_2/
0x01 Action配置详解
Action是用于处理请求操作的,它是由StrutsPrepareAndExecuteFilter分发过来的。
实现Action控制类
在 Struts2 中,一个 Action 类代表一次请求或调用,每个请求的动作都对应一个相应的 Action 类。也就是说,用户的每次请求,都会转到一个相应的 Action 类中,由这个 Action 类进行处理。简而言之,Action 就是用于处理一次用户请求的对象。
实现Action接口
当 Action 类处理用户请求成功后,有人习惯返回 index 字符串,有人习惯返回 success 字符串,这会导致在一个 Action 中可能会返回各种不同的值,十分不利于项目的统一管理。
为了让用户更规范地创建 Action 类,Struts2 提供了一个 Action 接口,该接口定义了 Action 类应该实现的规范,用户在创建 Action 时,可以实现这个接口。Action 接口中的具体代码如下所示:
1 | public interface Action { |
从上述代码中可以看出,Action 接口位于 com.opensymphony.xwork2 包中,并且接口中只定义了五个字符串常量和一个 execute() 方法。其中,execute() 方法是 Action 类的默认请求处理方法,该方法返回一个字符串,而上面五个字符串常量的作用是统一 execute() 方法的返回值。
继承ActionSupport类
由于 Xwork 的 Action 接口十分简单,为开发者提供的帮助较小,所以在实际开发过程中,通常都是采用继承 ActionSupport 类的方式创建 Action。其示例代码如下所示:
1 | public class LoginAction extends ActionSupport{ |
ActionSupport 是 Action 接口的默认实现类,所以继承 ActionSupport 就相当于实现了 Action 接口。除 Action 接口以外,ActionSupport 类还实现了 Validateable、ValidationAware、TextProvider、LocaleProvider 和 Serializable 等接口,这为用户提供了更多的功能。
ActionSupport 类中提供了许多默认方法,这些默认方法包括数据校验的方法、默认的处理用户请求的方法等。如果开发者的 Action 类继承 ActionSupport 类,会大大简化 Action 的开发。
需要注意的是,由于自定义的 Action 类继承了 ActionSupport 类,因此必须定义一个变量 serialVersionUID。这是因为 ActionSupport 类实现了 Serializable 接口,任何实现了 Serializable 接口的类都必须声明变量 serialVersionUID,如下所示:
1 | private static final long serialVersionUID = 1L; |
在学习过程中,细心的读者可能会发现,即使不加上述代码,程序也可以正常执行。但是在实际项目开发中,必须加上上述代码。
配置Action
配置 Action 主要就是配置 struts.xml 文件中 Action 的映射信息。Action 映射是指将一个请求的 URL 映射到一个 Action 类,当一个请求匹配某个 Action 名称时,Struts2 框架就使用这个 Action 确定如何处理请求。
struts.xml 文件是通过 <action>
元素对请求的 Action 和 Action 类进行配置的,其示例代码如下所示:
1 | <action name="userAction" class="com.mi1k7ea.action.UserAction" method="add"> |
在上述代码中,包含了 <action>
元素的三个常用属性 name、class 和 method,这三个属性的具体说明如下表所示。
名 称 | 可选/必填 | 说 明 |
---|---|---|
name | 必填属性 | 表示 Action 的名称(该名称必须唯一),它指定了 Action 所处理请求的 URL。该属性将在其他地方被引用,如作为 JSP 页面 form 表单的 action 属性值。 |
class | 可选属性 | 用于指定 Action 的实现类,如果没有指定 class 属性值,则其默认值为 com.opensymphony.xwork2.ActionSupport 类。 |
method | 可选属性 | 指定请求 Action 时调用的方法。如果指定了 method 属性,则该 Action 会调用 method 属性中指定的方法,如果不指定 method 属性,则 Action 会调用 execute() 方法。 |
使用通配符
由于在一个 Action 类中可能有多个业务逻辑处理方法,在配置 Action 时,就需要使用多个 <action>
元素。在实现同样功能的情况下,为了减少 struts.xml 配置文件的代码量,可以借助于通配符映射信息。
下面以一段 Action 的配置代码为例,说明如何使用通配符进行配置,如下所示:
1 | <package name="user" namespace="/user" extends="struts-default"> |
在上述代码中,method 属性值中的数字1表示匹配第 1 个 *
。当客户端发送 /user/userAction_login.action 这样的请求时,<action>
元素的 name 属性值就被设置成 userAction_login,method 属性值就被设置成 login。当客户端发送 /user/userAction_register.action 这样的请求时,<action>
元素的 name 属性值就被设置为 userAction_register,method 属性值也被设置成 register。
另外,对 <result>
元素也可以采用通配符配置,代码如下所示:
1 | <result>/(1).jsp</result> |
当客户端发送 userAction_login 这样的请求时,<result>
元素被设置成跳转到 login.jsp 页面。当客户端发送 userAction_register 这样的请求时,<result>
元素被设置成跳转到 register.jsp 页面。
0x02 Action访问Servlet API
在 Struts2 中,虽然 Action 已经与 Servlet API 完全分离,但在实现业务逻辑时,还是经常要访问 Servlet API 中的对象。
通常开发时需要访问 Servlet API 中的 HttpServletRequest、HttpSession 和 ServletContext 三个接口,它们分别对应 JSP 内置对象 request、session 和 application。
在 Struts2 中,访问 Servlet API 通常采用两种方式,分别是通过 ActionContext 访问和通过 ServletActionContext 访问,本节将针对这两种访问方式进行讲解。
通过ActionContext访问
ActionContext 是 Action 执行的上下文对象,在 ActionContext 中保存了 Action 执行所需要的所有对象,包括 request、session 和 application 等。ActionContext 类访问 Servlet API 的几个常用方法如表所示。
方法声明 | 功能描述 |
---|---|
void put(String key, Object value) | 将 key-value 键值对放入 ActionContext 中,模拟 Servlet API 中的 HttpServletRequest 的 setAttribute() 方法 |
Object get(String key) | 通过参数 key 查找当前 ActionContext 中的值 |
Map<String, Object> get Application() | 返回一个 Application 级的 Map 对象 |
static ActionContext getContext() | 获取当前线程的 ActionContext 对象 |
Map<String, Object> getParameters() | 返回一个包含所有 HttpServletRequest 参数信息的 Map 对象 |
Map<String, Object> getSession() | 返回一个 Map 类型的 HttpSession 对象 |
要访问 Servlet API,可以通过如下示例代码方式进行:
1 | ActionContext context = ActionContext.getContext(); |
在上述示例代码中,通过 ActionContext 类中的方法调用,分别在 request、application 和 session 中放入了(”name”,”mi1k7ea”)键值对。通过代码可以看到,ActionContext 类可以非常简单地访问 JSP 内置对象的属性。
Demo
沿用之前的示例项目继续演示。
添加login.jsp:
1 | <%@ page language="java" contentType="text/html; charset=utf-8" |
修改success.jsp:
1 | <%@ page language="java" contentType="text/html; charset=utf-8" |
添加error.jsp:
1 | <%@ page language="java" contentType="text/html; charset=utf-8" |
编写LoginAction类,主要用于登录逻辑处理:
1 | package com.mi1k7ea; |
修改struts.xml:
1 |
|
效果如图:
通过ServletActionContext访问
除了通过 ActionContext 类访问 Servlet API 以外,Struts2 框架还提供了 ServletActionContext 类访问 Servlet API,该类中的方法都是静态方法,其常见方法如表所示。
方法声明 | 功能描述 |
---|---|
static PageContext getPageContext() | 获取 Web 应用的 PageContext 对象 |
static HttpServletRequest getRequest() | 获取 Web 应用的 HttpServletRequest 对象 |
static HttpServletResponse getResponse() | 获取 Web 应用的 HttpServletResponse 对象 |
static ServletContext getServletContext() | 获取 Web 应用的 ServletContext 对象 |
Demo
在前面项目中的 com.mi1k7ea 包下创建 MessageAction 类,编写后的代码如下所示:
1 | package com.mi1k7ea; |
在struts.xml的package标签内添加一个action标签:
1 | <action name="message" class="com.mi1k7ea.MessageAction"> |
创建message.jsp:
1 | <div align="center">${requestScope.message }</div> |
运行效果:
0x03 Action处理请求参数
在 Struts2 框架中,页面的请求数据和 Action 有两种基本的对应方式,分别是字段驱动(FieldDriven,也称为属性驱动)方式和模型驱动(ModelDriver)方式。
属性驱动
属性驱动是指在 Action 中通过字段属性进行与页面之间的数据传递,通常使用时会包括两种情况:一种是与基本数据类型的属性对应,另一种是直接使用域对象。
基本数据类型字段驱动方式的数据传递
在 Struts2 中,可以直接在 Action 中定义各种 Java 基本数据类型的字段,使这些字段与表单数据相对应,并利用这些字段进行数据传递,如下面的代码所示:
1 | public class UserAction extends ActionSupport { |
在上述 Action 类的代码中,定义了两个字符串字段 username 和 password,这两个字段分别用于对应页面上的用户名和密码这两个表单域。
直接使用域对象字段驱动方式的数据传递
在基本数据类型字段驱动方式中,如果传入的数据很多,那么 Action 的属性也会变得很多,再加上属性对应的 getter/setter 方法,势必会导致 Action 非常臃肿。
为了解决这一问题,我们可以把属性的 getter/setter 方法从 Action 中提取出来单独作为一个域对象,并在相应的 Action 中直接使用这个域对象。此种方式中的域对象一般以 JavaBean 的形式实现,JavaBean 中所封装的属性要和页面中表单的属性一一对应。此时 JavaBean 将成为数据传递的载体,并可以在其他 Action 中使用。
Demo参考:http://c.biancheng.net/view/4095.html
模型驱动
在 Struts2 中,Action 还有另外一种方式处理请求参数,称为模型驱动(ModelDriven)。
模型驱动方式要求 Action 需要通过实现 ModelDriven 接口接收请求参数,并且要重写 getModel() 方法。getModel() 方法返回的就是 Action 所使用的数据模型对象。
与属性驱动中直接使用域对象字段驱动方式的数据传递类似,模型驱动方式也是通过 JavaBean 模型进行数据传递的。只要是普通的 JavaBean,就可以充当模型部分,并且 JavaBean 中所封装的属性要与表单的属性一一对应,JavaBean 就是数据传递的载体。
使用模型驱动方式时,Action 类中通过 getModel() 方法获取模型,其示例代码如下所示:
1 | package com.mi1k7ea; |
使用模型驱动时,其对应的表单页面也要做相应调整,调整后的代码片段如下所示:
1 | <form action="loginAction" method="post" name="form1"> |
从上述代码中可以看出,使用 ModelDriver 的方式后,表单中的文本域名称已经不需要添加 user 前缀,页面上的 username 会自动对应到这个 Model 的 username 属性。
与属性驱动相比,模型驱动不需要在 Action 类中定义与表单元素一一对应的所有属性及其各属性的 getter 和 setter 方法,这减少了 Action 类中的代码量。在项目应用中具体使用哪种驱动方法,现给出以下几点建议。
- 要统一整个系统中 Action 的驱动方法,即要么都使用属性驱动,要么都使用模型驱动。
- 如果持久层对象与表单中的属性是一一对应的关系,那么建议使用模型驱动,因为模型驱动方法使 Action 类中的代码更加整洁。
- 如果持久层对象与表单中的属性不是一一对应的关系,那么建议使用属性驱动,因为不是一一对应的关系时,系统中需要提供两个 JavaBean(一个对应表单提交的数据,一个用于持久层对象)。
总之,属性驱动的方法和模型驱动的方法各有优缺点,在实际开发中,需要根据项目实际情况选择使用哪种驱动方式。