第一节: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的几个常用方法:
具体使用,请参照如下案例:
- 访问页面的代码
<%--
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;
}
}