天天看点

三种对象传参和ModelDriven的原理 三种对象传参和ModelDriven的原理

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!