Struts视图组件
Struts框架的视图负责为客户提供动态网页内容。Struts视图主要由JSP网页构成,此外,Struts框架还提供了Struts客户化标签和ActionForm Bean,这些组件提供对国际化、接收用户输入的表单数据、表单验证和错误处理等的支持,使开发者可以把更多的经历放在实现业务需求上。
1 视图概述
视图使模型的外在表现形式,用户通过视图来了解模型的状态。同一个模型可以有多种视图。
在Struts框架中,视图主要由JSP组件构成,此外,视图还可以包含以下组件:
· HTML文档
· JSP客户化标签
· JavaScript和stylesheet
·
多媒体文件
·
消息资源(Resource Bundle)
· ActionForm Bean
2 在视图中使用JavaBean
JavaBean使可重用的、平台独立的Java组件,JavaBean支持属性、事件、方法和持久化。Struts框架仅利用了JavaBean的一小部分特性。在Struts应用中的JavaBean和普通的Java类很相似,不过,它应该遵守以下规范:
·
必需提供不带参数的构造方法。
·
为Bean的所有属性提供公共类型的get/set方法。
·
对于boolean类型的属性,如果存在isXXX()方法,那么该方法返回boolean类型的属性值。
·
对于数组类型的属性,应该提供getXXX(int index)和setXXX(int index, PropertyElement
value)方法,用来读取或设置数组中的元素。
2,1 DTO数据传输对象
在【Struts模型组件】介绍过可以利用JavaBean来创建业务对象,实体业务对象包含了模型的状态信息。此外,Struts框架还利用JavaBean来创建数据传输对象(Data Transfer Object,简称DTO)。DTO用于在不同的层之间传递数据。
不将模型层的业务对象直接传递到视图层(从技术角度来说使可以实现的),而是采用DTO来传输数据,这样做有两个好处:
·
减少传输数据的冗余,提高传输效率。
·
有助于实现各个层之间的独立,使每个层分工明确。模型层负责业务逻辑,视图层负责向用户战士模型状态。采用DTO,模型层对视图层屏蔽了业务逻辑细节,向视图层提供可以直接现实给用户的数据。
2.2
Struts框架提供的DTO:ActionForm Bean
ActionForm Bean是Struts框架提供的DTO,用于在视图层和控制层之间传递HTML表单数据。控制层可以从ActionForm Bean中读取用户输入的表单数据,也可以把来自模型层的数据存放到ActionForm
Bean中,然后把它返回给视图。ActionForm
Bean还具有表单验证功能,可以为模型层过滤不合法的数据。
在【Struts模型组件】,曾经强调过模型层应该和Web应用层保持独立。由于ActionForm类中使用了Servlet API,因此不提倡直接把ActionForm Bean传给模型层,而应该在控制层把ActionForm Bean的数据重新组装到自定义的DTO中,再把它传递给模型层。
3 使用ActionForm
3.1 使用ActionForm
ActionForm
Bean有两种存在范围:request和session。如果ActionForm存在于request范围,它仅在当前的请求/响应生命周期中有效。在请求从一个Web组件转发到另一个Web组件的过程中,ActionForm实例一直有效。当服务器把响应结果返回给客户,ActionForm实例及其包含的数据就会被销毁。如果ActionForm存在于session范围,同一个ActionForm实例在整个HTTP会话中有效。
当控制器接受到请求时,如果请求访问的Web组件为Action,并且为这个Action配置了和ActionForm的映射,控制器将从request或session范围中取出ActionForm实例,如果该实例不存在,就会自动创建一个新的实例。当控制器接受到一个新的请求时,ActionForm的生命周期如下:
· 控制器接收到请求
·
从request或session范围中取出ActionForm实例,如果该实例不存在,就自动创建一个新的实例。
·
调用ActionForm的reset()方法
·
把ActionForm实例保存在request或session范围中
·
把用户输入的表单数据组装到ActionForm中
·
如果<action>的validate属性为true,则调用ActionForm的validate()方法。
· (1) 如果存在验证错误,把请求转发给<action>的input属性指定的Web组件,ActionForm实例依然保持在request或session范围内。
(2) 如果无验证错误,调用Action的execute()方法,把ActionForm实例传递给execute()方法。
·
把请求转发给其他Web组件,ActionForm实例依然保存在request或session范围内。
3.2 创建ActionForm
Struts框架中定义的ActionForm类时抽象的,必需在应用中创建它的子类,来捕获具体的HTML表单数据,ActionForm
Bean中的属性和HTML表单中的字段一一对应。
1 validate()方法
如果Struts的配置文件满足以下两个条件,Struts控制器就会调用ActionForm的validate()方法:
· 为ActionForm配置了Action映射,即<form-bean>元素的name属性和<action>元素的name属性匹配。
·
<action>元素的validate属性为true。
在ActionForm基类中定义的validate()方法直接返回null,如果创建了扩展ActionForm基类的子类,那么应该在子类中覆盖validate()方法。
validate()方法主要负责检查数据的格式和语法,而不负责检查数据是否符合业务逻辑。
2 reset()方法
不管ActionFormj存在于哪个范围内,对于每个请求,控制器都会先调用ActionForm的reset()方法,然后再把用户输入的表单数据组装到ActionForm中。reset()方法用于恢复ActionForm的属性的默认值,例如把boolean类型属性设为true或false,把字符串属性设为null或某个初始值。
如果ActionForm在request范围内,那么对于每个新的请求都会创建新的ActionForm实例。当新的实例创建后,如果它的属性已经被初始化为默认值,那么接着再在reset()方法中把属性设为默认值不是很有必要,因此在这种情况下,可以让reset()方法为空。
对于session范围内的ActionForm,同一ActionForm实例会被多个请求共享,reset()方法在这种情况下极为有用。
3.3 配置ActionForm
Struts配置文件的<form-beans>元素用来配置所有的ActionForm Bean。<form-beans>元素可以包含多个<form-bean>子元素,它代表单个的ActionForm Bean。
同一个ActionForm可以和多个Action映射。在<action>元素中,name和scope属性分别指定ActionForm的名字和范围,validate属性指定是否执行表单验证。
3.4 访问ActionForm
ActionForm可以被JSP、Struts标签、Action和其他Web组件访问。访问ActionForm大致有以下一些方法:
1 使用Struts HTML标签库
Struts HTML标签库提供了一组和ActionForm密切关联的标签,<html:form>标签生成HTML表单,它包括<html:text>、<html:select>、<html:option>、<html:radio>和<html:submit>等子标签,这些子标签构成HTML表单的字段或按钮。<html:form>标签能和ActionForm交互,读取ActionForm的属性值,把他们赋值给表单中对应的字段。
2 从request或session范围内取出ActionForm实例
Struts框架把ActionForm实例保存在HttpServletRequest或HttpSession中,保存时采用的属性key为<form-bean>元素的name属性。因此,如果ActionForm在request范围内,则可以调用HttpServletRequest的getAttribute()方法读取ActionForm实例。如果ActionForm在session范围内,则可以调用HttpSession的getAttribute()方法读取ActionForm实例。
3 在Action类的execute()方法中直接访问ActionForm
如果配置了ActionForm的Action的映射,Struts框架就会把ActionForm作为参数传递给Action的execute()方法,因此在Action类的execute()方法中可以读取或设置ActionForm属性。
3.5 处理表单跨页
有的时候,由于表单数据太多,无法在同一个页面显示(如用于用户注册的表单),可以把它拆分成多个表单,分多个页面显示。在这种情况下,既可以为每个表单创建单独的ActionForm,页可以只创建一个ActionForm,它和多个表单对应。
(1) 把HTML表单拆分到多个JSP页面中
在两个JSP页面中均定义了HTML表单,由于这两个表单都对应同一个ActionForm,因此可以在每个表单中定义一个隐含字段<html:hidden property=”page”/>,它代表当前页面编号,ActionForm将通过这个字段来识别当前正在处理的时哪个表单。
(2) 创建多个HTML表单对应的ActionForm
在创建这个ActionForm时有以下几点需要注意:
· 提供和HTML表单的隐藏字段page对应的page属性:
private String
page = null;
public String
getPage() {
return page;
}
public void
setPage(String page) {
this.page = page;
}
·
在reset()方法中,只能把和当前正在处理的表单相关的属性恢复为默认值,否则,如果每次都把ActionForm的所有属性恢复为默认值,将使用户输入的上一页表单数据丢失。由于Struts框架先调用reset()方法,然后再把用户输入的表单数据组装到ActionForm中,因此在reset()方法中,不能根据page属性来判断处理的时哪个页面,而应该直接从HttpServletRequest对象中读取当前表单的page字段值:
int numPage = new
Integer(request.getParameter(“page”)).intValue();
·
在validate()方法中,仅对和当前表单相关的属性进行也政。由于Struts框架在调用validate()方法之前,已经把用户输入的表单数据组装到ActionForm中,因此在validate()方法中可以根据page属性决定正在处理哪个表单。
(3) 配置ActionForm和多个Action映射
当ActionForm与多个表单对应时,应该把ActionForm存放在session范围内。
4 使用动态ActionForm
在Struts框架中,ActionForm对象用来包装HTML表单数据,并能动态返回用于显示给用户的数据。自定义的ActionForm必需符合JavaBean规范,并继承Struts的ActionForm类,同时,用户可以有选择地覆盖两个方法:reset()和validate()。
ActionForm的以上特性可以简化Web应用的开发,因为它可以协助自动进行表示层的数据验证。ActionForm的唯一缺点时对于大型的Struts应用,必需以编程的方式创建大量的ActionForm类,如果HTML表单的字段发生变化,就必需修改并重编译相关的ActionForm类。
Struts 1.1对此做了改进,引入了动态ActionForm类的概念。Struts框架的DynaActionForm类及其子类实现了动态ActionForm,DynaActionForm类是ActionForm类的子类。
4.1 配置动态ActionForm
动态ActionForm支持在Struts配置文件中完成ActionForm的全部配置,没有必要编写额外的程序来创建具体的ActionForm类。配置动态ActionForm的方法为:在Struts配置文件中配置一个<form-bean>元素,将type属性设置为DynaActionForm或它的某个子类的全名。
<form-bean>的<form-property>子元素用来设置动态ActionForm的属性。<form-property>元素的name属性指定属性名,type指定属性类型,可以把动态ActionForm的属性设为以下Java类型:
·
java.lang.BigDecimal
·
java.lang.BigInteger
·
java.lang.Boolean
·
java.lang.Byte
·
java.lang.Character
·
java.lang.Class
·
java.lang.Double
·
java.lang.Float
·
java.lang.Integer
·
java.lang.Long
·
java.lang.Short
·
java.lang.String
·
java.sql.Data
·
java.sql.Time
·
java.sql.Timestamp
如果表单的字段值为Java基本类型,在配置时应该用响应的包装类型来代替,例如int类型的包装类型为Integer。
4.2 动态ActionForm的reset()方法
DynaActionForm基类提供了initialize()方法,它把表单的所有属性恢复为默认值。表单属性默认值由<form-bean>的<form-property>子元素的initial属性来决定。如果没有设置initial属性,则表单属性的默认值由其Java类型来自动决定,例如对象类型的默认值为null,整数类型的默认值为0,boolean类型的默认值为false。
DynaActionForm基类的initialize()方法的代码如下:
public void initialize(ActionMapping
mapping) {
String name = mapping.getName();
if (name == null) {
return;
}
FormBeanConfig
config =
mapping.getModuleConfig().findFormBeanConfig(name);
if (config ==
null) {
return;
}
FormPropertyConfig
props[] = config.findFormPropertyConfigs();
for (int i = 0;
i < props.length; i++) {
set(props[i].getName(),
props[i].initial());
}
}
DynaActionForm基类的reset()方法不执行任何操作,其代码如下:
public void reset(ActionMapping mapping,
HttpServletRequest request) {
; // Default implementation
does nothing
}
如果希望Struts框架在每次把表单数据组装到动态ActionForm中之前,先把所有的属性恢复为默认值,可以定义一个扩展DynaActionForm类的子类,然后覆盖其reset()方法,在reset()方法中只要调用initialize()方法即可,代码如下:
public class MyDynaActionForm extends
DynaActionForm {
……
public void rest(ActionMapping mapping, HttpServletRequest request) {
initialize(mapping);
}
}
4.3 访问动态ActionForm
Action类和JSP都可以访问动态ActionForm,使用方法与标准ActionForm大致相同,只有一点小差别。如果使用标准ActionForm对象,在标准ActionForm中针对每个属性都提供了get/set方法,来读取或设置属性。
而DynaActionForm把所有的属性保存在一个Map类对象中,并提供了下面的用于访问所有属性的通用方法:
public Object get(String name)
public void set(String name, Object value)
get(String name)方法根据指定的属性名返回属性值;set(String name, Object value)方法用于为给定的属性赋值。
4.4 动态ActionForm的表单验证
DynaActionForm基类的validate()方法没有提供任何默认的验证行为。可以定义扩展DynaActionForm的子类,然后覆盖validate()方法,但是以编程的方式来验证动态ActionForm违背了Struts框架提供动态ActionForm的初中,即以配置来替代编程。幸运的是,可以采用另一种验证机制,即Validator框架来完成验证。Validator框架允许采用特定的配置文件来为动态ActionForm配置验证规则。
5 小结
本篇侧重介绍了构成Struts视图的组件之一:ActionForm。ActionForm用于在视图层和控制层之间传递表单数据,ActionForm可以存放在request和session范围内。ActionForm是一种Web组件,不应该在模型层直接访问ActionForm Bean。同一个ActionForm可以对应多个HTML表单,在这种情况下,有以下开发技巧:
·
在HTML表单中定义<html:hidden property=”page” />隐藏字段,来标识当前页面。
·
在ActionForm中定义page属性,它和表单中的隐藏字段page对应。
·
在ActionForm的reset()方法中,只能把和当前表单相关的属性恢复为默认值。可以调用request.getParameter(“page”)方法来读取当前的页面编号。
·
在ActionForm的validate()方法中,只能对和当前表单相关的属性进行验证。此时page属性代表当前的页面编号。
·
在配置ActionForm和Action的映射时,应该把ActionForm的范围设为session。
Struts框架还印入了DynaActionForm类,它允许以配置的方式来创建动态ActionForm,使用DynaActionForm有以下几点需要注意:
· <form>和<form-property>子元素用于配置动态ActionForm的属性。<form-property>元素的type属性指定ActionForm的属性的类型。如果属性为Java基本类型,应该把属性设置为相应的Java包装类型。
·
提倡使用Validator框架来验证动态ActionForm,这样可以避免以编程的方式来实现validate()方法。
阅读材料:《精通Struts:基于MVC的Java Web设计与开发》
2005年05月12日 6:59 PM