淺談Struts2的模型驅動(ModelDrivenInterceptor)和屬性封裝和struts2數據封裝機制

1、模型驅動

 @Controller

 @Scope("prototype")

 public class UserAction extends ActionSupport implements ModelDriven<User>{

 private User model = new User();

 @Override // implements the ModelDriven

 public User getModel(){

   return this.model;

  }

  @Autowired

 private UserService userService;

 public String addUser(){

 userService.add(this.model);

 return SUCCESS;

 }

 }

 ModelDrivenInterceptor源碼如下:

 

爲什麼實現了ModelDriven之後能在action的方法中直接訪問實體類對象user?

 因爲在到達UserAction的方法之前會先執行struts-default.xml中配置的一些攔截器,包括   ModelDrivenInterceptor、ParametersInterceptor(action的屬性封裝),進而執行攔截器的攔截方法intercept,看源碼可知:

 1、先得到了action對象UserAction

 2、判斷這個action是否實現了ModelDriven

 3、若實現了,則將其強轉爲ModelDriven

 4、獲得創建ActionProxy之前靜態注入的ValueStack的實現類值棧對象

 5、調用modelDriven的getModel方法獲得實體類對象(本例中爲action中創建的User對象)

 6、調用值棧的push方法(內部使用getRoot.add(0,obj))將實體類對象放入對象棧的棧頂

 7、在到達ParametersInterceptor的攔截方法時將參數username、password等的值封裝對象棧棧頂的model對象中。

struts2的數據機制是:用戶提交請求數據,數據是由request.getParameter獲得的,因而數據位於request對象的map中。然後創建ActionContext、靜態注入ValueStack實例,這時將request對象放入值棧的Map棧中。ParametersInterceptor攔截器又繼承自MethodFilterInterceptor,其主要功能是把ActionContext中的請求參數設置到ValueStack中,如果棧頂是當前Action則把請求參數設置到了Action中,如果棧頂是一個model(Action實現了ModelDriven接口)則把參數設置到了model中


爲什麼ModelDrivenInterceptor和ParametersInterceptor一前一後且順序不能調換?兩種數據封裝方式能否同時使用?

在沒有使用模型驅動而使用屬性封裝時

 public class UserAction extends ActionSupport{

  private String username;

 private String password;

 public void setUsername(String username){ this.username = username; }

 public void setPassword(String password) { this.password = password; }

 @Autowired

 private UserService userService;

 public String add(){

   User user = new User();

 user.setUsername(username);

 user.setPassword(password);

 userService.add(user);

 return SUCCESS;

  }

 }

 在沒有實現模型驅動時,經過模型驅動攔截器時判斷action爲ModelDriven的實例失敗因此不會有將實體類對象放入

對象棧棧頂這個操作。那麼到達ParametersInterceptor的攔截方法時,默認是當前的action對象位於棧頂,request域

中的key/value會被封裝到action的屬性username、password中,因此到達add方法時這兩個參數的值已經被封裝好了。


ModelDrivenInterceptor和ParametersInterceptor的順序爲什麼不能調換?

 倘若調換了兩者的順序,那麼到達ParametersInterceptor的攔截方法時,還未經過ModelDrivenInterceptor的攔截

方法將實體類對象放入棧頂,即當前的action對象位於棧頂。而action對象的屬性User model是對象類型,request域中的

username,password只能封裝到action對象中的String或其他基本類型屬性中,因此會導致封裝數據失敗。


總而言之,模型驅動和屬性封裝可以同時使用,封裝的結果以模型驅動的機制優先,剩餘未封裝的數據最後封裝到action中。


若同時使用兩種封裝方式:

 public class UserAction extends ActionSupport implements ModelDriven<User>{

 private User user = new User();

 @Override

 public User getModel(){

  return this.user;

  }

 private String username;

 private String password;

 public void setUsername...

 public void setPassword...

 

 public void add(){ userService.add(user); }

 }

分析原則還是根據封裝機制爲到達ParametersInterceptor的攔截方法時將request域中的key/value封裝到位於對象棧

棧頂的對象中。由於到達攔截方法時,ModelDrivenInterceptor將user放入了棧頂,因此action中的user對象封裝成功。

而因爲參數username、password已經被成功封裝到了user對象中,一個參數只會被封裝一次,所以action中的username、

password屬性值爲null,但若頁面提交了action中的屬性而非模型中的屬性則會被封裝到action中。


使用ognl表達式封裝(本質爲屬性封裝)

public class UserAction extends ActionSupport{

 private User user;

 public User getUser(){

 return this.user;

 }

 public void setUser(User user){

 this.user = user;

}

 public void add(){ userService.add(user); }

}

此種封裝方式要求頁面輸入框的name屬性值的寫法遵循ognl表達式的格式,即對象名.屬性名,如user.username

user.password。

此種封裝方式的原理是拿參數中的對象名如user到action中找到其get'方法(這裏爲getUser),通過java的反射機制創建

User類對象,獲取User類的屬性及setter方法,找到參數中屬性名username、password的set方法並注入值。然後調用action

類中的setUser方法爲action類中的this.user賦值。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章