一、Struts2访问Servlet的API
前面已经对Struts2的流程执行完成了,但是如果表单中有参数如何进行接收?又或者我们需要向页面保存一些数据,又要如何完成呢?我们可以通过学习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的几个常用方法,具体如表所示:
列举的是ActionContext类访问Servlet API的常用方法,要访问Servlet API,可以通过如下方式进行,具体示例代码如下:
ActionContext context = ActionContext.getContext(); context.put("name", "张三"); context.getApplication().put("name", "张三"); context.getSession().put("name", "张三");
在上述示例代码中,通过ActionContext类中的方法调用,分别在request、application和session中放入了(“name”, “张三”)对。可以看到,通过ActionContext类可以非常简单地访问JSP内置对象的属性。
为了更好地掌握如何通过ActionContext类来访问Servlet API,接下来通过一个具体的案例来演示ActionContext的使用。
(1)创建一个web工程,在WebContent目录下编写一个简单的登录页面form.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" 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=UTF-8"> <title>Insert title here</title> </head> <body> <!-- 测试表单提交数据 --> <form action="${pageContext.request.contextPath }/userForm.action" method="post"> username:<input type="text" name="username" /><br/> password:<input type="password" name="password" /><br/> <input type="submit" value="提交" /> </form></body> </html>
在该页面中,编写了用户名和密码输入框和一个登录提交按钮。
(3)在src目录下创建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 --> <package name="demo" namespace="/" extends="struts-default"> <!-- action获取表单提交数据 --> <action name="userForm" class="cn.itcast.action.UserFormAction"> <result name="success">/show.jsp</result> </action> </package> </struts>
(4)创建UserFormAction类进行业务处理
public class UserFormAction extends ActionSupport{ @Override public String execute() throws Exception { // 接收表单参数 // 使用ActionContext对象 ActionContext actionContext = ActionContext.getContext(); // 接收参数 Map<String, Object> map = actionContext.getParameters(); for(String key:map.keySet()){ String[] value = (String[]) map.get(key); System.out.println(key+":"+Arrays.toString(value)); } // 向request中存入数据 actionContext.put("requestName", "张三"); // 向session中存入数据 actionContext.getSession().put("sessionName", "李四"); // 向application中存入数据 actionContext.getApplication().put("applicationName", "王五"); return “success"; } }
(5)在WebContent目录下编写show.jsp,用来显示Action传递到页面的数据
<%@ page language="java" contentType="text/html; charset=UTF-8" 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=UTF-8"> <title>Insert title here</title> </head> <body> <h1>获取数据</h1> ${requestName } ${sessionName } ${applicationName } </body> </html>
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实例。
下面以ServletRequestAware为例,讲解如何在Action中访问HttpServletRequest实例。
(1)修改UserFormAction
public class UserFormAction extends ActionSupport implements ServletRequestAware{ HttpServletRequest request; @Override public void setServletRequest(HttpServletRequest request) { this.request = request; } @Override public String execute() throws Exception { // 接收表单参数 // 使用ActionContext对象 ActionContext actionContext = ActionContext.getContext(); // 接收参数 Map<String, Object> map = actionContext.getParameters(); for(String key:map.keySet()){ String[] value = (String[]) map.get(key); System.out.println(key+":"+Arrays.toString(value)); } //----------------------------------------------------- // 向request中存入数据 actionContext.put("requestName", "张三"); // 向session中存入数据 actionContext.getSession().put("sessionName", "李四"); // 向application中存入数据 actionContext.getApplication().put("applicationName", "王五"); //------------------------------------------------------ request.setAttribute("message", "通过ServletRequestAware接口实现了访问Servlet API"); return "success"; } }
在上述文件中,自定义的UserFormAction类实现了Action的ServletRequestAware接口。需要注意的是,UserFormAction类中必须实现setServletRequest()方法和execute()方法,通过setServletRequest()方法,可以得到HttpServletRequest的实例,这是在调用execute()方法或者其他自定义的方法之前就调用的,然后在execute()方法中,就可以访问HttpServletRequest的属性内容了。
(2)在show.jsp展示页面中添加EL表达式访问存放在request对象中键为message的值
<div align="center">${requestScope.message }</div>
(3)重新发布struts2-01项目,在浏览器地址栏中输入http://localhost:8080/struts2-01/form.jsp
,成功访问后可以看出,使用ServletRequestAware接口顺利访问了Servlet API。
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对象
接下来,讲解如何通过ServletActionContext访问Servlet API。
(1)修改UserFormAction
public class UserFormAction extends ActionSupport { @Override public String execute() throws Exception { // 接收表单参数 HttpServletRequest request = ServletActionContext.getRequest(); // 接收参数 Map<String, String[]> map = request.getParameterMap(); for (String key : map.keySet()) { String[] value = (String[]) map.get(key); System.out.println(key + ":" + Arrays.toString(value)); } // 向request中存入数据 request.setAttribute("requestName", "张三"); // 向session中存入数据 request.getSession().setAttribute("sessionName", "李四"); // 向application中存入数据 ServletActionContext.getServletContext().setAttribute("applicationName", "王五"); return "success"; } }
(2)重新运行struts2-01项目,在浏览器地址栏中输入http://localhost:8080/struts2-01/form.jsp。
从以上内容可以看出,借助于ServletActionContext类的帮助,开发者也可以在Action中直接访问Servlet API,避免了Action类实现ServletRequestAware、ServletResponseAware、SessionAware和ServletContextAware等XxxAware接口。虽然如此,该Action依然与Servlet API直接耦合,一样不利于程序解耦。综上三种访问Servlet API的方式,建议在开发中优先使用ActionContext,以避免和Servlet API耦合。
Servlet的API已经可以访问了,那么在页面跳转的时候我们会发现其实Struts2默认使用的都是转发,那如果我们要使用重定向,应该怎么办呢?接下来我们来看下结果页面的配置。
二、结果页面的配置
在struts.xml文件中,result的配置非常简单,使用<result>
元素来配置result逻辑视图与物理视图之间的映射,<result>
元素可以有name和type属性,但这两种属性都不是必选的。
- name属性:指定逻辑视图的名称,默认值为success。
- type属性:指定返回的视图资源的类型,不同的类型代表不同的结果输出,默认值是dispatcher。
struts.xml文件中的<result>
元素配置代码如下所示:
<action name="demo" class="cn.itcast.action.result.DemoAction"> <result name="success" type="dispatcher">/demo.jsp</result> </action>
在上述配置中,使用了<result>元素的name、type属性。其中,为Action配置了name为success的result映射,该映射的值可以是JSP页面,也可以是一个Action的name值。用type属性指定了该result的结果类型为dispatcher,它也是默认的结果类型。
在结果页面的配置中,Struts2有两种配置的方式,一种称之为全局结果页面,一种称之为局部结果页面。全局结果是指在这个包下的所有返回相同字符串的值,都可以向这个页面来进行跳转。局部结果是指在某个Action中返回的字符串的值,会向这个页面跳转。
2.1 全局结果页面
全局结果页面是指在同一个包下面配置的Action中返回相同的字符串的值,都可以跳转到该页面。需要通过<global-results>
进行配置。
下面通过一个简单的案例来演示全局结果页面的使用。
(1)创建两个Action类——CustomerAction类和OrdersAction类,执行默认的execute()方法,默认方法都返回success,如下所示:
- CustomerAction
public class CustomerAction extends ActionSupport { @Override public String execute() throws Exception { return "success"; } }
- OrdersAction
public class OrdersAction extends ActionSupport { @Override public String execute() throws Exception { return "success"; } }
(2)在struts.xml文件对以上两个Action类进行配置
<action name="customer" class="cn.itcast.action.result.CustomerAction"> <result name="success">/form.jsp</result> </action> <action name="orders" class="cn.itcast.action.result.OrdersAction"> <result name="success">/form.jsp</result> </action>
上面配置好了两个action,两个action都是执行execute()方法,两个action的execute()方法都返回success,返回之后都到form.jsp这个页面中,这样写功能没有问题,但造成有很多重复的配置。所以可使用全局结果页面进行配置,在action标签所在的package标签里面使用global-results进行配置。
<!-- 配置全局结果页面 --> <global-results> <result name="success">/form.jsp</result> </global-results> <action name="customer" class="cn.itcast.action.result.CustomerAction"> <!-- <result name="success">/form.jsp</result> --> </action> <action name="orders" class="cn.itcast.action.result.OrdersAction"> <!-- <result name="success">/form.jsp</result> --> </action>
2.2 局部结果页面
局部结果页面是指在某个Action中根据该字符串的值进行页面的跳转。只对这个Action有效。
<action name="demo" class="cn.itcast.action.result.DemoAction"> <result name="success" type="dispatcher">/demo.jsp</result> </action>
注意:如果配置了全局结果页面,也配置了局部结果页面,那么最终以局部结果页面为准。
2.3 result标签的type属性
在Struts2中,当框架调用Action对请求进行处理后,就要向用户呈现一个结果视图。在Struts2中,预定义了多种ResultType,其实就是定义了多种展示结果的技术。
一个结果类型就是实现了com.opensymphony.xwork2.Result接口的类,Struts2把内置的<result-type>都放在struts-default包中,struts-default包就是配置包的父包,这个包定义在struts2-core-2.3.24.jar包中的根目录下的struts-default.xml文件中,可以找到相关的<result-type>的定义。
每个<result-type>
元素都是一种视图技术或者跳转方式的封装,其中的name属性指出在<result>
元素中如何引用这种视图技术或者跳转方式,对应着<result>
元素的type属性。Struts2中预定义的ResultType如表所示:
其中红色的几个值比较常用,需要重点记忆。其他的了解即可。 下面我们演示这四个属性的配置:
- dispatcher(转发)
<action name="Demo1Action" class="cn.itcast.action.Demo1Action" method="execute" > <result name="success" type="dispatcher" >/hello.jsp</result> </action>
- redirect(重定向)
<action name="Demo2Action" class="cn.itcast.action.Demo2Action" method="execute" > <result name="success" type="redirect" >/hello.jsp</result> </action>
- chain(转发到Action)
<action name="Demo3Action" class="cn.itcast.action.Demo3Action" method="execute" > <result name="success" type="chain"> <!-- action的名字 --> <param name="actionName">Demo1Action</param> <!-- action所在的命名空间 --> <param name="namespace">/</param> </result> </action>
- redirectAction(重定向到Action)
<action name="Demo4Action" class="cn.itcast.action.Demo4Action" method="execute" > <result name="success" type="redirectAction"> <!-- action的名字 --> <param name="actionName">Demo1Action</param> <!-- action所在的命名空间 --> <param name="namespace">/</param> </result> </action>