package cn.com.leadfar.struts2.actions;
publicclass user {
privateintid;
private string
username;
password;
privateintage;
address;
public string getusername() {
returnusername;
}
publicvoid setusername(string username) {
this.username = username;
public string getpassword() {
returnpassword;
publicvoid setpassword(string password) {
this.password = password;
publicint getage() {
returnage;
publicvoid setage(int
age) {
this.age = age;
public string getaddress() {
returnaddress;
publicvoid setaddress(string address) {
this.address = address;
publicint getid() {
returnid;
publicvoid setid(int
id) {
this.id = id;
}
假如要写一个action,用来添加user。
第一种做法是直接在action中定义所有需要的属性,然后在jsp中直接用属性名称来提交数据:
useraction:
publicclass useraction {
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";
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
name="password"><br/>
age:<input
name="age"><br/>
address:<input
name="address"><br/>
<input type="submit"
name="submit"
value="添加用户">
</form><br/>
上述做法不好之处是:如果实体类的属性非常多,那么action中也要定义相同的属性。
第二种做法是将user对象定义到useraction中,然后在jsp中通过user属性来给user赋值:
private user
user;
public user getuser() {
returnuser;
publicvoid setuser(user user) {
this.user = user;
name="user.username"><br/>
name="user.password"><br/>
name="user.age"><br/>
name="user.address"><br/>
这种做法不好的地方是:jsp页面上表单域中的命名变得太长
第三种做法是利用modeldriven机制,让useraction实现一个modeldriven接口,同时实现接口中的方法:getmodel()。如下所示:
publicclass useraction
implements modeldriven{
@override
public object getmodel() {
if(user ==
null){
user =
}
jsp的代码如下:
可见,第三种做法是比较好的,action和jsp写起来都比较简单。
modeldriven背后的机制就是valuestack。界面通过:username/age/address这样的名称,就能够被直接赋值给user对象,这证明user对象正是valuestack中的一个root对象!
那么,为什么user对象会在valuestack中呢?它是什么时候被压入valuestack的呢?答案是:modeldriveninterceptor。modeldriveninterceptor是缺省的拦截器链的一部分,当一个请求经过modeldriveninterceptor的时候,在这个拦截器中,会判断当前要调用的action对象是否实现了modeldriven接口,如果实现了这个接口,则调用getmodel()方法,并把返回值(本例是返回user对象)压入valuestack。
请看modeldriveninterceptor的代码:
publicclass
modeldriveninterceptor extends abstractinterceptor {
protectedbooleanrefreshmodelbeforeresult
= false;
publicvoid setrefreshmodelbeforeresult(boolean
val) {
this.refreshmodelbeforeresult
= val;
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 object
getmodel() {
if(user
== null){
//user.setusername("这是原来的user对象");
public string updateinput(){
//根据id,查询数据库,得到user对象
user =
new usermanager().finduserbyid(user.getid());
return"update_input";
上述代码中,new usermanager().finduserbyid(user.getid());这一行,将从数据库中查询相应的记录,同时转换为user对象返回。而return “update_input”;将转向更新显示页面。
更新页面如下:
name="method:update">
id:<input
name="id"
value="<s:propertyvalue="id"/>"><br/>
name="username"
value="<s:propertyvalue="username"/>"><br/>
name="password"
value="<s:propertyvalue="password"/>"><br/>
name="age"
value="<s:propertyvalue="age"/>"><br/>
name="address"
value="<s:propertyvalue="address"/>"><br/>
value="更新用户">
上述代码运行起来之后,你在更新界面上将看不到数据(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!