天天看點

三種對象傳參和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!