Struts2中的ModelDriven机制及其运用、refreshModelBeforeResult属性解决的问题

1.为什么需要ModelDriven? 
所谓ModelDriven,意思是直接把实体类当成页面数据的收集对象。比如,有实体类User如下:


package cn.com.leadfar.struts2.actions; 
public class User { 
private int id; 
private String username; 
private String password; 
private int age; 
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 int getAge() { 
return age; 

public void setAge(int age) { 
this.age = age; 

public String getAddress() { 
return address; 

public void setAddress(String address) { 
this.address = address; 

public int getId() { 
return id; 

public void setId(int id) { 
this.id = id; 

假如要写一个Action,用来添加User。 
第一种做法是直接在Action中定义所有需要的属性,然后在JSP中直接用属性名称来提交数据: 
UserAction:


public class UserAction { 
private int id; 
private String username; 
private String password; 
private int age; 
private String address; 
public String add(){ 
User user = new User(); 
user.setId(id); 
user.setUsername(username); 
user.setPassword(password); 
user.setAge(age); 
user.setAddress(address); 
new UserManager().addUser(user); 
return "success"; 

public int getId() { 
return id; 

public void setId(int id) { 
this.id = id; 

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 int getAge() { 
return age; 

public void setAge(int age) { 
this.age = age; 

public String getAddress() { 
return address; 

public void setAddress(String address) { 
this.address = address; 

}

add_input.jsp:


<form action="test/user.action" method="post"> 
<input type="hidden" name="method:add"> 
username:<input type="text" name="username"> <br/> 
password:<input type="text" name="password"> <br/> 
age:<input type="text" name="age"> <br/> 
address:<input type="text" name="address"> <br/> 
<input type="submit" name="submit" value="添加用户"> 
</form> <br/>

上述做法不好之处是:如果实体类的属性非常多,那么Action中也要定义相同的属性。 
第二种做法是将User对象定义到UserAction中,然后在JSP中通过user属性来给user赋值: 
UserAction:


public class UserAction { 
private User user; 
public String add(){ 
new UserManager().addUser(user); 
return "success"; 

public User getUser() { 
return user; 

public void setUser(User user) { 
this.user = user; 

}

add_input.jsp:


<form action="test/user.action" method="post"> 
<input type="hidden" name="method:add"> 
username:<input type="text" name="user.username"> <br/> 
password:<input type="text" name="user.password"> <br/> 
age:<input type="text" name="user.age"> <br/> 
address:<input type="text" name="user.address"> <br/> 
<input type="submit" name="submit" value="添加用户"> 
</form> <br/>

这种做法不好的地方是:JSP页面上表单域中的命名变得太长 
第三种做法是利用ModelDriven机制,让UserAction实现一个ModelDriven接口,同时实现接口中的方法:getModel()。如下所示:


public class UserAction implements ModelDriven{ 
private User user; 
@Override 
public Object getModel() { 
if(user == null){ 
user = new User(); 

return user; 

public String add(){ 
new UserManager().addUser(user); 
return "success"; 

public User getUser() { 
return user; 

public void setUser(User user) { 
this.user = user; 

}

JSP的代码如下:


<form action="test/user.action" method="post"> 
<input type="hidden" name="method:add"> 
username:<input type="text" name="username"> <br/> 
password:<input type="text" name="password"> <br/> 
age:<input type="text" name="age"> <br/> 
<input type="submit" name="submit" value="添加用户"> 
</form> <br/>

可见,第三种做法是比较好的,Action和JSP写起来都比较简单。 
2.ModelDriven背后的机制? 
ModelDriven背后的机制就是ValueStack。界面通过:username/age/address这样的名称,就能够被直接赋值给user对象,这证明user对象正是ValueStack中的一个root对象! 
那么,为什么user对象会在ValueStack中呢?它是什么时候被压入ValueStack的呢?答案是:ModelDrivenInterceptor(关于Interceptor的概念,请参考后续章节的说明)。ModelDrivenInterceptor是缺省的拦截器链的一部分,当一个请求经过ModelDrivenInterceptor的时候,在这个拦截器中,会判断当前要调用的Action对象是否实现了ModelDriven接口,如果实现了这个接口,则调用getModel()方法,并把返回值(本例是返回user对象)压入ValueStack。 
请看ModelDrivenInterceptor的代码:


public class ModelDrivenInterceptor extends AbstractInterceptor { 
protected boolean refreshModelBeforeResult = false; 
public void setRefreshModelBeforeResult(boolean val) { 
this.refreshModelBeforeResult = val; 

@Override 
public String intercept(ActionInvocation invocation) throws Exception { 
Object action = invocation.getAction(); 
if (action instanceof ModelDriven) { 
ModelDriven modelDriven = (ModelDriven) action; 
ValueStack stack = invocation.getStack(); 
Object model = modelDriven.getModel(); 
if (model != null) { 
stack.push(model); 

if (refreshModelBeforeResult) { 
invocation.addPreResultListener(new RefreshModelBeforeResult(modelDriven, model)); 


return invocation.invoke(); 
}

从ModelDrivenInterceptor中,即可以看到model对象被压入ValueStack中! 
其中的refreshModelBeforeResult是为了接下来描述的一个问题而提供的解决方法。 
理解常见的陷阱及解决办法 
假设我们要更新一个实体对象,那么第一步首先是打开更新界面,请看下述模拟打开更新界面的代码:


public class UserAction implements ModelDriven{ 
private User user; 
@Override 
public Object getModel() { 
if(user == null){ 
user = new User(); 
//user.setUsername("这是原来的User对象"); 

return user; 

public String updateInput(){ 
//根据ID,查询数据库,得到User对象 
user = new UserManager().findUserById(user.getId()); 
return "update_input"; 
}

上述代码中,new UserManager().findUserById(user.getId());这一行,将从数据库中查询相应的记录,同时转换为User对象返回。而return “update_input”;将转向更新显示页面。 
更新页面如下:


<form action="test/user.action" method="post"> 
<input type="hidden" name="method:update"> 
id:<input type="text" name="id" value="<s:property value="id"/>"> <br/> 
username:<input type="text" name="username" value="<s:property value="username"/>"><br/> 
password:<input type="text" name="password" value="<s:property value="password"/>"><br/> 
age:<input type="text" name="age" value="<s:property value="age"/>"> <br/> 
address:<input type="text" name="address" value="<s:property value="address"/>"><br/> 
<input type="submit" name="submit" value="更新用户"> 
</form> <br/>

上述代码运行起来之后,你在更新界面上将看不到数据(id属性有值,其它属性无显示)。关键的原因是在执行到updateInput之前,user对象(在getMode()方法中创建的对象)被压到ValueStack中,这时候,UserAction和ValueStack都指向同一个user对象;但紧接着,UserAction中的user被一个新的user对象覆盖,这时候,UserAction和ValueStack不再指向同一个user对象!ValueStack中是旧的user对象,而UserAction中是新的user对象!我们在JSP中,直接通过username/address等直接访问,当然是要访问ValueStack中的旧user对象,所以它们的属性都是空的(id属性除外)! 
理解上述问题很重要,当你理解了问题,那么问题的解决方法就可以有很多了: 
比如,你可以把新对象的属性拷贝到旧对象上;比如,你可以先把旧对象从ValueStack中移除,然后再把新对象压入ValueStack等…… 
在最新的struts2版本中,ModelDrivenInterceptor提供了一个配置参数:refreshModelBeforeResult,只要将它定义为true,上述问题就被解决了!struts2的解决方案就是:先把旧的model对象从ValueStack中移除,然后再把新的model对象压入ValueStack! 
结果: 
更新ValueStack中的model对象,先把旧的model对象从ValueStack中移除,然后再把新的model对象压进ValueStack!
官方解释: 
set to true if you want the model to be refreshed on the value stack after action execution and before result execution. The setting is useful if you want to change the model instance during the action execution phase, like when loading it from the data layer. This will result in getModel() being called at least twice.

转载于:https://www.cnblogs.com/lxzzlxz/p/10165174.html

struts中ModelDriven()接口相关推荐

  1. struts2 中 Preparable 接口实现数据准备

    今天才知道struts还有Preparable接口,实现此接口需要实现其prepare()方法,调用action中其他方法之前会先调用prepare()方法.此接口和方法可以用于初始化一些数据. 测试 ...

  2. 1. Action 实现 ModelDriven 接口后的运行流程

    1). 先会执行 ModelDrivenInterceptor 的 intercept 方法. public String intercept(ActionInvocation invocation) ...

  3. C++中的接口(抽象类)

    1.Cpp中的接口(抽象类) 接口描述了类的行为和功能,而不需要完成类的特定实现.接口是使用抽象类来实现的,抽象类与数据抽象互不混淆,数据抽象是一个把实现细节与相关的数据分离开的概念.如果类中至少有一 ...

  4. Java中实现接口与继承的区别

    ** Java中实现接口与继承的区别 ** 首先,先来了解一下什么是接口和继承.接口一般是使用interface来定义的.接口定义同类的定义类似,分为接口的声明和接口体,其中接口体由常量定义和方法定义 ...

  5. Objective-C 入门(七)协议 protocol(JAVA中的接口)

    Objective-C 入门(七)协议 protocol(JAVA中的接口) 接口的作用想必大家都比较了解 OV中的 protocol 相比接口作用相似 语法稍有不同 1.先来看声明一个协议 在创建文 ...

  6. Android中Parcelable接口用法

    --  通过writeToParcel将你的对象映射成Parcel对象,再通过createFromParcel将Parcel对象映射成你的对象.也可以将Parcel看成是一个流,通过writeToPa ...

  7. Java中的接口命名[关闭]

    本文翻译自:Interface naming in Java [closed] Most OO languages prefix their interface names with a capita ...

  8. 初步解读Golang中的接口相关编写方法

    初步解读Golang中的接口相关编写方法 概述如果说goroutine和channel是Go并发的两大基石,那么接口是Go语言编程中数据类型的关键.在Go语言的实际编程中,几乎所有的数据结构都围绕接口 ...

  9. struts中简单的校验

    Struts中简单的校验 "计应134(实验班) 凌豪" Struts2校验简要说明:struts2中通常情况下,类型转换要在数据校验之前进行.类型转换其实也是基本的服务器端校验, ...

最新文章

  1. java什么时候用list_Java快问快答:用 ArrayList 还是 LinkedList?
  2. 浅析综合布线系统中检测双绞线的几种方式
  3. tablestore换mysql_mysql数据迁移到tablestore
  4. 用SCCM2007 R2管理Windows更新,SCCM系列之六
  5. android 卡片旋转动画,Android 卡片翻转效果
  6. 使用OAuth2令牌的安全REST服务
  7. u8系统怎么连接服务器,用友U8 怎么连接远程服务器
  8. 大数据驱动乡村振兴共享共治机制研究
  9. linux 网络 PING IP可以通,ping域名ping不通
  10. 321电商学院 与华中师大联手 - 2014-10-22
  11. Vue使用js读取Excel数据
  12. 三小时学会HTML(菜鸟教程精华版)
  13. 记一次西安thoughtworks的面试经历
  14. python超清壁纸_Python爬取5K分辨率超清唯美壁纸
  15. android webview静态方法,在android webview中加载静态页面
  16. uefi开发环境搭建
  17. PPT画图软件,强烈推荐!提升能力的利器。
  18. 第一款个人应用——《不做手机控》——终于上线啦!
  19. vue3+ts+vite后台管理模板
  20. web 前端的浏览器

热门文章

  1. CVPR2019| 最新CVPR2019论文:含目标检测、分割、深度学习、GAN等领域
  2. c++图片背景替换为白色_4种方法,3秒快速更换证件照背景!你还要去照相馆花冤枉钱吗?...
  3. 九连环_儿子的玩具—九连环
  4. MySQL关联eclipse_MySQL JDBC 连接数据库基本操作
  5. 前端代码获取文件大小_vue技术栈前端建设方案
  6. centos7 linux关闭端口占用,CentOS7使用firewalld打开关闭防火墙、端口
  7. 金融工程与计算机联系紧密吗,美国留学金融工程专业院校有什么推荐的呢?
  8. 图像处理-RBG图像和灰度图像
  9. 渗透测试入门23之OSCP渗透测试认证经验分享
  10. python 接口自动化的sql验证_基于Python的接口自动化实战-基础篇之pymysql模块操做数据库...