3.5.1 实现Action处理类
Struts 2采用了低侵入式的设计,Struts 2不要求Action类继承任何的Struts 2基类,甚至不要求实现任何Struts 2接口。在这种设计方式下,Struts 2的Action类是一个普通的POJO(通常应该包含一个无参数的execute方法),从而有很好的代码复用性。
Struts 2通常直接使用Action来封装HTTP请求参数,因此,Action类里还应该包含与请求参数对应的属性,并且为该属性提供对应的setter和getter方法。
例如,用户请求包含user和pass两个请求参数,那么Action类应该提供user和pass两个属性来封装用户请求参数,并且为user和pass提供对应的setter和getter方法。下面是处理该请求的Action类的代码片段。
// 处理用户请求的Action类,只是一个POJO,无须继承任何基类,无须实现任何接口 public class LoginAction { //提供两个属性来封装HTTP请求参数 private String user; private String pass; // 省略user属性的getter和setter方法 ... // 省略pass属性的getter和setter方法 ... //Action类默认处理用户请求的方法:execute方法 public String execute() { ... //返回处理结果字符串 return resultStr } }
上面的Action类只是一个普通的Java类,这个Java类提供了两个属性:user和pass,这两个属性分别对应两个HTTP请求参数。
提示:
即使Action需要处理的请求包含user和pass两个HTTP请求参数,Action类也可以不包含user和pass属性。因为系统是通过对应的getter和setter方法来处理请求参数的,而不是通过属性名来处理请求参数的。也就是说,如果是包含 user的HTTP请求参数,Action类里是否包含user属性并不重要,重要的是需要包含void setUser(String user)和String getUser()两个方法。
Action类里的属性,不仅可用于封装请求参数,还可用于封装处理结果。例如,在前面的Action代码中,如果希望将服务器提示的“登录成功”在下一个页面输出,那么可以在Action类中增加一个tip属性,并为该属性提供对应的setter和getter方法,即为Action类增加如下代码片段。
// 封装服务器提示的tip属性 private String tip; // tip属性对应的getter和setter方法 public String getTip() { return tip; } public void setTip(String tip) { this.tip = tip; }
一旦在Action中设置了tip属性值,我们就可以在下一个页面中使用Struts 2标签来输出该属性值。在JSP页面中输出tip属性值的代码片段如下:
<!-- 使用Struts 2标签来输出tip属性值 --> <s:property value="tip"/>
系统不会严格区分 Action 里哪个属性是用于封装请求参数的属性,哪个属性是用于封装处理结果的属性。对系统而言,封装请求参数的属性和封装处理结果的属性是完全平等的。如果用户的HTTP请求里包含了名为tip的请求参数,系统会调用Action类的void setTip(String tip)方法,通过这种方式,名为tip的请求参数就可以传给Action实例;如果Action类里没有包含对应的方法,则名为tip的请求参数无法传入该Action。
同样,在JSP页面中输出Action属性时,它也不会区分该属性是用于封装请求参数的属性还是用于封装处理结果的属性。因此,使用Struts 2标签既可以输出Action的处理结果,也可以输出HTTP请求参数值。
从上面代码中看到,需要在 JSP 页面输出的处理结果是一个简单的字符串,可以使用<s:property.../>标签来控制输出。实际上,Action 类里可以封装非常复杂的属性,包括其他用户自定义的类、数组、集合对象和Map对象等。对于这些复杂类型的输出,一样可以通过Struts 2标签来完成。关于如何输出复杂类型的结果,请参看本书第8章的标签库介绍。
为了让用户开发的Action 类更规范,Struts 2 提供了一个Action接口,这个接口定义了Struts 2的Action处理类应该实现的规范。下面是标准Action接口的代码。
public interface Action { //定义Action接口里包含的一些结果字符串 public static final String ERROR = "error"; public static final String INPUT = "input"; public static final String LOGIN = "login"; public static final String NONE = "none"; public static final String SUCCESS = "success"; //定义处理用户请求的execute方法 public String execute() throws Exception; }
上面的Action接口里只定义了一个execute方法,该接口的规范规定了Action类应该包含一个execute方法,该方法返回一个字符串。除此之外,该接口还定义了5个字符串常量,它们的作用是统一execute方法的返回值。
例如,当Action类处理用户请求成功后,有人喜欢返回welcome字符串,有人喜欢返回success字符串……这样不利于项目的统一管理。Struts 2的Action定义了上面的5个字符串:error、input、login、none和success,分别代表了特定的含义。当然,如果开发者希望使用特定的字符串作为逻辑视图名,则依然可以返回自己的逻辑视图名。
另外,Struts 2还为Action接口提供了一个实现类:ActionSupport,下面是该ActionSupport实现类的代码片段。
// 系统提供的ActionSupport类 public class ActionSupport implements Action, Validateable, ValidationAware, TextProvider, LocaleProvider, Serializable { //收集校验错误的方法 public void setActionErrors(Collection errorMessages) { validationAware.setActionErrors(errorMessages); } //返回校验错误的方法 public Collection getActionErrors() { return validationAware.getActionErrors(); } public void setActionMessages(Collection messages) { validationAware.setActionMessages(messages); } public Collection getActionMessages() { return validationAware.getActionMessages(); } ... //设置表单域校验错误信息 public void setFieldErrors(Map errorMap) { validationAware.setFieldErrors(errorMap); } //返回表单域校验错误信息 public Map getFieldErrors() { return validationAware.getFieldErrors(); } //控制Locale的相关信息 public Locale getLocale() { return ActionContext.getContext().getLocale(); } public String getText(String aTextName) { return textProvider.getText(aTextName); } //返回国际化信息的方法 public String getText(String aTextName, String defaultValue) { return textProvider.getText(aTextName, defaultValue); } public String getText(String aTextName, String defaultValue, String obj) { return textProvider.getText(aTextName, defaultValue, obj); } ... //用于访问国际化资源包的方法 public ResourceBundle getTexts() { return textProvider.getTexts(); } public ResourceBundle getTexts(String aBundleName) { return textProvider.getTexts(aBundleName); } //添加错误信息 public void addActionError(String anErrorMessage) { validationAware.addActionError(anErrorMessage); } public void addActionMessage(String aMessage) { validationAware.addActionMessage(aMessage); } //添加字段校验失败的错误信息 public void addFieldError(String fieldName, String errorMessage) { validationAware.addFieldError(fieldName, errorMessage); } //默认的input方法,直接返回input字符串 public String input() throws Exception { return INPUT; } public String doDefault() throws Exception { return SUCCESS; } //默认的处理用户请求的方法,直接返回success字符串 public String execute() throws Exception { return SUCCESS; } ... //清除所有错误信息的方法 public void clearErrorsAndMessages() { validationAware.clearErrorsAndMessages(); } //包含空的输入校验方法 public void validate() { } public Object clone() throws CloneNotSupportedException { return super.clone(); } ... }
正如在上面代码中所见到的,ActionSupport是一个默认的Action类,该类里已经提供了许多默认方法,这些默认方法包括获取国际化信息的方法、数据校验的方法、默认的处理用户请求的方法等。实际上,ActionSupport类完全可以作为Struts 2应用的Action处理类(因为它已经实现了execute方法),如果让开发者的Action类继承该ActionSupport类,则会大大简化Action的开发。
提示:
由于ActionSupport完全可以作为Struts 2应用的Action处理类,因此当用户配置Action类没有指定class属性时,系统自动使用ActionSupport类作为默认的Action处理类。此时,该Action总是返回success字符串作为逻辑视图名。