[ SSH框架 ] Struts2框架学习之二
一、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框架提供了 Action Context类来访问 Servlet API。Action Context是 Action执行的上下文对象,在 Action Context中保存了 Action执行所需要的所有对象,包括 parameters, request, session,application等。下面列举 Action context类访问 Servlet apl的几个常用方法,具体如表所示。
以上列举的是 Action context类访问 Servlet apl的常用方法,要访问 Servlet api,可以通过如下方式进行,具体示例代码如下
ActionContext context=ActionContext.getContext(); context.put("name", "Kevin"); context.getApplication().put("name", "Kevin"); context.getSession().put("name", "Kevin");
在上述示例代码中,通过 ActionContext类中的方法调用,分别在 request、 application和 session中放入了("name"," Kevin")对。可以看到,通过 ActionContext类可以非常简单地访问JSP内置对象的属性。
为了让大家更好地掌握如何通过 Action Context类来访问 Servlet API,接下来通过一个具体的案例来演示 Actioncontext的使用:
(1)在 Eclipse中创建一个名称为 struts2dayo2的web项目,将 Struts2所需的jar包复制到Web项目的WEB-INF/lib路径下,并发布到类路径下。在 WebRoot目录下编写一个简单的登录页面 form1. jsp,如下所示:
<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%> <html> <head> <title>My JSP 'form1.jsp' starting page</title> </head> <body> <form action="${pageContext.request.contextPath }/form1.action" method="post"> Username: <input type="text" name="username"> <br/> Password: <input type="text" name="password"> <br/> Address: <input type="text" name="address"> <br/> <input type="submit" value="Submit"> <br/> </form> </body> </html>
(2)打开web.xml,在web.xml中进行如下配置。
<?xml version="1.0" encoding="UTF-8"?> <web-app id="WebApp_9" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- <welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list> --> </web-app>
(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> <constant name="struts.i18n.encoding" value="UTF-8"></constant> <package name="demo1" extends="struts-default" namespace="/"> <action name="form1" class="com.Kevin.form.form1Action"></action> </package> </struts>
(4)在src目录下创建com.Kevin.form包,在包中创建一个form1Action
package com.Kevin.form; /** * 使用ActionContext获取 */ import java.util.Arrays; import java.util.Map; import java.util.Set; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionSupport; public class form1Action extends ActionSupport{ public String execute() throws Exception { //第一种方式:使用ActionContext类获取 //1.获取ActionContext对象 ActionContext context=ActionContext.getContext(); //2.调用方法得到表单数据 //key是表单输入项name属性值,value是输入值 Map<String,Object> map=context.getParameters(); Set<String> keys=map.keySet(); for(String key:keys){ //根据key得到value //数组形式:因为输入项里可能包含复选框情况 Object[] obj=(Object[])map.get(key); System.out.println(Arrays.toString(obj)); } return NONE; } }
1.2 通过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)在src目录下的com.Kevin.form包中创建一个form2Action,代码如下:
package com.Kevin.form; /** * 使用ServletActionContext类获取 */ import java.util.Arrays; import java.util.Map; import java.util.Set; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionSupport; public class form2Action extends ActionSupport{ public String execute() throws Exception { //第二种方式:使用ServletActionContext类获取 //1.使用ServletActionContext类获取request对象 HttpServletRequest request=ServletActionContext.getRequest(); //2.调用方法得到表单数据 String name=request.getParameter("username"); String pwd=request.getParameter("password"); String address=request.getParameter("address"); System.out.println(name+" "+pwd+" "+address); return NONE; } }
(2)在struts.xml文件中进行如下配置:
<action name="form2" class="com.Kevin.form.form2Action"></action>
(3)更改form1.jsp中配置:
<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%> <html> <head> <title>My JSP 'form1.jsp' starting page</title> </head> <body> <form action="${pageContext.request.contextPath }/form2.action" method="post"> Username: <input type="text" name="username"> <br/> Password: <input type="text" name="password"> <br/> Address: <input type="text" name="address"> <br/> <input type="submit" value="Submit"> <br/> </form> </body> </html>
1.3通过特定接口访问通过(一般不使用)
Struts2框架提供了ActionContext类来访问Servlet API,虽然这种方法可以访问Servlet API,但是无法直接获得 Servlet API 实例。为了在 Action中直接访问 Servlet API,Struts2还提供了一系列接口,具体如下:
● ServletRequestAware:实现该接口的 Action可以直接访问Web应用的 HttpServletRequest实例。
● ServletRresponseAware:实现该接口的 Action可以直接访问Web应用的 HttpServletRresponse实例。
● SessionAware:实现该接口的 Action可以直接访问Web应用的 HttpSession实例。
● ServletContextAware:实现该接口的 Action可以直接访问Web应用的ServletContext 实例。
下面以 ServletRequestAware为例,讲解如何在 Action中访问 HttpServletRequest 实例:
(1)在src目录下的com.Kevin.form包中创建一个form3Action,代码如下:
package com.Kevin.form; /** * 使用接口注入 */ import java.util.Arrays; import java.util.Map; import java.util.Set; import javax.servlet.http.HttpServletRequest; import org.apache.struts2.ServletActionContext; import org.apache.struts2.interceptor.ServletRequestAware; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionSupport; public class form3Action extends ActionSupport implements ServletRequestAware{ private HttpServletRequest request; public void setServletRequest(HttpServletRequest request) { this.request=request; } public String execute() throws Exception { String name=request.getParameter("username"); String pwd=request.getParameter("pasword"); String address=request.getParameter("address"); return NONE; } }
(2)在struts.xml文件中进行如下配置:
<action name="form3" class="com.Kevin.form.form3Action"></action>
(3)更改form1.jsp中配置
<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%> <html> <head> <title>My JSP 'form1.jsp' starting page</title> </head> <body> <form action="${pageContext.request.contextPath }/form3.action" method="post"> Username: <input type="text" name="username"> <br/> Password: <input type="text" name="password"> <br/> Address: <input type="text" name="address"> <br/> <input type="submit" value="Submit"> <br/> </form> </body> </html>
二、结果页面配置
在 sturts.xml文件中,Result的配置非常简单,使用< result >元素来配置 Result 逻辑视图与物理视图之间的映射,< result>元素可以有name和type属性,但这两种属性都不是必选的。
● name属性:指定逻辑视图的名称,默认值为 success。
● type属性:指定返回的视图资源的类型,不同的类型代表不同的结果输出,默认值是dispatcher。
struts. xml文件中的< result>元素配置代码如下所示:
<action name="orders" class="com.Kevin.action.OrdersAction">
<result name="success" type="dispatcher">/hello.jsp</result>
</action>
在上述配置中,使用了< result>元素的name、type属性。其中,为 Action配置了name为 success的 Result映射,该映射的值可以是JSP页面,也可以是一个 Action的name值用type属性指定了该Result的结果类型为 dispatcher,它也是默认的结果类型。
在结果页面的配置中, Struts2有两种配置的方式,一种称为全局结果页面,一种称为局部结果页面。全局结果是指在这个包下的所有返回相同字符串的值,都可以向这个页面来进行跳转。局部结果是指在某个 Action中返回的字符串的值,会向这个页面跳转。
2.1 全局结果页面
全局结果页面是指在同一个包下面配置的action返回相同的字符串的值,都可以跳转到该页面。需要通过<global-results>进行配置。
<!-- 全局结果页面配置 --> <global-results> <result name="success">/hello.jsp</result> </global-results>
2.2 局部结果页面
局部结果页面是指在某个action中根据该字符串的值进行页面的跳转。只对这个action有效。
<action name="book" class="com.Kevin.action.BookAction">
<!-- action访问名称 -->
<result name="success" type="redirectAction">orders</result>
</action>
在 Struts2中,当框架调用 Action对请求进行处理后,就要向用户呈现一个结果视图。在 Struts中,预定义了多种 ResultType,其实就是定义了多种展示结果的技术。
一个结果类型就是实现了 com.opensymphony.xwork2.ActionSupport.Result 接口的类,Struts2把内置的result-type都放在 struts- default 包中,sturts- default包就是配置包的父包,这个包定义在struts2-core-2.324jar包中的根目录下的 struts-default.xml文件中,可以找到相关的 result-type的定义。
每个< result-type>元素都是一种视图技术或者跳转方式的封装,其中的name属性指出在< result >元素中如何引用这种视图技术或者跳转方式,对应着< result>元素的type属性。 Struts2中预定义的ResultType如表所示:
其中红色的几个值比较常用,需要重点记忆,其他的了解即可。到这我们已经了解了 Struts2的结果页面的配置了,也知道如何接收数据了,但是接收过来的数据,往往需要进行封装才会向业务层进行传递,那么作为一个框架,如果连这点功能都没有,那就太不像是一个“框架”了。那么在Struts2中提供了对于数据封装的几种方式。接下来我们就来学习一下。
三、Struts2的数据封装
在很多的实际开发的场景中:页面提交请求参数到 Action,在 Action中接收参数并且对请求参数需要进行数据的封装。封装到一个 Javabean中,然后将 Javabean传递给业务层。那么这些操作Struts2已经替我们都想好了。 Struts2将数据的封装分成两大类,一类被称为是属性驱动,一类被称为是模型驱动。我们先来看第一种:属性驱动。
属性驱动可以细分成两种,一种只需要提供属性的set方法即可。另一种可以通过表达式方式直接封装到对象中。
3.1 属性驱动
在Struts2中,可以直接在Action中定义各种Java基本数据类型的字段,使这些字段与表单数据相对应,并利用这些字段进行数据传递。
【属性驱动方式之一:提供属性的set方法的方式】
编写jsp页面:
<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%> <html> <head> <title>My JSP 'form1.jsp' starting page</title> </head> <body> <form action="${pageContext.request.contextPath }/data1.action" method="post"> Username: <input type="text" name="username"> <br/> Password: <input type="text" name="password"> <br/> Address: <input type="text" name="address"> <br/> <input type="submit" value="Submit"> <br/> </form> </body> </html>
编写action类:
package com.Kevin.form; /** * 使用属性封装获取表单数据 */ import com.opensymphony.xwork2.ActionSupport; public class DataDemo1Action extends ActionSupport{ private String username; private String password; private String address; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String execute() throws Exception { System.out.println(username+password+address); return NONE; } }
以上这种方式需要通过在 Action中定义属性,并且提供属性的set和get方法来完成。但若需要传入的数据很多的话,那么 Action的属性也会变得很多。再加上属性有对应的 getter/setter方法, Action类的代码会很庞大,在 Action里编写业务的代码时,会使 Action非常臃肿,不够简洁。那么要怎样解决这个问题呢?
把属性和相应的 getter/setter方法从 Action里提取出来,单独作为一个值对象,这个对象就是用来封装这些数据的,在相应的 Action里直接使用这个对象,而且可以在多个 Action里使用。采用这种方式,一般以 Javabean来实现,所封装的属性和表单的属性一一对应,Javabean将成为数据传递的载体。
【属性驱动方式之一:页面提供表达式的方式】
编写jsp页面:
<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%> <html> <head> <title>My JSP 'form1.jsp' starting page</title> </head> <body> <form action="${pageContext.request.contextPath }/data3.action" method="post"> Username: <input type="text" name="user.username"> <br/> Password: <input type="text" name="user.password"> <br/> Address: <input type="text" name="user.address"> <br/> <input type="submit" value="Submit"> <br/> </form> </body> </html>
编写action类:
package com.Kevin.form; /** * 使用表达式获取表单数据 */ import com.Kevin.entity.User; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven; public class DataDemo3Action extends ActionSupport{ //1.声明实体类 User user; //2.声明实体类的get和set方法 public User getUser() { return user; } public void setUser(User user) { this.user = user; } @Override public String execute() throws Exception { System.out.println(user); return NONE; } }
3.2 模型驱动封装
在 Struts2中,Action处理请求参数还有另外一种方式,叫做模型驱动( ModelDriven )。通过实现 ModelDriven 接口来接收请求参数,Action类必须实现 ModelDriven 接口,并且要重写 getModel( ) 方法,这个方法返回的就是 Action所使用的数据模型对象。
模型驱动方式通过 Javabean模型进行数据传递。只要是普通的 Javabean,就可以充当模型部分。采用这种方式,Javabean所封装的属性与表单的属性一一对应,Javabean将成为数据传递的载体。
【模型驱动】
编写jsp页面:
<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%> <html> <head> <title>My JSP 'form1.jsp' starting page</title> </head> <body> <form action="${pageContext.request.contextPath }/data2.action" method="post"> Username: <input type="text" name="username"> <br/> Password: <input type="text" name="password"> <br/> Address: <input type="text" name="address"> <br/> <input type="submit" value="Submit"> <br/> </form> </body> </html>
编写Action:
package com.Kevin.form; /** * 使用模型驱动获取表单数据 */ import com.Kevin.entity.User; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven; public class DataDemo2Action extends ActionSupport implements ModelDriven<User>{ //创建对象 //前提:表单属性值name和实体类中属性名称一致 User user=new User(); public User getModel() { //返回创建user对象 return user; } @Override public String execute() throws Exception { System.out.println(user); return NONE; } }
3.3 表达式封装和模型封装比较
● 相同点:都可以将数据封装到实体对象里面
● 不同点: ①使用模型驱动只能把数据封装到一个实体类里(在一个action中不能使用模型封装把数据封装到不同的实体类里)
②使用表达式封装可以把数据封装到不同的实体类对象里。
下面使用一个示例来展示用表达式封装将获取到的表单数据封装到不同的实体类中:
首先在src目录下创建com.Kevin.entity包,在包中创建两个实体类:
User实体类:
package com.Kevin.entity; public class User { private String username; private String password; private String address; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "User [username=" + username + ", password=" + password + ", address=" + address + "]"; } }
Book实体类:
package com.Kevin.entity; public class Book { private String bname; public String getBname() { return bname; } public void setBname(String bname) { this.bname = bname; } }
编写jsp页面:
<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%> <html> <head> <title>My JSP 'form1.jsp' starting page</title> </head> <body> <form action="${pageContext.request.contextPath }/data4.action" method="post"> Username: <input type="text" name="user.username"> <br/> Password: <input type="text" name="user.password"> <br/> Address: <input type="text" name="user.address"> <br/> BookName: <input type="text" name="book.bname"> <br/> <input type="submit" value="Submit"> <br/> </form> </body> </html>
编写Action:
package com.Kevin.form; /** * 使用表达式获取表单数据,并封装到不同的实体类中 * */ import com.Kevin.entity.Book; import com.Kevin.entity.User; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven; public class DataDemo4Action extends ActionSupport{ //1.声明实体类 User user; Book book; //2.声明实体类的get和set方法 public User getUser() { return user; } public Book getBook() { return book; } public void setBook(Book book) { this.book = book; } public void setUser(User user) { this.user = user; } @Override public String execute() throws Exception { System.out.println(user); System.out.println(book.getBname()); return NONE; } }
配置struts.xml:
<action name="data4" class="com.Kevin.form.DataDemo4Action"></action>
到这我们已经能够将数据封装到一个Java对象中了,大部分我们会优先使用模型驱动的方式,因为 Struts2内部有很多结果是围绕模型驱动设计的。但如果页面向多个对象中封装,那么就需要使用属性驱动的方式二了。这些都是像某个对象中封装数据,那么如果 Action中需要一个对象的集合呢?又应该如何进行数据的封装呢?那么接下来我们来了解一下 Struts2中复杂类型数据的封装。
四、Struts2中封装集合类型的数据
在实际的开发中,有些时候我们需要批量插入用户或者批量插入其他的对象,在 Action中需要接受到这多个 Action中封装的对象,然后传递给业务层。那么这个时候就需要将表单的数据封装到集合中,一般我们使用的集合无非是List或者是Map集合。下面就以这两种集合进行数据的封装的示例演示。
4.1 封装到List集合中
编写jsp页面:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <html> <head> <title>My JSP 'list1.jsp' starting page</title> </head> <body> <form action="${pageContext.request.contextPath }/list.action" method="post"> <!-- list[0]表示list集合中的第一个user对象 --> Username: <input type="text" name="list[0].username"> <br/> Password: <input type="text" name="list[0].password"> <br/> Address: <input type="text" name="list[0].address"> <br/> Username: <input type="text" name="list[1].username"> <br/> Password: <input type="text" name="list[1].password"> <br/> Address: <input type="text" name="list[1].address"> <br/> <input type="submit" value="Submit"> <br/> </form> </body> </html>
编写Action:
package com.Kevin.form; import java.util.List; import com.Kevin.entity.User; import com.opensymphony.xwork2.ActionSupport; /** * 封装数据到List集合 * @author Kevin * */ public class ListAction extends ActionSupport{ //1.声明List变量 private List<User> list; //2.生成get、set方法 public List<User> getList() { return list; } public void setList(List<User> list) { this.list = list; } @Override public String execute() throws Exception { System.out.println(list); return NONE; } }
4.2 封装到Map集合
编写jsp页面:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <html> <head> <title>My JSP 'list1.jsp' starting page</title> </head> <body> <form action="${pageContext.request.contextPath }/map.action" method="post"> <!-- 设置key的值['key值'] 设置value值 --> Username: <input type="text" name="map['one'].username"> <br/> Password: <input type="text" name="map['one'].password"> <br/> Address: <input type="text" name="map['one'].address"> <br/> Username: <input type="text" name="map['two'].username"> <br/> Password: <input type="text" name="map['two'].password"> <br/> Address: <input type="text" name="map['two'].address"> <br/> <input type="submit" value="Submit"> <br/> </form> </body> </html>
编写Action:
package com.Kevin.form; import java.util.Map; import com.Kevin.entity.User; import com.opensymphony.xwork2.ActionSupport; /** * 封装数据到Map集合 * @author Kevin * */ public class MapAction extends ActionSupport{ //1.声明map集合 private Map<String, User> map; //2.生成get和set方法 public Map<String, User> getMap() { return map; } public void setMap(Map<String, User> map) { this.map = map; } @Override public String execute() throws Exception { System.out.println(map); return NONE; } }