本文包括以下两个部分:
1. Struts2的基本配置和通用功能。
2. Struts2的核心功能:数据接收,数据共享,类型转换,上传下载。
一、Struts2的基本配置和通用功能
1.1 Struts2中action类的三种写法
1.1.1 第一种写法:action类不实现任何接口,不继承任何类。
package edu.scut.a_action; public class UserAction1 { public String login(){ System.out.println("执行了UserAction1!"); return "success"; } }
1.1.2 第二种写法:action类实现Action接口。优点:a. 为action类提供了一个默认的execute方法;
b. 为action类提供了常用的视图标记字符串,例如:SUCCESS、ERROR、INPUT、NONE、LOGIN。
package edu.scut.a_action; import com.opensymphony.xwork2.Action; //实现action接口 public class UserAction2 implements Action{ @Override public String execute() throws Exception { System.out.println("执行了UserAction2!"); return SUCCESS; } }
1.1.3 第三种写法:action类继承ActionSupport类(常用方法)。优点:a. 为action类提供了Action接口的默认实现,默认跳转到SUCESS;
b. 为action类提供了常用的视图标记字符串,例如:SUCCESS、ERROR、INPUT、NONE、LOGIN。
c. 为action类额外提供了数据表单验证以及国际化等功能。
package edu.scut.a_action; import com.opensymphony.xwork2.ActionSupport; //继承actionSupport接口 public class UserAction3 extends ActionSupport{ public String login(){ System.out.println("执行了UserAction3!"); return SUCCESS; } }
1.2 Struts2的常量配置
Struts2的常量配置文件在struts2-core-2.3.4.1.jar当中,查找路径为: /org/apache/struts2/default.properties(这是Struts2的默认常量配置文件!)
1.2.1 常用的常量配置:
a. struts.i18n.encoding. 请求参数的编码,相当于servlet中的request.setCharacterEncoding("utf-8")。
b. struts.multipart.parser. 文件上传使用的组件,默认为jakarta。
c. struts.multipart.saveDir. 文件上传时使用的临时目录,默认为windows的临时目录。
d. struts.multipart.maxSize. 文件上传时最大的文件大小,默认为2MB。
e. struts.action.extension. action访问的后缀名, 默认为 action。
f. struts.enable.DynamicMethodInvocation. 动态方法调用,默认为true,一般只在开发调试阶段使 用,建议项目上线时关闭此功能。
g. struts.ui.theme. struts2的UI的主题,默认为 xhtml。
1.2.2 Struts2的常量配置可以修改,在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.enable.DynamicMethodInvocation" value="true"></constant> <!-- 编码设置 --> <constant name="struts.i18n.encoding" value="utf-8"></constant> <!-- 扩展名称 --> <constant name="struts.action.extension" value="action"></constant> <package name="constant" extends="struts-default" namespace="/constant"> <action name="user_*" class="edu.scut.b_constant.UserAction" method="{1}"> <result>/index.jsp</result> </action> </package> </struts>
1.3 action的全局视图和默认视图
1.3.1 全局视图
作用:在当前package下面的所有action都可以共享这个视图。
注意:如果在action中配置了同名的视图则会覆盖全局视图。
<?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="constant" extends="struts-default" namespace="/constant"> <!-- 配置全局视图 --> <global-results> <result>/index.jsp</result> </global-results> <action name="user_*" class="edu.scut.b_constant.UserAction" method="{1}"> <result>/index.jsp</result> </action> </package> </struts>
1.3.2 默认视图
注意:通常使用action的默认配置可以进行WEB-INF目录下的页面跳转,如下代码中的第2个action。
<?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="constant" extends="struts-default" namespace="/constant"> <!-- 配置全局视图 --> <global-results> <result>/index.jsp</result> </global-results> <action name="book" class="edu.scut.b_constant.BookAction" method="add"> </action> <action name="types"> <result>/WEB-INF/user.jsp</result> </action> </package> </struts>
1.4 注入action属性
作用:action的参数可以通过struts.xml文件设置到action中。
Example
需求:将c:/f.jpg图片复制到/c:targetFile/路径下。
注入属性的步骤:
a. 在对应action的配置中进行配置。
<?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="property" extends="struts-default" namespace="/property"> <action name="book" class="edu.scut.c_property.BookAction" method="addBook"> <param name="serverPath">/c:targetFile/</param> <result name="success" >/index.jsp</result> </action> </package> </struts>
b. 在对应的action类中提供一个同名的setter方法。
package edu.scut.c_property; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import org.apache.commons.io.FileUtils; import com.opensymphony.xwork2.ActionSupport; public class BookAction extends ActionSupport { //通过配置注入action参数 private String serverPath; public void setServerPath(String serverPath) { this.serverPath = serverPath; } public String addBook() throws Exception{ //1 接受图片的内容 File file = new File("c:/f.jpg"); FileInputStream fis = new FileInputStream(file); //2 保存到硬盘 FileUtils.copyInputStreamToFile(fis, new File(serverPath+file.getName())); return SUCCESS; } }
二、Struts2的核心功能
2.1 页面请求参数的三种接收方式
在Struts2框架中,有关于接参数的拦截器:com.opensymphony.xwork2.interceptor.PrepareInterceptor 。其作用是:把表单用户填写的数据接收过来,然后通过Action的setter方法赋值给Action类。因此,使用Struts2框架时,访问action,一般都要经过这个拦截器。
2.1.1 第一种方式:基本类型数据的接收
优点:参数数量少的时候比较简单使用。
缺点:如果参数数量多的时候,必须每个参数在Action类中提供setter方法逐个接收。
表单代码:
<form action="${pageContext.request.contextPath }/constant/user_login.action" method="post"> 用户名:<input type="text" name="name"><br> 密码:<input type="password" name="password"><br> <input type="submit" value="登录"> </form>
Action代码:package edu.scut.d_param; import com.opensymphony.xwork2.ActionSupport; //1 采用setter方法的形式进行接收,接收基本数据类型,适合接受少量的数据 public class UserAction1 extends ActionSupport { private String name; private String password; public void setName(String name) { this.name = name; } public void setPassword(String password) { this.password = password; } //注册方法 public String reg(){ System.out.println("执行了注册方法!"); System.out.println(name); System.out.println(password); return SUCCESS; } }
2.1.2 第二种方式:JavaBan类型数据的接收
注意:在action类中必须同时提供getter和setter方法。
优点:a. 参数多的时候更方便地接收。
b. 可以接收多个不同的表单(即封装多个不同的对象)。
缺点:需要修改表单的name属性值。name属性的值都要写成(实体类名.属性名)的形式。
表单代码:
<form action="${pageContext.request.contextPath }/constant/user_login.action" method="post"> 用户名:<input type="text" name="user.name"><br> 密码:<input type="password" name="user.password"><br> <input type="submit" value="登录"> </form>
User类代码:
package edu.scut.d_param; import java.util.Arrays; //约定:javabean的属性名称和表单的那么属性保持一致 public class User { private String name; private String password; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "User [name=" + name + ", password=" + password + "]"; } }
Action代码:
package edu.scut.d_param; import com.opensymphony.xwork2.ActionSupport; //2 采用javaBean的形式接受数据,表单的name一定要写成user.name的形式 public class UserAction2 extends ActionSupport { private User user; //必须同时提供setter和getter方法 public User getUser() { return user; } public void setUser(User user) { this.user = user; } //注册方法 public String reg(){ System.out.println("执行了注册方法!"); System.out.println(user); return SUCCESS; } }
2.1.3 第三种方式:模型驱动的方法,action类实现ModelDriven接口
注意:
a. Action类实现ModelDriven接口。
b. 实现getModel()方法。
c. 在Action类声明一个对象的成员变量在getModel方法中去接收表单的数据。d. 一定要给对象初始化(private User user=new User())。
优点:a. 参数多的时候更方便地接收。
b. 不用修改表单。缺点:只能接收一个表单(只能封装一个对象)。
表单代码:
<form action="${pageContext.request.contextPath }/constant/user_login.action" method="post"> 用户名:<input type="text" name="name"><br> 密码:<input type="password" name="password"><br> <input type="submit" value="登录"> </form>
User类代码:
package edu.scut.d_param; import java.util.Arrays; public class User { private String name; private String password; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "User [name=" + name + ", password=" + password + "]"; } }
Action代码:
package edu.scut.d_param; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven; //采用modledriver的方式 public class UserAction3 extends ActionSupport<span style="color:#FF0000;"> implements ModelDriven<User> </span>{ private User user= new User(); //一定要实现getModel方法 @Override public User getModel() { return user; } //注册方法 public String reg(){ System.out.println("执行了UserAction的注册方法!"); System.out.println(user); return SUCCESS; } }
2.2 数据共享的三种方式
2.2.1 第一种方式:使用原生的servlet的api进行域对象共享
优点:
a. 可以使用servlet中的类。
b. 可以通过原生的request对象获取用户的浏览器的信息。
缺点:
依赖了servlet的api,而strut2框架并不建议使用servlet的api。
Book实体类代码:
package edu.scut.e_data; import java.io.Serializable; public class Book implements Serializable { private String name; private double price; public Book() { super(); } public Book(String name, double price) { super(); this.name = name; this.price = price; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } }
Action代码:package edu.scut.e_data; import java.util.ArrayList; import java.util.List; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionSupport; //1 采用域对象实现数据共享,servlet的api共享数据 public class BookAction1 extends ActionSupport{ public String list(){ List<Book> books = new ArrayList<Book>(); for(int i=0; i<5; i++){ books.add(new Book("java编程思想"+i,50+i*5)); } //共享数据 //request域 HttpServletRequest request = ServletActionContext.getRequest(); request.setAttribute("r_books", books); //session域 HttpSession session = request.getSession(); session.setAttribute("s_books", books); //context域 ServletContext context = ServletActionContext.getServletContext(); context.setAttribute("c_books", books); return SUCCESS; } }
2.2.2 第二种方式:使用struts2提供的三个Map集合对象来共享数据
RequestMap: 封装了HttpServletRequest对象
SessionMap :封装了HttpSession对象
ApplicationMap: 封装了ServletContext对象
优点:
a. 不依赖于servlet的api,可以更加方便地去测试action类。
b. 给jsp页面共享更多的数据。
缺点:
Action类中的每个方法都必须重新获取Map集合才能共享数据。
Action代码:
package edu.scut.e_data; import java.util.ArrayList; import java.util.List; import java.util.Map; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionSupport; //第二种方式 使用Struts2提供三个MAP集合进行数据共享 public class BookAction2 extends ActionSupport { public String list(){ //模拟数据 List<Book> books = new ArrayList<Book>(); for(int i = 0 ; i<5; i++){ books.add(new Book("计算机网络"+i,50+i*5)); } //获取Context属性 ActionContext ac = ActionContext.getContext(); //获取RequestMap Map rmap = (Map)ac.get("request"); rmap.put("r_books", books); //获取SessionMap Map smap = ac.getSession(); smap.put("s_books", books); //获取ApplicationMap集合 Map cmap = ac.getApplication(); cmap.put("c_books", books); return SUCCESS; } }
2.2.3 第三种方式:采用全局的方法获取Map集合共享数据
分别实现三个接口:RequestAware,SessionAware, ApplicationAware
优点:
a. 不依赖servlet的api。
b. 可以在Action的任何方法去使用Map集合共享数据。
Action代码:
BaseAction
package edu.scut.e_data; import java.util.Map; import org.apache.struts2.interceptor.ApplicationAware; import org.apache.struts2.interceptor.RequestAware; import org.apache.struts2.interceptor.SessionAware; import com.opensymphony.xwork2.ActionSupport; public class BaseAction extends ActionSupport implements RequestAware,SessionAware,ApplicationAware { protected Map requestMap = null; protected Map sessionMap = null; protected Map applicationMap = null; public void setRequest(Map<String,Object> request) { this.requestMap = request; } public void setSession(Map<String,Object> session) { this.sessionMap = session; } public void setApplication(Map<String,Object> application) { this.applicationMap = application; } }
Actionpackage edu.scut.e_data; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.faces.application.Application; import org.apache.struts2.interceptor.ApplicationAware; import org.apache.struts2.interceptor.RequestAware; import org.apache.struts2.interceptor.SessionAware; import com.opensymphony.xwork2.ActionSupport; import com.sun.faces.context.ApplicationMap; //三种方式 public class BookAction3 extends BaseAction { public String list(){ //模拟数据 List<Book> books = new ArrayList<Book>(); for(int i=0; i<5; i++){ books.add(new Book("计算机组成原理"+i,50+i*5)); } //将数据分别保存 //RequestMap requestMap.put("r_books", books); //SessionMap sessionMap.put("s_books", books); //ApplicationMap applicationMap.put("c_books", books); return SUCCESS; } }
2.3 数据类型转换
Struts2内置的参数拦截器可以把表单字符串(String)类型自动转换对应的其他类型。
例如:将表单传过来的String类型转换成double,int,float,short,java.util.Date。
但是表单输入的内容必须按照Struts框架能够识别的格式输入(日期类型默认只支持短日期类型,即yyyy-MM-dd)。
如果需要扩展Struts2的转换类型,可以自定义类型转换器。
2.3.1 开发自定义类型转换器步骤
a. 编写java类,继承StrutsTypeConverter类。
package edu.scut.c_convert; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Map; import org.apache.struts2.util.StrutsTypeConverter; public class MyDateConverter extends StrutsTypeConverter { //设置转换格式,4种不同格式的日期类型的转换 SimpleDateFormat[] sdf = new SimpleDateFormat[]{ new SimpleDateFormat("yyyy/MM/dd"), new SimpleDateFormat("yyyy-MM-dd"), new SimpleDateFormat("yyyy.MM.dd"), new SimpleDateFormat("yyyy#MM#dd") }; @Override public Object convertFromString(Map context, String[] values, Class toClass) { //1 得到页面的日期数据 String dateString = values[0]; /** * 页面数据》》》》action * convert:上下文对象 * values:页面传过来的参数 * toClass:需要转换的类型 * 返回值为转换后的值 */ //2 数据类型转换 //使用SimpleDateFormat对数据进行逐个转换 for(SimpleDateFormat s : sdf){ try { Date date = s.parse(dateString); return date; } catch (ParseException e) { //使用下一个SimpleDateFormat继续进行转换 continue; } } return null; } /** * action》》》》页面 */ @Override public String convertToString(Map context, Object o) { return null; } }
b. 绑定类型转换器。
局部绑定:只能绑定一个Action
规则:在需要绑定类型转换器的Action同目录下建立properties文件:Action的名称-conversion.properties。内容:属性名称=gz.itcast.c_convert.MyDateConverter。
例如:BookAction-conversion.properties。publishtime=edu.scut.c_convert.MyDateConverter
全局绑定:可以绑定项目中的全部Action
规则:在src目录下创建xwork-conversion.properties文件。
内容:java.util.Date=gz.itcast.c_convert.MyDateConverter。java.util.Date=edu.scut.c_convert.MyDateConverter
2.3.2 三种不同接参方式的绑定规则
a. 基本数据类型接收参数
绑定类型转换器:在Action的同目录下建立 Action的名称-conversion.properties。
内容:属性名称=gz.itcast.c_convert.MyDateConverter。
b. javabean类型接收参数
绑定类型转换器:在Action的同目录下建立Action的名称-conversion.properties。
内容:book.publishtime=gz.itcast.c_convert.MyDateConverter。(注意是实体类名.属性名)
c. 模型驱动接收参数
绑定类型转换器:在模型对象的同目录下建立模型的对象名称-conversion.properties。(注意是模型的对象名称,Book-conversion.properties。而不是BookAction-conversion.properties)
2.4 文件的上传与下载
2.4.1 文件上传
a. 文件上传的三个必要条件:
- form表单,type="File"
- post提交方式
- form的enctype="multipart/form-data"
b. action类,其中包含单文件上传和多文件上传,两种方式都是通过setter方法接收表单上传的文件。多文件上传时,将接受的文件内容,名称,类型用集合封装。
package edu.scut.b_upload_down; import java.io.File; import java.io.IOException; import java.util.List; import org.apache.commons.io.FileUtils; import com.opensymphony.xwork2.ActionSupport; import com.sun.org.apache.xml.internal.resolver.helpers.FileURL; public class UploadAction extends ActionSupport{ //接收普通数据 private String name; public void setName(String name) { this.name = name; } //接收一个文件 //1 文件内容 private File attach; //2 文件名称 private String attachFileName; //3 文件类型 private String attachContentType; public void setAttach(File attach) { this.attach = attach; } public void setAttachFileName(String attachFileName) { this.attachFileName = attachFileName; } public void setAttachContentType(String attachContentType) { this.attachContentType = attachContentType; } //注入serverPath private String serverPath; public void setServerPath(String serverPath) { this.serverPath = serverPath; } //单文件上传 public String upload1() throws Exception{ //输出文件的相关信息 System.out.println("用户名:"+name); System.out.println("文件名:"+attachFileName); System.out.println("文件类型:"+attachContentType); System.out.println("文件大小:"+attach.length()); //将文件接收保存在serverPath FileUtils.copyFile(attach, new File(serverPath+attachFileName)); return SUCCESS; } //接收多个文件 //1 文件内容 private List<File> img; //2 文件名称 private List<String> imgFileName; //3 文件类型 private List<String> imgContentType; public void setImg(List<File> img) { this.img = img; } public void setImgFileName(List<String> imgFileName) { this.imgFileName = imgFileName; } public void setImgContentType(List<String> imgContentType) { this.imgContentType = imgContentType; } //多文件上传 public String upload2() throws Exception{ //输出用户名 System.out.println("用户名:"+name); if(img!=null){ for (int i = 0; i < img.size(); i++) { //输出文件的相关信息 System.out.println("文件名:"+imgFileName); System.out.println("文件类型:"+imgContentType); System.out.println("文件大小:"+img.get(i).length()); //将文件接收保存在serverPath FileUtils.copyFile(img.get(i), new File(serverPath+(imgFileName.get(i)))); } return SUCCESS; } return INPUT; } }
struts.xml配置
<span style="color:#000000;"><?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="upload" extends="struts-default" namespace="/upload"> <action name="upload_*" class="edu.scut.b_upload_down.UploadAction" method="{1}"> <!-- 注入一个参数 --> <param name="serverPath" >/c:targetFiles/</param> <!-- 上传成功的页面 --> <result >/success.jsp</result> <!-- 上传失败的页面 --> <result name="input">/fail.jsp</result> </action> </package> </struts>
注意:
INPUT 名称的result是struts2内置的处理错误的视图,struts2框架在文件上传时超过大小,类型转换时转换错误,表单数据验证错误时都会指向到INPUT的视图。
2.4.2 文件下载
Struts框架对文件的下载进行了简化。关键点:需要一个名称为steam的视图。
a. 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="downLoad" extends="struts-default" namespace="/downLoad"> <action name="down_*" class="edu.scut.b_upload_down.DownAction" method="{1}"> <!-- 配置一个下载的视图,四个参数,文件类型,下载提示框,文件输入流,缓存大小 --> <result type="stream"> <!-- 配置视图参数:给视图注入参数 --> <!-- 下载的文件类型:通用的二进制类型 --> <param name="contentType">application/octet-stream</param> <!-- 下载提示框 --> <!-- ${fileName}会获取当前Action的getFileName方法的返回值 --> <param name="contentDisposition">attachment;filename=${fileName}</param> <!-- 需要下载的文件输入流:需要配置getter方法 --> <param name="inputName">fileContent</param> <!-- 下载的缓存大小:默认是1024 --> <param name="bufferSize">512</param> </result> </action> </package> </struts>
b. action类package edu.scut.b_upload_down; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionSupport; //下载Action public class DownAction extends ActionSupport { //需要下载的文件 File file = null; //将文件名称通过getter方法设置到struts.xml配置文件当中 private String fileName; public String getFileName() throws Exception { //如果文件名是中文需要们进行URL编码 return URLEncoder.encode(fileName,"utf-8"); } //需要下载的文件要以流的形式返回 public InputStream getFileContent() throws Exception { return new FileInputStream(file); } //下载方法 public String down(){ //f.jpg放在WebRoot/download文件夹 file = new File(ServletActionContext.getServletContext().getRealPath("/download/f.jpg")); fileName = file.getName(); return SUCCESS; } }