ModelDriven模型驱动
把动作和模型隔开
在使用 Struts 作为前端的企业级应用程序时把动作和模型清晰地隔离开是有必要的: 有些动作类不代表任何模型对象, 它们的功能仅限于提供图文显示服务
ModelDriven 拦截器
- 情景: 有一个用来处理客户的 CustomerAction 类, 该动作类实现了 ModelDriven 接口.
- 当用户触发CustomerAction 动作时, ModelDriven 拦截器将调用相关CustomerAction 对象的 getModel() 方法, 并把返回的模型(Customer实例)压入到 ValueStack 栈. 接下来 Parameters拦截器将把表单字段映射到 ValueStack 栈的栈顶对象的各个属性中. 因为此时 ValueStack栈的栈顶元素是刚被压入的模型(Product)对象, 所以该模型将被填充. 如果某个字段在模型里没有匹配的属性, Param 拦截器将尝试ValueStack 栈中的下一个对象.
- 一个模型类必须有一个不带任何参数的构造器.
1.编写jsp页面
test.jsp
<%@ page language="java" pageEncoding="utf-8" contentType="text/html; charset=utf-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<html>
<head>
<title>My JSP 'index.jsp' starting page</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/model/userAction_add.action">用户信息录入</a><br>
<br>
<br>
<a href="${pageContext.request.contextPath}/model/userAction_edit.action">用户信息修改</a><br>
</body>
</html>
add.jsp页面
<%@ page language="java" pageEncoding="utf-8" contentType="text/html; charset=utf-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<html>
<head>
<title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="description" content="page">
</head>
<body>
<s:debug></s:debug>
<s:form name="form1" namespace="/model" action="userAction_save" method="post" theme="simple">
用户名:<s:textfield name="username"/><br>
电话:<s:textfield name="tel" /><br>
描述:<s:textfield name="des" /><br>
<s:token></s:token>
<s:submit type="submit" value="保存"></s:submit>
</s:form>
</body>
</html>
edit.jsp页面
<%@ page language="java" pageEncoding="utf-8" contentType="text/html; charset=utf-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<html>
<head>
<title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="description" content="page">
</head>
<body>
<s:debug></s:debug>
<s:form name="form1" namespace="/model" action="userAction_update" method="post" theme="simple">
用户名:<s:textfield name="username"/><br>
电话:<s:textfield name="tel" /><br>
描述:<s:textfield name="des" /><br>
<s:token></s:token>
<s:submit type="submit" value="保存"></s:submit>
</s:form>
</body>
</html>
2.配置struts_model.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>
<action name="userAction_*" class="cn.itcast.model.UserAction" method="{1}">
<result name="add">/model/add.jsp</result>
<result name="edit">/model/edit.jsp</result>
<result name="success">/model/success.jsp</result>
<!-- 配置表单重复提交后,要转向到的页面 -->
<result name="invalid.token">/model/error.jsp</result>
</action>
</package>
</struts>
3.struts.xml文件引入struts_model.xml文件
4.编写Action文件
配置ModelDriven 拦截器
让UserAction类实现ModelDriven接口
模型驱动的目的是将对象放入到值栈顶,由struts2中的模型驱动拦截器处理
例:
public class UserAction extends ActionSupport implements ModelDriven<CustomAction> {
public CustomAction getModel() {
return user;
}
}
UserAction
package cn.itcast.model;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import com.opensymphony.xwork2.util.ValueStack;
@SuppressWarnings("serial")
public class UserAction extends ActionSupport implements ModelDriven<User> {
private User user = new User();
/*public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}*/
public User getModel() {
return user;
}
public String add(){
System.out.println("UserAction ************* add()");
return "add";
}
public String save(){
System.out.println("UserAction ************* save()");
return "success";
}
public String edit(){
System.out.println("UserAction ************* edit()");
//模拟数据库查询
User newUser = new User();
newUser.setUsername("zhang");
newUser.setTel("13888888888");
newUser.setDes("xxx");
//页面回显技术
//方法一
// user.setUsername(newUser.getUsername());
// user.setTel(newUser.getTel());
// user.setDes(newUser.getDes());
//方法二
ValueStack valueStack = ServletActionContext.getContext().getValueStack();
valueStack.pop();
valueStack.push(newUser);
return "edit";
}
public String update(){
System.out.println("UserAction ************* update()");
return "success";
}
}
编写bean对象
User对象
package cn.itcast.model;
public class User {
private String username;
private String tel;
private String des;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getTel() {
return tel;
}
public void setTel(String tel) {
this.tel = tel;
}
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
}
ModelDriven 拦截器—底层代码
struts2标签自动回显
struts2标签如何自动回显
回显结果:
问题:user=newUser可以吗?
Token Interceptor 处理表单重复提交
如何处理表单重复提交?
1.定义一个jsp页面
在jsp页面增加一个隐藏域
<s:token></s:token>
2.定义一个struts_token.xml配置文件
3.配置struts.xml文件
引入自定义struts_token.xml配置文件
error.jsp页面打印错误信息
<%@ page language="java" pageEncoding="utf-8" contentType="text/html; charset=utf-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<html>
<head>
<title>My JSP 'index.jsp' starting page</title>
</head>
<body>
<s:actionerror/>
表单重复提交!!!!<br>
</body>
</html>
配置错误信息国际化资源文件
引入配置的资源文件
<s:token />
标签防止重复提交
总结
模型驱动:
* 要从页面中获取表单元素的值,需要在动作类中声明与页面元素同名的属性。导致动作类中既有javabean又有业务方法。
* 将javabean和业务方法进行分离:
* 将重新创建一个javabean,将javabean的内容放置其中。
* 动作类action中只留业务方法
* 在动作类中声明的javabean无法从页面中获取同名的属性
* 需要使用struts2框架提供"ModelDriven(模型驱动)"
* 实现ModelDriven这个接口
* 重写getModel()方法,返回该javabean的实例
* 代码如下:
public class UserAction extends ActionSupport implements ModelDriven<User> {
private User user = new User();
public User getModel() {
return user;
}
public String add(){
System.out.println("UserAction ************* add()");
return "add";
}
public String save(){
System.out.println("UserAction ************* save()");
return "success";
}
}
* 模型驱动的原理:
* 在不使用模型驱动的时候,之所以在动作类中获取不到对应的属性的原因:
* 在ValueStack中没有对应javabean的所有属性
* 模型驱动的作用,就是将javabean的实例压入对象栈的栈顶,从而可以获取到对应的属性的值
页面回显技术:
* 通过模型驱动,在动作类action中,可以获取到页面中元素的值
//方法一
* 通过javabean实例的set()方法,将新的内容set到javabean中,从而放置在页面对应元素中
user.setUsername(newUser.getUsername());
user.setTel(newUser.getTel());
user.setDes(newUser.getDes());
//方法二
* 首先将值栈中,旧的javabean的内容,删除掉
* 然后将新的内容压入到值栈中的栈顶
ValueStack valueStack = ServletActionContext.getContext().getValueStack();
valueStack.pop();
valueStack.push(newUser);
处理表单重复提交:
* 在页面中增加一个隐藏域:<s:token></s:token>
* 创建一个struts.xml的配置文件,具体配置如下:
<!-- 配置默认执行的拦截器栈,增加令牌拦截器 -->
<interceptors>
<interceptor-stack name="tokenStack">
<interceptor-ref name="defaultStack" />
<interceptor-ref name="token" >
<!-- 配置令牌拦截器,拦截的方法名,如果配置多个方法时,用","隔开 -->
<param name="includeMethods">save,update</param>
</interceptor-ref>
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="tokenStack" />
* 在struts.xml配置文件中,增加一个result结果类型:
<!-- 配置表单重复提交后,要转向到的页面 -->
<result name="invalid.token">/model/error.jsp</result>
* 在表单重复提交后,要转向到的页面中通过<s:actionerror>获取struts2框架底层提供错误提示信息
* 将struts2框架底层提供错误提示信息改成中文:
* 在与动作类action同级目录下,创建名为"token.properties"的资源文件,文件内容如下:
struts.messages.invalid.token=表单重复提交,请刷新后重试!