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賦值。