Struts2(上)
一、 经典的MVC模式
二、 Struts1.x对MVC的实现
三、 Struts1.x的主要组件和作用
组件 |
作用 |
ActionServlet |
*控制器 |
Action |
分控制器,调用JavaBean实现业务逻辑,Action可以分为Action、DispatchAction等不同类型 |
ActionForm |
封装参数,实现服务器验证,文件上传等 |
Forward |
转发到目的地,可以是JSP,也可以是另一个Action |
Struts-config.xml |
配置文件,配置有ActionForm,Action,Forward等,通过XML解析,然后使用反射技术使用该文件 |
Struts标签库 |
和JSTL类似,和Struts-config.xml、ActionForm、Action等对象可以紧密集成 |
四、 Struts1.x 开发实例-登录程序
见案例。
五、 Struts2(WebWork)的MVC
Struts2的MVC架构
六、 Struts2的主要组件
组件 |
作用 |
FilterDispatcher |
起*控制器作用的过滤器 |
Action |
处于Model层的Action,调用JavaBean实现业务逻辑 |
struts.xml |
核心配置文件,配置有Action、Result等 |
result |
和forward类似,转发的目的地,支持多种视图技术。 |
七、 Struts2的实例-登录
在MyEclipse环境中建立一个新的web工程,名称“Struts2Login”,存放路径“f:\Struts2Login”。
在Struts2.0.11版本中找到war包struts2-blank-2.0.11.war,解开该war包,在WEB-INF/lib目录下复制出所有的jar包,这些jar包就是一个Struts2程序所需要的基础jar包,把它们复制到新建的web工程的WEB-INF/lib目录中。
在src目录下建立一个类,包名mypack,类名UserAction,其代码如下:
package mypack; import com.opensymphony.xwork2.ActionSupport; public class UserAction extends ActionSupport { private String username; private String userpass; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getUserpass() { return userpass; } public void setUserpass(String userpass) { this.userpass = userpass; } @Override public String execute() throws Exception { if ("Mike".equals(username) && "123".equals(userpass) || "张三".equals(username) && "abc".equals(userpass)) return "success"; else return "error"; } } |
在src目录下建立Struts2的配置文件struts.xml,内容如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <!-- 定义包管理配置的action 继承struts-default.xml中的配置 --> <package name="actions" extends="struts-default"> <!-- 定义Action(login.action) --> <action name="login" class="mypack.UserAction"> <!-- 定义转发路径对应的字符串名 --> <result name="success">/Success.jsp</result> <result name="error">/Error.jsp</result> </action> </package> </struts> |
修改web.xml,在其中加入一个过滤器,过滤/*路径。
<!-- 过滤器类 --> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class> </filter> <!-- 过滤所有的url请求 --> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> |
在webroot目录下编写3个jsp页面,index.jsp、Success.jsp和Error.jsp,注意这里把page指令中的pageEncoding设置为“utf-8”,就没有中文问题了。
l index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'index.jsp' starting page</title> </head> <body> <!-- 提交到action --> <form action="login.action" method="post"> 用户名: <!-- 参数名和action中的属性名一样 --> <input type=text name=username> <br> 密 码: <input type=password name=userpass> <br> <input type=submit name=subm value="提交"> <input type=reset name=reset value="取消"> </form> </body> </html> |
l Success.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'Success.jsp' starting page</title> </head> <body> <h1> 欢迎 <%=request.getParameter("username")%> ,登录 </h1> </body> </html> |
l Error.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'Error.jsp' starting page</title> </head> <body> <h1> 用户名或密码错误! </h1> </body> </html> |
把程序部署在tomcat5.5以上的版本中运行。
图1-2 index.jsp
图1-3 Success.jsp
八、 Struts2的中文乱码解决
中文乱码问题一般是指当请求参数有中文时,无法在Action中得到正确的中文。Struts2中有2种办法可以解决这个问题:
l 设置JSP页面的pageEncoding=”utf-8”,就不会出现中文乱码;
l 如果JSP页面的pageEncoding=”GBK”,那么需要修改struts.i18n.encoding=GBK,在struts.xml中加入如下语句进行修改。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.i18n.encoding" value="GBK"/> …… </struts> |
上面2种方法可以解决POST请求中的中文参数,但是GET请求中的中文参数不能解决,GET请求中的中文参数的乱码需要通过修改Tomcat的server.xml文件来解决,修改如下内容,加入URIEncoding=”GBK”:
<Connector port="8080" …… URIEncoding="GBK"/> |
九、 Struts2的Action中访问web对象
Struts2的Action就是一个普通的POJO对象,它和Web对象request、response、session和application没有耦合在一起,这样便于单独测试Action,那么我们在Action中如何访问这些web对象呢?
访问这些web内部对象有2种方式:
l 直接访问Web对象
Struts2框架提供org.apache.struts2.ServletActionContext辅助类来获得web对象。
HttpServletRequest request = ServletActionContext.getRequest(); HttpServletResponse response = ServletActionContext.getResponse(); HttpSession session = request.getSession(); ServletContext application = ServletActionContext.getServletContext(); |
l Action访问ActionContext
com.opensymphony.xwork2.ActionContext是一个Action执行的上下文,Action执行期间所用到的对象都保存在ActionContext中,例如session、参数等,并且ActionContext是一个局部线程变量,不用担心Action的线程安全。
ActionContext context = ActionContext.getContext(); |
该类的常用方法见表1-3所示:
表1-3 ActionContext中的常用方法
Object get(Object key) |
使用key来查找当前ActionContext中的值 |
Map getApplication() |
返回一个Application范围的Map |
static ActionContext getContext() |
获得当前线程的ActionContext实例 |
Map getParameters() |
Map类型的所有HttpServletRequest的参数 |
Map getSession() |
Map类型的HttpSession值 |
ValueStack getValueStack() |
返回一个ValueStack类型OGNL值栈 |
void put(Object key,Object value) |
向当前ActionContext存入值,等于在HttpServletRequest中加入值 |
void setApplication(Map application) |
设置application上下文 |
void setSession(Map session) |
设置session值,参数为Map实例 |
这种方法使用的所有对象和Web对象没有直接联系,所以在测试的时候也是很方便的,我们推荐在程序中使用此方法来访问web对象。
十、 操作实例 Struts2实现按类别查询图书
本案例的工程文件结构如图1-7所示,文件说明见表1-4所示:
图1-7 案例的工程文件结构
表1-4 案例的文件说明
文件名 |
说明 |
BookAction.java |
Strut2的Action |
DbConn.java |
数据库连接类 |
BookOper.java |
数据库查询类 |
Book.java |
数据对象类 |
struts.xml |
Struts2的action配置文件 |
web.xml |
Web应用配置文件,Struts2框架在其中配置了Filter |
booklist.jsp |
显示查询结果页面 |
Index.jsp |
输入书籍类别的页面 |
- 生成一个web project,名称为“Struts2按类别查询图书”,指定存放路径为f:\ Struts2按类别查询图书;
- 生成Model层的JavaBean;
l 数据库连接类DbConn.java
package db; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class DbConn { public static Connection getConn() { Connection conn = null; try { Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); conn = DriverManager.getConnection( "jdbc:sqlserver://localhost:1433;databasename=pubs", "sa", ""); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } return conn; } } |
l 数据库查询类BookOper.java
package oper; import java.sql.Connection; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import po.Book; public class BookOper { public List<Book> selectByType(String type) { Connection conn = db.DbConn.getConn(); java.sql.PreparedStatement pstmt = null; java.sql.ResultSet rs = null; List<Book> data = new ArrayList<Book>(); try { pstmt = conn .prepareStatement("select title_id,title,type,price from titles where type=?"); pstmt.setString(1, type); rs = pstmt.executeQuery(); while (rs.next()) { Book book = new Book(); book.setTitleid(rs.getString(1)); book.setTitle(rs.getString(2)); book.setType(rs.getString(3)); book.setPrice(rs.getFloat(4)); data.add(book); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { if (rs != null) try { rs.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (pstmt != null) try { pstmt.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (conn != null) try { conn.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return data; } } |
l 数据持久化类Book.java
package po; //图书的数据类 public class Book { private String titleid; private String title; private String type; private float price; public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getTitleid() { return titleid; } public void setTitleid(String titleid) { this.titleid = titleid; } public String getType() { return type; } public void setType(String type) { this.type = type; } } |
- 生成Struts2的Action对象,在src目录中编写action的配置文件struts.xml;
l BookAction.java
package action; import oper.BookOper; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionSupport; public class BookAction extends ActionSupport { private String type; //书类型 private BookOper bo = new BookOper(); //查询类 public String getType() { return type; } public void setType(String type) { this.type = type; } @Override public String execute() throws Exception { // TODO Auto-generated method stub //查询结果放在request中 ActionContext.getContext().put("booklist", bo.selectByType(type)); //返回success指定的页面 return SUCCESS; //"success" } } |
l struts.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="actions" extends="struts-default"> <action name="typequery" class="action.BookAction"> <!-- name="success" --> <result>/booklist.jsp</result> </action> </package> </struts> |
- 生成JSP页面index.jsp和booklist.jsp;
l index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'index.jsp' starting page</title> </head> <body> <h1>按类别查询图书</h1> <form action="typequery.action" method="post"> 书籍类别:<input type=text name=type> <!-- 参数名和action中属性名一样 --> <input type=submit name=subm value="查询"> </form> </body> </html> |
l booklist.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'booklist.jsp' starting page</title> </head> <body> <h1>查询结果</h1> <table border=1> <tr><th>编号</th><th>名称</th><th>类型</th><th>单价</th></tr> <c:forEach var="book" items="${booklist}"> <tr><td>${book.titleid }</td> <td>${book.title }</td> <td>${book.type }</td> <td>${book.price }</td></tr> </c:forEach> </table> </body> </html> |
- 修改web.xml,在其中配置Struts2的过滤器;
<?xml version="1.0" encoding="UTF-8"?> <web-app 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.FilterDispatcher </filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app> |
- 在tomcat5.5以上版本中部署web程序为“type”,运行效果如图1-8和1-9。
图1-8 index.jsp
图1-9 booklist.jsp
Struts2(下)
一、 Action的分类
- 1. 继承ActionSupport实现Action
通过继承ActionSupport来实现Action是我们的推荐做法,因为ActionSupport中提供了输入验证、国际化、execute等常用方法,使得编写Action时代码很简单。
package mypack; import com.opensymphony.xwork2.ActionSupport; public class HelloWorld extends ActionSupport { private String message; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } @Override public String execute() throws Exception { // TODO Auto-generated method stub message = "大家好!"; return SUCCESS; } } |
ActionSupport实现了Action接口,这个接口中定义了一些常量和execute方法。
public abstract interface com.opensymphony.xwork2.Action{ // 定义常量 public static final java.lang.String SUCCESS = "success"; public static final java.lang.String NONE = "none"; public static final java.lang.String ERROR = "error"; public static final java.lang.String INPUT = "input"; public static final java.lang.String LOGIN = "login"; // 定义抽象方法 public abstract java.lang.String execute() throws Exception; } |
ActionSupport类的代码部分内容如下:
public class com.opensymphony.xwork2.ActionSupport implements com.opensymphony.xwork2.Action,com.opensymphony.xwork2.Validateable, com.opensymphony.xwork2.ValidationAware,com.opensymphony.xwork2.TextProvider, com.opensymphony.xwork2.LocaleProvider,java.io.Serializable{ ...... //添加字段异常 public void addFieldError(java.lang.String fieldName,java.lang.String errorMessage){} //execute public String execute() throws Exception{} //输入验证 public void validate(){} ...... } |
struts.xml的配置如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="actions" extends="struts-default"> <action name="hello" class="mypack.HelloWorld"> <result>/Success.jsp</result> </action> </package> </struts> |
2. 模型驱动(ModelDriven)的Action
Struts2的Action属于MVC模型层,Action中的方法代表业务逻辑,Action中的属性代表请求中的参数,当页面请求参数较多的时候,把过多的参数对象的属性定义在Action中不太符合Struts所倡导的松耦合原则,所以我们推荐单独用JavaBean来封装参数,在Action中为JavaBean赋值,这就是ModelDriven的Action。模型驱动的Action要求Action实现ModelDriven接口,假如登录页面需要传输参数username和userpass,我们把这2个参数封装在一个数据的JavaBean中,然后在Action中定义该JavaBean为Model即可,代码如下:
l Userinfo.java
package po; //用户名和密码的封装对象 public class Userinfo { private String username; private String userpass; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getUserpass() { return userpass; } public void setUserpass(String userpass) { this.userpass = userpass; } } |
l UserAction.java
package action; import po.Userinfo; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven; public class UserAction extends ActionSupport implements ModelDriven<Userinfo> { private Userinfo model = new Userinfo(); @Override public String execute() throws Exception { // TODO Auto-generated method stub return SUCCESS; } //返回模型对象的实例 public Userinfo getModel() { // TODO Auto-generated method stub return model; } } |
当请求该Action的时候,请求中的参数会自动填充到模型Userinfo的属性中,当然需要参数名和属性名一样,到跳转的页面上利用Struts2标签<s:property value="username" />可以取出模型Userinfo中的属性username。在ModelDriven接口中的方法getModel()必须实现,通过它告诉系统模型的具体对象是什么。
l struts.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="actions" extends="struts-default"> <action name="user" class="action.UserAction"> <result>/new.jsp</result> </action> </package> </struts> |
l new.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <%@ taglib uri="/struts-tags" prefix="s"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'new.jsp' starting page</title> </head> <body> username:<s:property value="username" /> </body> </html> |
3. 多方法的Action
Action中的方法代表业务逻辑,那么一个模块中的多个业务逻辑如何用Action来处理呢?我们有2种办法来处理这个问题:
- 一个Action对应一个业务逻辑,实现方便,但是Action数量多,struts.xml中需要配置的内容也多,这种方法不推荐;
- 一个Action对应多个业务逻辑,例如表的CRUD操作,含有多个业务逻辑,我们只写一个Action来实现,Action的数量没有增加,struts.xml的配置也简单,所以这种方法是我们推荐的做法。
Action中自定义方法的声明和execute方法一样,方法的调用路径为“Action名称!方法名称.action”。
public String 方法名() throws Exception{} |
以用户表Userinfo的CRUD操作为例,看一下多方法Action的代码:
package action; import po.Userinfo; import service.UserService; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven; public class CrudUserAction extends ActionSupport implements ModelDriven<Userinfo> { // crud业务方法 private UserService userservice = new UserService(); private Userinfo userinfo=new Userinfo(); // 模型对象userinfo public Userinfo getModel() { // TODO Auto-generated method stub return userinfo; } // 增加 public String create() throws Exception { userservice.createUser(userinfo); return SUCCESS; } // 查询 public String retrive() throws Exception { // 查询结果放在request中 ActionContext.getContext().put("userlist", userservice.selectUsers()); return "list"; } // 修改 public String update() throws Exception { userservice.updateUser(userinfo); return SUCCESS; } // 删除 public String delete() throws Exception { userservice.deleteUser(userinfo.getUsername()); return SUCCESS; } // 默认的execute public String execute() throws Exception { return SUCCESS; } } |
在struts.xml中配置如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="actions" extends="struts-default"> <action name="CrudUser" class="action.CrudUserAction"> <result>/Success.jsp</result> <result name="list">/UserList.jsp</result> </action> </package> </struts> |
调用CRUD业务逻辑的请求路径见表2-1。
表2-1 多方法Action中每个方法的请求路径
业务逻辑方法 |
请求路径 |
create() |
CrudUser!create.action |
retrive() |
CrudUser!retrive.action |
update() |
CrudUser!update.action |
delete() |
CrudUser!delete.action |
execute() |
CrudUser.action |
二、 Result类型
Action中表示跳转的目的地使用了在struts.xml配置的字符串,格式为:<result name=”” type=””></result>,type可以有多种选择,Struts2支持各种视图技术,例如JSP、JSF、XML等,默认的是JSP。常见的type类型配置如下:
l dispatcher
转发到JSP页面,和<jsp:forward page=””/>的效果一样,是默认类型。
<result>/Success.jsp</result> <result name=”a”>/Success.jsp</result> <result name=”b” type=”dispatcher”>/Success.jsp</result> |
l redirect
重定向到JSP页面,和response.sendRedirect(“”)的效果一样。
<result name=”a” type=”redirect”>/Success.jsp</result> |
l redirect-action
重定向到action,目的地为Action,配置时可以指定如下两个参数:actionName-重定向的Action名;namespace-重定向的Action所在的命名空间。
<result name=”a” type=”redirect-action”> <param name=”actionName”>myaction</param> <param name=”namespace”>/test</param> </result> |
l chain
转发到action,形成action-chain,可以指定两个参数:actionName-重定向的Action名;namespace-重定向的Action所在的命名空间。
<result type=”chain”> <param name=”actionName”>myaction</param> <param name=”namespace”>/test</param> </result> |
l stream
用于向页面返回一个InputStream,原始数据直接传递给HttpServletResponse,这种结果类型在用户下载文件(例如PDF文件等)等情况下非常有意义。
<result name=”success” type=”stream”> <param name=”contentType”>image/jpg</param> <param name=”inputName”>imageStream</param> <param name=”contentDisposition”>filename=”document.pdf”</param> <param name=”buffersize”>1024</param> </result> |
l plaintext
用于输出目的地JSP/HTML的源代码内容,可以指定两个参数:location-目的地JSP/HTML,charSet-输出内容时使用的字符集。
<result name="success" type="plaintext"> <param name="location">/Success.jsp</param> <param name="charset">utf-8</param> </result> |
除了上述类型以外,还支持如下的类型:
l chart:用于整合JFreeChart的result类型;
l freemarker:用于整合FreeMarker的result类型;
l httpheader:用于处理特殊http行为的result类型;
l jasper:用于整合JasperReport的result类型;
l jsf:用于整合JSF的result类型;
l titles:用于整合Titles的result类型;
l velocity:用于整合Velocity的result类型;
l xslt:用于整合XML/XSLT的result类型。
这些视图技术的支持,有些还需要导入相应的插件包,即Struts2提供的含有plugin字样的jar包。
三、 输入验证
- 1. javaScript客户端验证
- 2. validate方法验证
l 在Action中加入validate验证方法,把添加和修改时验证分开,添加用户时验证方法为validateCreate(),修改用户时的验证方法为validateUpdate()。
package action; import javax.servlet.http.HttpServletRequest; import org.apache.struts2.ServletActionContext; import oper.UserOper; import po.Userinfo; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven; public class UserAction extends ActionSupport implements ModelDriven<Userinfo> { // 数据模型 private Userinfo user = new Userinfo(); // 业务类 private UserOper uo = new UserOper(); public Userinfo getModel() { // TODO Auto-generated method stub return user; } // 增加前 public String precreate() throws Exception { return "addupdate"; } // 增加 public String create() throws Exception { uo.create(user); return select(); } // 删除 public String delete() throws Exception { uo.delete(user.getUserid()); return select(); } // 修改 public String update() throws Exception { uo.update(user); return select(); } // 查询 public String select() throws Exception { ActionContext.getContext().put("userlist", uo.retriveAll()); return SUCCESS; } // 查询单个 public String retrive() throws Exception { Userinfo myuser = uo.retriveOne(user.getUserid()); user.setUsername(myuser.getUsername()); user.setUserpass(myuser.getUserpass()); user.setSex(myuser.getSex()); user.setSfz(myuser.getSfz()); user.setBirthday(myuser.getBirthday()); user.setWorktime(myuser.getWorktime()); user.setEmail(myuser.getEmail()); user.setInterest(myuser.getInterest()); user.setIntro(myuser.getIntro()); user.setXl(myuser.getXl()); return "addupdate"; } // 验证方法 // 增加时验证 public void validateCreate() { checkForm(); // 用户名是否存在 if(uo.checkUserName(user.getUsername())) this.addFieldError("username", "用户名已经被占用"); } // 修改时验证 public void validateUpdate() { checkForm(); if(uo.checkUserName(user.getUsername(),user.getUserid())) this.addFieldError("username", "用户名已经被占用"); } // 验证要求 public void checkForm() { HttpServletRequest request = ServletActionContext.getRequest(); // 用户名不能为空 if (user.getUsername() == null || user.getUsername().equals("")) this.addFieldError("username", "用户名不能为空"); // 密码不能为空 if (user.getUserpass() == null || user.getUsername().equals("")) this.addFieldError("userpass", "密码不能为空"); // 2次密码相同 else if (!user.getUserpass().equals(request.getParameter("userpass1"))) this.addFieldError("userpass", "2次密码不一样"); // 身份证不能为空 if (user.getSfz() == null || user.getSfz().equals("")) this.addFieldError("sfz", "身份证不能为空"); // 身份证必须是15或18位 else if (!user.getSfz().matches("^\\d{17}[\\d|X]|\\d{15}$")) this.addFieldError("sfz", "身份证必须是15位或18位"); // email不能为空 if (user.getEmail() == null || user.getEmail().equals("")) this.addFieldError("email", "Email不能为空"); else if (!user.getEmail().matches("^\\w+@\\w+(\\.\\w+)+$")) this.addFieldError("email", "Email格式错误"); // 生日不能为空 if (user.getBirthday() == null) this.addFieldError("birthday", "生日不能为空"); // 兴趣至少选一项 if (user.getInterest() == null) this.addFieldError("interest", "兴趣至少选一项"); // 工作年限1-100之间 if (user.getWorktime() < 1 || user.getWorktime() > 100) this.addFieldError("worktime", "工作年限必须在1-100之间"); // 简介不能为空 if (user.getIntro() == null || user.getIntro().equals("")) this.addFieldError("intro", "简介不能为空"); } } |
l Struts.xml中增加了验证出错时跳转到的页面<result name=”input”></result>。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <!-- 请求参数的中文处理 --> <constant name="struts.i18n.encoding" value="GBK"/> <!-- 修改后的xml自动加载 --> <constant name="struts.configuration.xml.reload" value="true"/> <package name="actions" extends="struts-default"> <action name="user" class="action.UserAction"> <result>/UserList.jsp</result> <result name="addupdate">/AddUpdate.jsp</result> <result name="input">/AddUpdate.jsp</result> </action> </package> </struts> |
- 3. validate框架(xml)验证
为了使用Struts2的框架验证文件进行输入验证,需要建立一个特定的验证规则文件,该文件是一个XML格式配置文件,文件命名规则为<Action类名-validation.xml>,保存在Action实现类相同的目录下,如果是多方法Action,需要使用Action的别名配置(每个方法映射为不同的Action别名,可以使用通配符),每个方法的验证文件名为<Action类名-别名-validation.xml>。这个例子中的验证文件的名字为:StuAction-validation.xml。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"> <!-- 验证规则定义根元素 --> <validators> <!-- 第一个验证字段:姓名name --> <field name="name"> <!-- 验证规则:非空(系统预先定义好的规则) --> <field-validator type="requiredstring"> <!-- 错误提示 --> <message>姓名不能为空</message> </field-validator> <!-- 验证规则:长度在5-10之间(系统预先定义好的规则) --> <field-validator type="stringlength"> <param name="minLength">5</param> <param name="maxLength">10</param> <!-- 错误提示 --> <message>姓名长度必须在${minLength}-${maxLength}个字符之间</message> </field-validator> </field> <!-- 第二个验证字段:年龄age --> <field name="age"> <field-validator type="conversion" short-circuit="true"> <!-- 错误提示 --> <message>必须输入整数</message> </field-validator> <field-validator type="int"> <param name="min">20</param> <param name="max">35</param> <!-- 错误提示 --> <message>年龄必须在${min}-${max}之间</message> </field-validator> </field> <!-- 第三个验证字段:分数mark --> <field name="mark"> <field-validator type="conversion" short-circuit="true"> <!-- 错误提示 --> <message>必须输入数字</message> </field-validator> <field-validator type="double"> <param name="minInclusive">50</param> <param name="maxInclusive">100</param> <!-- 错误提示 --> <message>分数必须在${minInclusive}-${maxInclusive}之间</message> </field-validator> </field> <!-- 第三个验证字段:入学时间enrolltime --> <field name="enrolltime"> <field-validator type="conversion" short-circuit="true"> <!-- 错误提示 --> <message>必须是日期格式</message> </field-validator> <field-validator type="required"> <!-- 错误提示 --> <message>入学时间不能为空</message> </field-validator> <field-validator type="date"> <param name="min">1990-01-01</param> <param name="max">2008-10-09</param> <!-- 错误提示 --> <message>入学时间必须在${min}到${max}之间</message> </field-validator> </field> </validators> |
上述验证文件中,short-circuit属性为true表示发生验证错误的时候,后续验证不再执行,即“短路”。
Action的局部资源文件StuAction.properties做如下修改,这样当数据类型转换异常的时候由验证框架文件来给出错误提示,而基本标签的提示指定为空,不然会出现重复提示:
invalid.fieldvalue.age= invalid.fieldvalue.mark= invalid.fieldvalue.enrolltime= |
重新运行原来的例子,显示验证错误结果信息如图2-9所示:
图2-9 使用验证框架文件的输入验证
四、 Strut2的国际化
l 在struts.xml中配置struts.custom.i18n.resources常量
<constant name="struts.custom.i18n.resources" value="globalMessages"/> |
l 在src目录下建立中文和英文的资源文件,中文资源文件globalMessages_zh_CN.properties的内容如下:
username=用户名 userpass=密码 success=登录成功 error=登录失败 login=登录 |
使用native2ascii工具把该文件转换为unicode编码。
l 英文资源文件globalMessages_en.properties的内容如下:
username=User Name userpass=Password success=Welcome error=Sorry! You can not log in login=Login |
l 编写登录页面Login.jsp,其内容如下:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <%@ taglib uri="/struts-tags" prefix="s" %> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'Login.jsp' starting page</title> </head> <body> <s:form action="dologin"> <s:textfield name="username" key="username"/> <s:password name="userpass" key="userpass"/> <s:submit key="login"/> </s:form> </body> </html> |
登录成功页面Success.jsp内容如下:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <%@ taglib uri="/struts-tags" prefix="s"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'Success.jsp' starting page</title> </head> <body> <s:property value="username"/> <s:text name="success"/> </body> </html> |
登录失败页面Error.jsp内容如下:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <%@ taglib uri="/struts-tags" prefix="s"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'Error.jsp' starting page</title> </head> <body> <s:text name="error"></s:text> </body> </html> |
上述页面中的Struts2标签都可以访问资源文件,表单标签<s:textfield name=”” key=””/>
中的属性key用于访问资源文件,<s:text name=””/>标签中的name属性用于访问资源文件。
五、 标签库和OGNL
在jsp页面中使用标签库指令引入后<% taglib uri=”/struts-tags” prefix=”s” %>,就可以使用所有的Struts2标签了。
六、 Struts2拦截器
拦截器(interceptor)是Struts2框架核心组成部分。很多功能都是构建在拦截器基础之上的,例如文件的上传和下载、国际化、数据类型转换和数据有效性验证等,Struts2利用内建的拦截器,完成了框架内的大部分操作。
拦截器就是动态拦截Action调用的对象。它提供了一种机制,使开发者可以定义一个特定的功能模块,这个模块可以在Action执行之前或之后运行,也可以在一个Action执行之前阻止Action执行。同时也提供了一种可以提取Action中可重用部分的方式。
Struts2框架的Action被一个或者多个拦截器(拦截器栈)所包围,所有的用户请求都会被拦截器所拦截,然后交给Action处理,处理结果以逻辑视图的方式返回给用户。而这个调用的执行流程,是由Strut2的配置文件(struts.xml)来实现的。
在前面几章中,没有明确说明拦截器,为什么可以直接调用Action呢?那是因为在Struts2框架中如果没有显式的拦截器配置,则系统会调用默认的拦截器来调用Action,在用户看来,好像没有配置拦截器一样。演示一个简单拦截器的开发:HelloWorld拦截器。
假设我们需要实现这么一个功能,在调用每个Action之前都能在控制台打印出“HelloWorld”。这样的一个功能使用Struts2拦截器来实现最简单。下面介绍一个具体的实现步骤。
- 建立一个Action类MyAction.java和配置文件Struts.xml;
l MyAction.java
package action; import com.opensymphony.xwork2.ActionSupport; public class MyAction extends ActionSupport { private int age;//年龄 public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String execute() throws Exception { // TODO Auto-generated method stub System.out.println("Action execute......"); return SUCCESS; } } |
l struts.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="action" extends="struts-default"> <action name="test" class="action.MyAction"> <result>Success.jsp</result> </action> </package> </struts> |
- 建立一个拦截器类HelloWorldInterceptor.java,其代码如下;
package interceptor; import action.MyAction; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; public class HelloWorldInterceptor extends AbstractInterceptor { //拦截方法 @Override public String intercept(ActionInvocation arg0) throws Exception { // 获得被调用的Action类 Object action = arg0.getAction(); //打印HelloWorld System.out.println("拦截器信息:HelloWorld!"); //执行Action或调用下一个拦截器 String result = arg0.invoke(); //执行完action后提示 System.out.println("Action执行完毕!"); return result; } } |
- 在struts.xml中加入拦截器的配置,见struts.xml内容;
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="action" extends="struts-default"> <!-- 定义拦截器 --> <interceptors> <interceptor name="helloworld" class="interceptor.HelloWorldInterceptor"/> </interceptors> <action name="test" class="action.MyAction"> <result>Success.jsp</result> <!-- action中引用拦截器 --> <interceptor-ref name="helloworld"/> </action> </package> </struts> |
- 编写JSP页面Success.jsp。
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <%@ taglib uri="/struts-tags" prefix="s" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>My JSP 'Success.jsp' starting page</title> </head> <body> <h1>调用成功</h1> <s:property value="age"/> </body> </html> |
- 运行该web程序,在地址栏输入http://localhost:8080/hw/test.action?age=35,运行效果如图4-1到4-2所示。
图4-1 执行Action后的输出
图4-2 interceptor运行结果
从图4-1我们可以看到,程序的运行有错误,就是参数age的值没有填充到Action的属性age中,所以页面上显示age就是0,那么这是为什么呢?是因为参数的值填充到名称相同的属性中这个过程也是依赖拦截器实现的,但是我们一旦在action中引用了自己定义的拦截器,那么系统默认的拦截器就没有了,所以我们只要在struts.xml的配置中让Action引用默认拦截器就正确了,默认拦截器是通过<package … extends=”struts-default”></package>中的extends引入的,修改后的struts.xml如下。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="action" extends="struts-default"> <!-- 定义拦截器 --> <interceptors> <interceptor name="helloworld" class="interceptor.HelloWorldInterceptor" /> </interceptors> <action name="test" class="action.MyAction"> <result>Success.jsp</result> <!-- action中引用默认拦截器 --> <interceptor-ref name="defaultStack" /> <!-- action中引用拦截器 --> <interceptor-ref name="helloworld" /> </action> </package> </struts> |
- 再次运行程序,输出结果正确,见图4-3所示。
图4-3 加入默认拦截器后的执行结果
七、 拦截器应用实例-文件上传和下载
Struts2框架默认使用Common-fileUpload组件实现文件上传,该组件将解析出HttpServletRequest请求中的文件域信息,并使用IO流方式,将文件保存在服务器的指定位置。
Struts2框架中本身不带有Common-fileUpload组件的jar包,需要自己下载后放在web程序的WEB-INF/lib目录中,需要2个jar包,一个是commons-fileupload-1.2.1.jar,可从网址http://jakarta.apache.org/commons/fileupload下载,另一个是commons-io-1.4.jar,可从网址http://jakarta.apache.org/commons/io/。
l 文件上传
上传单个文件的JSP页面代码如下:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <%@ taglib uri="/struts-tags" prefix="s" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>My JSP 'index.jsp' starting page</title> </head> <body> <s:form action="fileupload" method="post" enctype="multipart/form-data"> 上传文件:<s:file name="doc"/><br> <s:submit value="上传"/> </s:form> </body> </html> |
form表单的enctype属性设置为multipart/form-data。enctype用来指定表单数据的编码方式,有如下3个值。
- application/x-www-form-urlencoded:指定该值,则表单中的数据被编码为Key-Value对,这是默认的编码方式。
- multipart/form-data:使用mime编码,会以二进制流的方式来处理表单数据,文件上传需要使用该编码方式。
- text/plain:表单数据以纯文本形式进行编码,其中不含任何控件和格式字符。
file类型表单域doc用于选择上传文件,它和Action中的java.io.File类型的属性doc对应,同时上传文件的文件名对应于Action中的属性docFileName,上传文件的文件类型对应于Action中的属性docContentType。一般说来,为了上传文件,如果表单域名称为xxx,那么在Action中应建立如下3个属性来接收上传文件的信息。
private java.io.File xxx;//封装上传文件的二进制内容 private String xxxContentType;//封装上传文件的文件类型 private String xxxFileName;//封装上传文件的文件名 |
封装上传文件的数据类FileInfo.java代码如下:
package po; import java.io.File; public class FileInfo { private File doc; //封装上传文件的属性 private String docFileName; //封装上传文件的名称属性 private String docContentType; //封装上传文件的类型属性 private String targetdir; //保存路径 private String targetfilename; //保存的文件名 public File getDoc() { return doc; } public void setDoc(File doc) { this.doc = doc; } public String getDocContentType() { return docContentType; } public void setDocContentType(String docContentType) { this.docContentType = docContentType; } public String getDocFileName() { return docFileName; } public void setDocFileName(String docFileName) { this.docFileName = docFileName; } public String getTargetdir() { return targetdir; } public void setTargetdir(String targetdir) { this.targetdir = targetdir; } public String getTargetfilename() { return targetfilename; } public void setTargetfilename(String targetfilename) { this.targetfilename = targetfilename; } } |
用于文件上传的Action代码如下:
package action; import java.io.File; import org.apache.commons.io.FileUtils; import org.apache.struts2.ServletActionContext; import po.FileInfo; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven; public class FileUploadAction extends ActionSupport implements ModelDriven<FileInfo> { // 封装文件信息 FileInfo fileinfo = new FileInfo(); public FileInfo getModel() { // TODO Auto-generated method stub return fileinfo; } @Override public String execute() throws Exception { // TODO Auto-generated method stub // 获得服务器上保存上传文件的目录updfile的绝对路径 String realpath = ServletActionContext.getServletContext().getRealPath( "/updfile"); // 设置保存文件的目录 fileinfo.setTargetdir(realpath); // 设置目标文件名 fileinfo.setTargetfilename(generateFileName(fileinfo.getDocFileName())); // 把doc内容复制到target FileUtils.copyFile(fileinfo.getDoc(), new File(fileinfo.getTargetdir(), fileinfo.getTargetfilename())); return SUCCESS; } // 产生唯一的文件名 private synchronized String generateFileName(String filename) { int position = filename.lastIndexOf("."); String ext = filename.substring(position); return System.nanoTime() + ext; } } |
Action的配置文件struts.xml。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="action" extends="struts-default"> <action name="fileupload" class="action.FileUploadAction"> <result>Success.jsp</result> </action> </package> </struts> |
显示结果页面Success.jsp。
<%@ page language="java" import="java.util.*" pageEncoding="GBK"%> <%@ taglib uri="/struts-tags" prefix="s" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>My JSP 'Success.jsp' starting page</title> </head> <body> 上传文件类型:<s:property value="docContentType"/><br> 上传成功后文件位置:<s:property value="targetdir"/><br> 上传图片:<img width=200 height=200 src="<s:property value="'updfile/'+targetfilename"/>"><br> </body> </html> |
部署后运行,效果如图4-4、图4-5所示。
图4-4 选择文件上传
图4-5 图片上传后显示
l 上传文件的过滤
上传文件的时候可以限制上传文件的类型和大小,使用拦截器来实现。文件上传的拦截器是系统拦截器,我们只要配置参数就可以了。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <!-- 上传文件的临时保存目录 --> <constant name="struts.multipart.saveDir" value="/tmp" /> <package name="action" extends="struts-default"> <action name="fileupload" class="action.FileUploadAction"> <result>/Success.jsp</result> <result name="input">/index.jsp</result> <interceptor-ref name="fileUpload"> <!-- 允许上传的文件类型 --> <param name="allowedTypes"> image/bmp,image/png,image/gif,image/jpeg </param> <!--上传文件的最大容量 单位字节 --> <param name="maximumSize">20000</param> </interceptor-ref> <interceptor-ref name="defaultStack" /> </action> </package> </struts> |
重新运行该程序,选择一个比较大的文件,单击“上传”按钮,结果如图4-6所示。
图4-6 文件太大
图4-7 文件类型不对
错误提示都是英文,如果要改成中文提示,需要配置全局资源文件globalMessage_zh_CN.proeprties,内容如下:
struts.messages.error.file.too.large=上传文件太大 struts.messages.error.content.type.not.allowed=上传文件必须是图像文件 struts.messages.error.uploading=上传过程出现异常 |
转换编码成为unicode编码。然后在struts.xml中引入该资源文件<constant name="struts.custom.i18n.resources" value="globalMessage" />。
重新运行后,效果如图4-8和4-9所示。
图4-8 提示文件类型不对
图4-9 提示上传文件太大
在struts.xml中配置<constant name="struts.multipart.saveDir" value="/tmp" />用于指定上传文件保存的临时目录,上传完成后系统会自动删除临时目录中的内容。
l 文件下载
文件下载可以通过配置struts.xml中result的类型来实现,执行下载操作的Action代码如下所示:
package action; import java.io.InputStream; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionSupport; public class FileDownloadAction extends ActionSupport { private String inputpath; //下载文件路径 private String contenttype; //文件类型 private String filename; //文件名 //返回一个InputStream类型 public java.io.InputStream getInputStream() { return ServletActionContext.getServletContext().getResourceAsStream(inputpath); } @Override public String execute() throws Exception { //调用相关业务逻辑方法 动态设置下载信息 inputpath = "/updfile/Bliss.jpg"; contenttype = "image/jpeg"; //解决下载的中文文件名问题 filename = java.net.URLEncoder.encode("文件.jpg","utf-8"); return SUCCESS; } public String getContenttype() { return contenttype; } public void setContenttype(String contenttype) { this.contenttype = contenttype; } public String getFilename() { return filename; } public void setFilename(String filename) { this.filename = filename; } public String getInputpath() { return inputpath; } public void setInputpath(String inputpath) { this.inputpath = inputpath; } } |
上述代码中的java.io.InputStream getInputStream()方法必须有,它返回要下载文件的二进制输入流。
struts.xml中配置下载Action的result类型为stream,内容如下:
<action name="filedownload" class="action.FileDownloadAction"> <result name="success" type="stream"> <!-- 定义相关参数 --> <param name="contentType">${contenttype}</param> <param name="inputName">inputStream</param> <param name="bufferSize">4096</param> <param name="contentDisposition">attachment;filename=${filename}</param> </result> </action> |
地址栏输入http://localhost:8080/upd/filedownload.action,出现如图4-10所示画面。
图4-10 下载图片
八、 操作案例:验证框架实现CRUD
使用验证框架后,增加了一些新的文件,工程文件结构如图2-19所示。
图2-19 加入验证框架后的工程结构
表2-5 案例3增加的文件说明
文件名 |
说明 |
UserAction-usercreate-validation.xml |
调用Action的create()方法时执行的验证框架文件 |
UserAction-userupdate-validation.xml |
调用Action的update()方法时执行的验证框架文件 |
UserAction.properties |
Action局部资源文件,用于屏蔽标签自带的提示信息 |
Userinfo-myContext-validation.xml |
Visitor验证类型需要的独立类的验证框架文件 |
UsernameUnique.java |
自定义的验证类 |
validators.xml |
加入自定义的验证类后验证类描述文件 |
- 修改UserAction.java文件,添加模型类的set/get方法,去掉案例2中添加的validate方法;
package action; import oper.UserOper; import po.Userinfo; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven; public class UserAction extends ActionSupport implements ModelDriven<Userinfo>{ // 数据模型 private Userinfo user=new Userinfo(); // 业务类 private UserOper uo = new UserOper(); public Userinfo getModel() { // TODO Auto-generated method stub return user; } // 增加前 public String precreate() throws Exception { return "addupdate"; } // 增加 public String create() throws Exception { uo.create(user); return select(); } // 删除 public String delete() throws Exception { uo.delete(user.getUserid()); return select(); } // 修改 public String update() throws Exception { uo.update(user); return select(); } // 查询 public String select() throws Exception { ActionContext.getContext().put("userlist", uo.retriveAll()); return SUCCESS; } // 查询单个 public String retrive() throws Exception { Userinfo myuser = uo.retriveOne(user.getUserid()); user.setUsername(myuser.getUsername()); user.setUserpass(myuser.getUserpass()); user.setSex(myuser.getSex()); user.setSfz(myuser.getSfz()); user.setBirthday(myuser.getBirthday()); user.setWorktime(myuser.getWorktime()); user.setEmail(myuser.getEmail()); user.setInterest(myuser.getInterest()); user.setIntro(myuser.getIntro()); user.setXl(myuser.getXl()); return "addupdate"; } public Userinfo getUser() { return user; } public void setUser(Userinfo user) { this.user = user; } } |
- 用别名通配符来配置Action的调用路径,修改struts2.xml为如下内容;
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <!-- 请求参数的中文处理 --> <constant name="struts.i18n.encoding" value="GBK"/> <!-- 修改后的xml自动加载 --> <constant name="struts.configuration.xml.reload" value="true"/> <package name="actions" extends="struts-default"> <action name="user*" class="action.UserAction" method="{1}"> <result>/UserList.jsp</result> <result name="addupdate">/AddUpdate.jsp</result> <result name="input">/AddUpdate.jsp</result> </action> </package> </struts> |
- 为create和update方法编写验证框架文件,命名方式为:<Action类名>-<别名>-validation.xml,和Action类放在同一个目录下;
l UserAction-usercreate-validation.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"> <!-- 验证规则定义根元素 --> <validators> <field name="user"> <field-validator type="visitor"> <!-- 关联文件带有的名称 --> <param name="context">myContext</param> <!-- 表单字段的属性不用带字段名的前缀 --> <param name="appendPrefix">false</param> <message></message> </field-validator> </field> </validators> |
l UserAction-userupdate-validation.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"> <!-- 验证规则定义根元素 --> <validators> <field name="user"> <field-validator type="visitor"> <!-- 关联文件带有的名称 --> <param name="context">myContext</param> <!-- 表单字段的属性不用带字段名的前缀 --> <param name="appendPrefix">false</param> <message></message> </field-validator> </field> </validators> |
- 编写模型类的验证框架文件,命名方式:<模型类名>-<context名>-validation.xml,这里的名称就是Userinfo-myContext-validation.xml,要和模型类放在同一目录中。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"> <!-- 验证规则定义根元素 --> <validators> <field name="username"> <field-validator type="requiredstring" short-circuit="true"> <message>用户名不能为空</message> </field-validator> <!-- 自定义的验证规则 用于判断用户名是否已经被占用 --> <field-validator type="usernameunique"> <message>用户名已经被占用</message> </field-validator> </field> <field name="userpass"> <field-validator type="requiredstring" short-circuit="true"> <message>密码不能为空</message> </field-validator> <field-validator type="fieldexpression"> <param name="expression"> <![CDATA[userpass==userpass1]]> </param> <message>2次输入密码不同</message> </field-validator> </field> <field name="sfz"> <field-validator type="requiredstring"> <message>身份证不能为空</message> </field-validator> <field-validator type="regex"> <param name="expression"> <![CDATA[\d{15}|\d{17}[\d|X]]]> </param> <message>身份证必须是15或18位</message> </field-validator> </field> <field name="birthday"> <field-validator type="conversion" short-circuit="true"> <message>生日格式错误</message> </field-validator> <field-validator type="required"> <message>生日不能为空</message> </field-validator> </field> <field name="email"> <field-validator type="requiredstring"> <message>email不能为空</message> </field-validator> <field-validator type="regex"> <param name="expression"> <![CDATA[\w+@\w+(\.\w+)+]]> </param> <message>email格式错误</message> </field-validator> </field> <field name="worktime"> <field-validator type="conversion" short-circuit="true"> <message>必须是整数</message> </field-validator> <field-validator type="int"> <param name="min">1</param> <param name="max">100</param> <message>必须是${min}-${max}的整数</message> </field-validator> </field> <field name="intro"> <field-validator type="requiredstring"> <message>简介不能为空</message> </field-validator> </field> </validators> |
- 编写自定义的验证类,用于验证用户名是否被占用;
package validator; import oper.UserOper; import po.Userinfo; import com.opensymphony.xwork2.validator.FieldValidator; import com.opensymphony.xwork2.validator.ValidationException; import com.opensymphony.xwork2.validator.ValidatorContext; public class UsernameUnique implements FieldValidator { private String fieldName; private String defaultMessage; private ValidatorContext validatorContext; private String validatorType; private String message; private String messageKey; public String getFieldName() { return this.fieldName; } public void setFieldName(String arg0) { this.fieldName = arg0; } public String getDefaultMessage() { return this.defaultMessage; } public String getMessage(Object arg0) { return this.message; } public String getMessageKey() { return this.messageKey; } public ValidatorContext getValidatorContext() { return this.validatorContext; } public String getValidatorType() { return this.validatorType; } public void setDefaultMessage(String arg0) { this.defaultMessage = arg0; } public void setMessageKey(String arg0) { this.messageKey = arg0; } public void setValidatorContext(ValidatorContext arg0) { this.validatorContext = arg0; } public void setValidatorType(String arg0) { this.validatorType = arg0; } public void validate(Object arg0) throws ValidationException { // 得到用户名和编号 int userid = ((Userinfo) arg0).getUserid(); String username = ((Userinfo) arg0).getUsername(); // 验证是否存在 UserOper uo = new UserOper(); boolean b = false; if (userid > 0) b = uo.checkUserName(username, userid); else b = uo.checkUserName(username); //存在 加入FieldError if (b) this.validatorContext.addFieldError(this.fieldName, this.defaultMessage); } } |
- 把自定义的验证类添加到默认的验证类文件中,文件名validators.xml,放在src目录中。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator Config 1.0//EN" "http://www.opensymphony.com/xwork/xwork-validator-config-1.0.dtd"> <!-- START SNIPPET: validators-default --> <validators> <validator name="required" class="com.opensymphony.xwork2.validator.validators.RequiredFieldValidator"/> <validator name="requiredstring" class="com.opensymphony.xwork2.validator.validators.RequiredStringValidator"/> <validator name="int" class="com.opensymphony.xwork2.validator.validators.IntRangeFieldValidator"/> <validator name="double" class="com.opensymphony.xwork2.validator.validators.DoubleRangeFieldValidator"/> <validator name="date" class="com.opensymphony.xwork2.validator.validators.DateRangeFieldValidator"/> <validator name="expression" class="com.opensymphony.xwork2.validator.validators.ExpressionValidator"/> <validator name="fieldexpression" class="com.opensymphony.xwork2.validator.validators.FieldExpressionValidator"/> <validator name="email" class="com.opensymphony.xwork2.validator.validators.EmailValidator"/> <validator name="url" class="com.opensymphony.xwork2.validator.validators.URLValidator"/> <validator name="visitor" class="com.opensymphony.xwork2.validator.validators.VisitorFieldValidator"/> <validator name="conversion" class="com.opensymphony.xwork2.validator.validators.ConversionErrorFieldValidator"/> <validator name="stringlength" class="com.opensymphony.xwork2.validator.validators.StringLengthFieldValidator"/> <validator name="regex" class="com.opensymphony.xwork2.validator.validators.RegexFieldValidator"/> <validator name="usernameunique" class="validator.UsernameUnique"/> </validators> <!-- END SNIPPET: validators-default --> |
- 部署运行。