3.7.2 使用模型驱动
下面以一个简单的登录应用为例,介绍如何利用模型驱动模式来开发Struts 2应用。本应用的登录页面与前面的登录页面并没有太多不同,一样提供了两个文本框,分别用于输入用户名和密码,当用户单击“登录”按钮时,请求将提交到login.action。
login Action采用模型驱动模式实现,采用模型驱动模式时必须提供对应的模型,该模型类就是一个普通JavaBean。模型代码如下。
程序清单:codes\03\3.7\ModelDriven\WEB-INF\src\lee\UserBean.java
public class UserBean { // 下面是用于封装用户请求参数的两个属性 private String username; private String password; // 封装提示信息的tip属性 private String tip; // username属性的getter和setter方法 public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } // password属性的getter和setter方法 public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } // tip属性的setter和getter方法 public void setTip(String tip) { this.tip = tip; } public String getTip() { return this.tip; } }
上面的Model类完全是一个POJO(普通的Java对象),仅仅提供了两个用于封装请求参数的属性,提供了一个封装处理结果的属性,并为三个属性提供了对应的getter和setter方法。
对于采用模型驱动的Action而言,该Action必须实现ModelDriven接口,下面是本应用所使用的Action的代码。
程序清单:codes\03\3.7\ModelDriven\WEB-INF\src\org\crazyit\struts2\action\LoginAction.java
// 采用模型驱动的Action必须实现ModelDriven接口 public class LoginAction implements Action , ModelDriven<UserBean> { // 定义用于封装请求参数和处理结果的模型 private UserBean model = new UserBean(); // 处理用户请求的execute方法 public String execute() throws Exception { // 当用户请求参数的username等于crazyit,密码请求参数为leegang时 // 返回success字符串,否则返回error字符串 if (getModel().getUsername().equals("crazyit") && getModel().getPassword().equals("leegang")) { getModel().setTip("哈哈,服务器提示!"); return SUCCESS; } else { return ERROR; } } //实现ModelDriven接口必须实现的方法 public UserBean getModel() { return model; } }
使用模型驱动模式时,Action 必须实现 ModelDriven 接口,实现该接口则必须实现getModel()方法,该方法用于把Action和与之对应的Model实例关联起来。
配置模型驱动的Action与配置属性驱动的Action没有任何区别,Struts 2不要求配置模型对象,即不需要配置UserBean实例。
比较难以理解的是,系统是如何将请求参数封装到LoginAction的model属性中的呢?查看struts-default.xml文件,看到如下配置片段。
<!-- 定义Struts 2默认的包 --> <package name="struts-default"> <!-- 该元素里定义该框架所有的拦截器和拦截器栈 --> <interceptors> ... <!-- 定义属性驱动的拦截器 --> <interceptor name="params" class="com.opensymphony.xwork2.interceptor.Parameters Interceptor"/> <!-- 定义模型驱动的拦截器 --> <interceptor name="modelDriven" class="com.opensymphony.xwork2.interceptor.ModelDriven Interceptor"/> ... <!-- 定义一个模型驱动的拦截器栈 --> <interceptor-stack name="modelDrivenStack"> <interceptor-ref name="modelDriven"/> <interceptor-ref name="basicStack"/> </interceptor-stack> ... <!-- 定义一个默认的拦截器栈 --> <interceptor-stack name="defaultStack"> ... <!-- 定义模型驱动模式的拦截器 --> <interceptor-ref name="modelDriven"/> <!-- 定义属性驱动模式的拦截器 --> <interceptor-ref name="params"> ... </interceptor-stack> </interceptors> <!-- 定义Struts 2系统默认的拦截器栈 --> <default-interceptor-ref name="defaultStack"/> </package>
上面的配置片段中配置了系统默认的拦截器栈,它包含了两个拦截器引用:modelDriven和params,这两个拦截器引用又对应系统中的modelDriven和params拦截器。其中params拦截器负责提取请求参数,如果使用属性驱动模式,则还负责将请求参数传给 Action 实例的属性;而modelDriven拦截器则负责将请求参数传给模型的属性。
采用了上面的模型驱动模式后,Action 类里没有直接封装处理结果,而是封装在 model属性的tip属性中。因此,如果想在JSP页面上输出服务器处理结果,则应该采用如下形式的标签。
<!-- 使用表达式输出Action实例中model属性的tip属性 --> <s:property value="model.tip"/>
如果在登录页面中输入 crazyit、leegang,然后提交登录请求,将看到如图3.28所示的页面。
图3.28 成功登录的页面
当然,Struts 2是一个非常“智能”的系统,当采用模型驱动模式时,也可以采用如下形式来输出Action属性。
<!-- 直接输出Action的tip属性 --> <s:property value="tip"/>
因为 Action 实例中没有 tip 属性,并且采用了模型驱动模式,系统将自动输出该 Action关联的model的tip属性值。
通过上面的应用可以看出,模型驱动和属性驱动各有利弊,模型驱动结构清晰,但编程烦琐(需要额外提供一个JavaBean来作为模型);属性驱动则编程简洁,但结构不够清晰。
通常,笔者更愿意选择属性驱动作为Struts 2的应用模式,毕竟,大量定义烦琐的JavaBean不是一件让人感兴趣的事情;而即使采用属性驱动,Struts 2一样提供了非常清晰的处理流程。