浅谈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赋值。