一、比较 action与Action类
1. action:代表一个Struts2的请求(应用程序可以完成的每一个操作,如显示表单、保存信息等)
2. Action类:能够处理Struts2请求的类(普通Java类), 必须遵守以下规则:
- 属性: 属性名必须遵守与JavaBeans属性名相同的命名规则;属性类型可以是任意类型,从字符串到非字符串(基本数据库类型)之间的数据转换可以自动发生;
- 构造器: 必须有一个不带参的构造器,action通过反射创建Action类的实例;
- 方法: 同一个Action类可以包含多个(至少一个)可供action调用的方法;
- 非单例: Struts2会为每一个HTTP请求创建一个新的Action实例,即Action不是单例的,是线程安全的。
二、如何在Action类中访问WEB资源?
1. 什么是WEB资源?
HttpServletRequest、HttpSession、ServletContext等原生的Servlet API。
2. 为什么要访问WEB资源?
在B\S应用的控制器中必然需要访问WEB资源,如向域对象中读写属性、读写Cookie、获取realPath等……
3. 如何访问WEB资源?
- 与Servlet API解耦的方式:只能访问到有限的Servlet API对象,且只能访问其有限的方法(读取请求参数、读写域对象的属性、Session失效等);
- 与Servlet API耦合的方式:可以访问更多的Servlet API对象,且可以调用其原生的方法。
三、采用与Servlet API解耦的方式来访问WEB资源
为了避免与Servlet API耦合,方便Action进行单元测试,Struts2封装了HttpServletRequest、HttpSession与ServletContext,并构造了3个Map对象来替代,故在Action中可以直接使用HttpServletRequest、HttpSession与ServletContext所对应的Map对象来保存和读取数据。
1. 通过ActionContext访问WEB资源
ActionContext是Action执行的上下文对象,可以从中获取当前Action所需要的任何信息,包括parameters、request、session和application等……
2. 通过实现XxxAware接口访问WEB资源
Action类可以通过实现XxxAware接口,使Struts2框架在运行时向Action实例**注入**parameters、request、session和application对应的Map对象。
代码整体结构为:
TextActionContextAction.java:
package com.qiaobc.struts2.action;
import java.util.Date;
import java.util.Map;
import org.apache.struts2.dispatcher.SessionMap;
import com.opensymphony.xwork2.ActionContext;
/**
* 通过ActionContext的方式来实现对WEB资源的访问
* 具体步骤:
* 1). 调用ActionContext对象的getContext()方法获取ActionContext对象
* 2). 调用ActionContext对象的getXxx()方法获取对应Map对象
* 3). 操作Map对象即可
* 具体对比:若Action类中有多个action方法,多个方法中均需使用域对象对应的Map对象时,不建议使用该方式
* 注意:session对应的Map实际上是SessionMap类型的,强转后调用其invalidate()方法可使其失效!
* @author qiaobc
*/
public class TextActionContextAction {
public String execute() {
// 0. 获取ActionContext对象,即Action的上下文对象,可以从中获取到当前Action需要的一切信息
ActionContext actionContext = ActionContext.getContext();
// 1. 获取application对应的Map对象,并操作
// 通过调用ActionContext对象的getApplication()方法来获取application所对应的Map对象进行操作
Map<String, Object> applicationMap = actionContext.getApplication();
// 设置属性
applicationMap.put("applicationKey", "applicationValue");
// 获取属性
Date date = (Date) applicationMap.get("date");
System.out.println("Date: " + date);
// 2. 获取session对应的Map对象,并操作
Map<String, Object> sessionMap = actionContext.getSession();
sessionMap.put("sessionKey", "sessionValue");
if(sessionMap instanceof SessionMap) {
SessionMap sm = (SessionMap) sessionMap;
sm.invalidate(); // 使Session失效
}
// 3. 获取request对应的Map对象,并操作
// ActionContext中并未提供getRequest()方法来获取request对应的Map对象,而需要调用其get("request")来获取
Map<String, Object> requestMap = (Map<String, Object>) actionContext.get("request");
requestMap.put("requestKey", "requestValue");
// 4. 获取请求参数对应的Map对象,并操作
// 注意:1. getParameters()返回值为Map<String, Object>,而不是Map<String, Object[]>
// 2. parametersMap只能读而不能写入数据,如果写入不会出错且不起作用
Map<String, Object> parametersMap = actionContext.getParameters();
parametersMap.put("parametersKey", "parametersValue");
System.out.println(((String[])parametersMap.get("name"))[0]);
return "success";
}
}
TextAwareAction.java:
package com.qiaobc.struts2.action;
import java.util.Map;
import org.apache.struts2.interceptor.ApplicationAware;
import org.apache.struts2.interceptor.ParameterAware;
import org.apache.struts2.interceptor.RequestAware;
import org.apache.struts2.interceptor.SessionAware;
/**
* 通过实现XxxAware接口的方式来实现对WEB资源的访问
* 具体步骤:
* 1). 实现对应的XxxAware接口,并实现其中的抽象方法
* 2). 依赖注入:定义成员变量,接收Struts2已封装好的对应Map对象
* 3). 操作Map对象即可
* 具体对比:若Action类中有多个action方法,多个方法中均需使用域对象对应的Map对象时,建议使用该方式
* 注意:session对应的Map实际上是SessionMap类型的,强转后调用其invalidate()方法可使其失效!
* @author qiaobc
*/
public class TextAwareAction implements ApplicationAware, SessionAware,
RequestAware, ParameterAware {
private Map<String, Object> application;
private Map<String, Object> session;
private Map<String, Object> request;
private Map<String, String[]> parameters;
public String execute() {
// 1. 向application中加入属性:applicationKey2-->applicationValue2
application.put("applicationKey2", "applicationValue2");
session.put("sessionKey2", "sessionValue2");
request.put("requestKey2", "requestValue2");
// 2. 从application中读取属性:data,并打印输出
System.out.println(application.get("date"));
System.out.println(parameters.get("name")[0]);
return "success";
}
@Override
public void setApplication(Map<String, Object> application) {
this.application = application;
}
@Override
public void setParameters(Map<String, String[]> parameters) {
this.parameters = parameters;
}
@Override
public void setRequest(Map<String, Object> request) {
this.request = request;
}
@Override
public void setSession(Map<String, Object> session) {
this.session = session;
}
}
index.jsp:
<%@page import="java.util.Date"%>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<a href="TextActionContext.action?name=qiaobc">Text ActionContext...</a>
<br><br>
<a href="TextAwareAction.action?name=qiaobc">Text AwareAction...</a>
<br><br>
<%
if(application.getAttribute("date") == null) {
application.setAttribute("date", new Date());
}
%>
</body>
</html>
test-ActionContext.jsp:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<h4>Test ActionContext Page...</h4>
<br><br>
ApplicationValue: ${applicationScope.applicationKey } <br><br>
SessionValue: ${sessionScope.sessionKey } <br><br>
RequestValue: ${requestScope.requestKey } <br><br>
ParametersValue: ${parameters.name } <br><br>
</body>
</html>
test-AwareAction.jsp:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<h4>Test AwareAction Page...</h4>
<br><br>
ApplicationValue: ${applicationScope.applicationKey2 } <br><br>
SessionValue: ${sessionScope.sessionKey2 } <br><br>
RequestValue: ${requestScope.requestKey2 } <br><br>
ParametersValue: ${parameters.name } <br><br>
</body>
</html>
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>
<package name="struts2-text-ActionContext" extends="struts-default">
<action name="TextActionContext"
class="com.qiaobc.struts2.action.TextActionContextAction" method="execute">
<result name="success">/test-ActionContext.jsp</result>
</action>
<action name="TextAwareAction"
class="com.qiaobc.struts2.action.TextAwareAction" method="execute">
<result name="success">/test-AwareAction.jsp</result>
</action>
</package>
</struts>
再次说明:若Action类中有多个action方法,多个方法中均需使用域对象对应的Map对象时,建议使用实现Aware接口的方式来访问WEB资源。
四、采用与Servlet API耦合的方式来访问WEB资源
直接访问Servlet的原生API将使Action类与Servlet环境耦合在一起,测试时需要有Servlet容器,不便于对Action进行单元测试。
1. 通过ServletActionContext访问WEB资源
/**
* 通过ServletActionContext对象的方式来实现对WEB资源的充分访问
* 常用方法:
* 1). 获取HttpServletRequest对象:ServletActionContext.getRequest()
* 2). 获取HttpSession对象:ServletActionContext.getRequest().getSession()
* 3). 获取ServletContext对象:ServletActionContext.getServletContex()
* @author qiaobc
*/
2. 通过实现ServletXxxAware接口访问WEB资源
/**
* 通过实现ServletXxxAware接口的方式可以由Struts2注入需要使用的Servlet相关的对象
* 常用接口:
* 1). ServletRequestAware接口:注入HttpServletRequest对象
* 2). ServletContextAware接口:注入ServletContext对象
* 3). ServletResponseAware接口:注入HttpServletResponse对象
* @author qiaobc
*/
五、关于ActionSupport类
- ActionSupport类是默认的Action类,若action节点未配置class属性,则其默认值为com.opensymphony.xwork2.ActionSupport,默认执行的方法为execute()方法;
- 在手工完成字段验证、显示错误消息、国际化等情况下,推荐继承于ActionSupport,进行相应扩展即可。
六、练习实例
1. 关于Struts2可受理的请求的扩展名问题
- org.apache.struts2包下的default.properties中配置了Struts2应用的一些常量;
- 常量struts.action.extension定义了当前Struts2应用可应答的请求的扩展名;
- 可以再struts.xml文件中以常量配置的方式修改default.properties中所配置的常量。
<!-- 配置当前Struts应用可受理的请求的扩展名 -->
<constant name="struts.action.extension" value="action,do,"></constant>
2. 关于Struts2访问WEB资源的练习
具体实现要求:
整体代码结构:
UserAction.java:
package com.qiaobc.struts2.action;
import java.util.Map;
import org.apache.struts2.dispatcher.SessionMap;
import org.apache.struts2.interceptor.ApplicationAware;
import org.apache.struts2.interceptor.SessionAware;
public class UserAction implements SessionAware, ApplicationAware{
private String userName;
public void setUsername(String userName) {
this.userName = userName;
}
private Map<String, Object> sessionMap;
private Map<String, Object> applicationMap;
public String executeLogin() {
// 将用户信息保存在Session域中
// 1). 采用实现XxxAware接口的方式,获取session对应的map对象
// 2). 获取登录信息:通过在Action中添加对应的setter方法来获取
// 3). 把用户信息保存在Session域中
sessionMap.put("userName", userName);
// 使在线人数加1
// 1). 从application中获取当前的在线人数
Integer count = (Integer) applicationMap.get("count");
if(count == null) {
count = 0;
}
// 2). 使当前的在线人数加1
count++;
applicationMap.put("count", count);
return "LoginSuccess";
}
public String executeLogout() {
// 当前在线人数减1
Integer count = (Integer) applicationMap.get("count");
if(count != null && count > 0){
count--;
applicationMap.put("count", count);
}
// 使session失效
((SessionMap) sessionMap).invalidate();
return "LogoutSuccess";
}
@Override
public void setSession(Map<String, Object> sessionMap) {
this.sessionMap = sessionMap;
}
@Override
public void setApplication(Map<String, Object> applicationMap) {
this.applicationMap = applicationMap;
}
}
index.jsp:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<h4>The Begin Page...</h4>
<a href="Login_Ui">>>> Login...</a>
</body>
</html>
login.jsp:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<h4>Login</h4>
<form action="User_login.action" method="post">
<table>
<tr>
<td>User Name: </td>
<td><input type="text" name="username"/>
</tr>
<tr>
<td>Password: </td>
<td><input type="text" name="password"/>
</tr>
</table>
<input type="submit" value="Submit"/>
</form>
</body>
</html>
details.jsp:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
Welcome: ${sessionScope.userName } <br><br>
Welcome. The number of users online is ${applicationScope.count }. <br><br>
<a href="User_logout.action">>>>Logout...</a>
</body>
</html>
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>
<!-- 配置当前Struts应用可受理的请求的扩展名 -->
<constant name="struts.action.extension" value="action,do,"></constant>
<package name="struts2-accessWebRes" extends="struts-default">
<action name="Login_Ui">
<result>/WEB-INF/pages/login.jsp</result>
</action>
<action name="User_login" class="com.qiaobc.struts2.action.User" method="executeLogin">
<result name="LoginSuccess">/WEB-INF/pages/details.jsp</result>
</action>
<action name="User_logout" class="com.qiaobc.struts2.action.User" method="executeLogout">
<result name="LogoutSuccess">/WEB-INF/pages/login.jsp</result>
</action>
</package>
</struts>