表格提交
本文是由三部分组成的文章系列的第二篇文章,该系列文章“开发在IBM WebSphere Portal中使用的Spring Portlet MVC Framework应用程序”。 这是一篇高级文章; 尽管不需要任何高级知识,但您应该熟悉开发JSR-168 Portlet的基础知识,并且对Spring框架有基本的了解。
在本系列的第一篇文章“ Spring Portlet MVC框架简介”中,我们讨论了如何使用Spring Portlet MVC Framework开发简单的HelloWorld Portlet。 在本文中,我们介绍了如何处理表单提交以及如何生成动态内容,这是大多数实际Portlet应用程序中的常见要求。
例如,开发一个示例联系人管理应用程序,使您可以在基础数据源中创建,检索,更新和删除(CRUD)联系人。 我们还将研究Spring Portlet MVC Framework收到表单提交请求时发生的事件顺序。
处理表格提交
您在本系列的第一部分中开发的HelloWorld Portlet有一个重要的限制:它不支持动作处理,这是大多数实际Portlet应用程序的基本要求。 在这里,我们开发了一个示例联系人管理应用程序,该应用程序允许用户列出,添加,更新和删除联系人。 为了简化开发环境,我们将联系信息存储在Hashmap中,而不是存储在基础数据库中的CONTACT表中。 请按照以下步骤来构建联系人管理应用程序。
第一步是在IBM Rational Application Developer for WebSphere Software中将ContactManagPortlet创建为动态Web应用程序。 使用/ src / main / java作为Java源目录,使用/ src / main / webapp作为Web内容目录。
然后将pom.xml构建脚本从本文的示例代码复制到新项目的根目录。 复制构建脚本后,执行mvn integration-test命令,该命令将下载ContactManagPortlet的所有必需依赖项。
下载所有依赖项之后,执行mvn eclipse:eclipse命令以将所有依赖项JAR文件添加到项目的类路径文件中。 右键单击ContactManagPortlet项目并刷新它时,您会看到在项目的构建路径中添加的所有依赖项JAR文件。
开发ContactDAO.java
在开发方面,第一步是定义联系人管理应用程序的所有数据访问操作。 创建ContactDAO.java接口,如清单1所示。
清单1. ContactDAO.java的代码清单
public interface ContactDAO {
public ArrayList getContactList();
public Contact getContact(String contactId);
public int insertContact(Contact contact);
public int updateContact(Contact contact);
public int deleteContact(String contactId);
}
如您所见,ContactDAO界面定义了联系人管理应用程序的创建,检索,删除和更新(CRUD)操作。
开发Contact.java
接下来,创建Contact类,它是DTO对象,用于在Web部件和数据访问层之间传递数据,如清单2所示。
清单2. Contact.java的代码清单
public class Contact {
private String contactId;
private String firstName;
private String lastName;
private String email;
private String phoneNumber;
public String getContactId() {
return contactId;
}
public void setContactId(String contactId) {
this.contactId = contactId;
}
}
在示例应用程序中,每个联系人记录都有五个字段:contactId,firstName,lastName,phoneNumber和email。 为所有其他字段添加getter和setter方法,或直接从本文的示例代码复制Contact.java。
接下来,从本文的“ 下载”部分中包含的样本代码中复制com.ibm.developerworks.contact.service.HashmapContact.java和com.ibm.developerworks.contact.service.ContactDAOImpl.java。 ContactDAOImpl类实现ContactDAO接口,并将控制委托给HashmapContact对象,以实际存储联系人记录。 HashmapContact对象将联系信息存储在Hashmap中,因此当应用程序重新启动时,您的联系信息会丢失。 您可以创建ContactDAORDBMS.java类,以将联系信息存储在实际数据库中。
开发applicationContext.xml
下一步是在/ WEB-INF /文件夹中创建applicationContext.xml文件,如清单3所示。
清单3.更改applicationContext.xml的代码清单
<bean id="contactDAO"
class="com.ibm.developerworks.contact.service.ContactDAOImpl">
<property name="hashMapContact" ref="hashMapContact"/>
</bean>
<bean id="hashMapContact"
class="com.ibm.developerworks.contact.service.HashmapContact" />
这是applicationContext.xml文件的部分列表。 查看完整清单的示例代码。 我们已将以下两个bean定义添加到applicationContext.xml中:
- contactDAO。 contactDAO是ContactDAOImpl类的对象,并具有hashMapContact属性。
- hashMapContact。 hashMapContact bean是HashmapContact类的对象。 这是联系人表的基于Hashmap的实现。
Spring Framework将applicationContext.xml文件加载到应用程序上下文对象中。 请注意,Spring Framework创建了一个应用程序上下文层次结构,其中子对象可以查看父对象,但是父对象不能查看子对象。 在applicationContext.xml文件中定义的bean是在portlet应用程序级别创建的,并且portlet上下文中的bean可以访问在portlet应用程序上下文级别定义的bean。 但是,在portlet应用程序上下文级别定义的bean无法访问portlet上下文中的bean。 在样本代码中,在portlet应用程序级别可以访问contactDAO和hashMapContact bean。
开发InsertController.java
在Spring Portlet MVC Framework中,要处理表单提交,您可以从AbstractFormController或其子类派生Controller类。 SimpleFormController是AbstractFormController的具体实现之一,它提供了用于处理表单提交的简单工作流。 每当SimpleFormController收到请求时,它都会检查以确定您是要访问表单页面还是提交表单。
- 如果您正在访问表单页面,则它将返回一个formView页面。
- 如果要提交表单,则它将读取此Controller的commandClass的值,创建一个commandClass对象,并使用用户在表单上提交的值填充其属性。 然后,它允许您执行一些业务逻辑。 之后,它将向用户显示successView页面。
我们将在本文后面详细介绍这些信息。
在联系人管理应用程序中,您既要使用InsertController来向用户显示新的联系人页面,又要处理页面提交,因此您要创建InsertController.java,如清单4所示。
清单4. InsertController.java的代码清单
public class InsertController extends SimpleFormController{
private ContactDAO contactDAO;
public ContactDAO getContactDAO() {
return contactDAO;
}
public void setContactDAO(ContactDAO contactDAO) {
this.contactDAO = contactDAO;
}
protected void onSubmitAction(ActionRequest request, ActionResponse response,
Object command, BindException errors) throws Exception {
contactDAO.insertContact((Contact)command);
response.setRenderParameter("action", "list");
}
}
如您所见,InsertController的实现非常简单。 首先,我们使用相应的getter和setter方法将contactDAO定义为该bean的属性。 我们决定重写SimpleFormController的onSubmitAction()方法。 在onSubmitAction()方法内部,首先我们调用contactDAO.insertContact(contact)方法,该方法将新的联系人记录插入基础数据源中。 接下来,我们将action设置为render参数,其值等于“插入”。
SimpleFormController还定义了doSubmitAction(command)方法,该方法足以实现您的业务逻辑。 不过,在我们的情况下,我们希望在添加新联系人后将用户重定向到联系人电子表格。 为此,我们在呈现阶段将控件传递给SelectController。 设置action render参数可以解决这一问题。
insertController的Bean定义
Spring Portlet MVC Framework如何知道使用哪个JSP文件来显示“插入新联系人”页面? 如何知道要使用com.ibm.developerworks.contact.domain.Contact作为InsertController的命令类? 这就是insertController bean定义变得重要的地方。 在应用程序的/ WEB-INF /文件夹中创建SpringContactManag-portlet.xml,并添加insertController bean定义,如清单5所示。
请记住,InsertController是Java类的名称,而insertController是在Spring上下文文件中定义的bean的名称。 通常,Java类名称的首字母大写。 在Spring上下文文件中定义的Bean名称中,名称的第一个字母为小写。
清单5. ContactManagPortlet-portlet.xml中insertController bean定义的代码清单
<bean id="insertController"
class="com.ibm.developerworks.contact.controller.InsertController">
<property name="contactDAO" ref="contactDAO"></property>
<property name="commandName" value="contact"/>
<property name="commandClass" value="com.ibm.developerworks.contact.domain.Contact" />
<property name="formView" value="insert"></property>
<property name="successView" value="list"></property>
</bean>l
在示例代码中,我们为以下insertController bean属性设置值:
- contactDAO。 我们将引用传递给contactDAO bean,后者是负责实际联系人插入的服务类。 记住,我们在applicationContext.xml文件中声明了contactDAO bean。
- commandClass。 在示例代码中,我们使用Contact类作为命令类,其中填充了用户在表单上提交的值。
- commandName。 在此表单的模型中设置commandClass对象时,将使用commandName的值。
- formView。 formView参数的值用于确定应向用户显示哪个视图以进行输入。 在我们的示例代码中,当用户单击“添加联系人”链接时,我们要显示insert.jsp页面,因此formView属性的值是insert。 (我们将InternalResourceViewResolver用于ViewResolution,它将插入值映射到/WEB-INF/jsp/insert.jsp文件。)
- successView。 successView参数的值用于确定添加新联系人是否成功时要显示给用户的视图。 在这种情况下,如果添加成功,我们希望显示list.jsp文件,因此此属性的值为list。
开发insert.jsp
在定义insertController bean时,我们将insert.jsp配置为formView属性的值。 当您单击联系人电子表格上的“添加联系人”链接时,Spring Portlet MVC框架会尝试查找/WEB-INF/jsp/insert.jsp页面。 下一步是在/WEB-INF/jsp/insert.jsp中创建insert.jsp页面,如清单6所示。
清单6. insert.jsp的代码清单
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
<%@ taglib prefix="portlet" uri="http://java.sun.com/portlet" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<portlet:actionURL var="formAction">
<portlet:param name="action" value="insert"/>
</portlet:actionURL>
<h3>Add New Contact</h3>
<form:form commandName="contact" method="post" action="${formAction}">
<table cellpadding="4">
<tr>
<td>Contact Id</td>
<td><form:input path="contactId" size="30" maxlength="80"/></td>
</tr>
<tr>
<td><input type="submit" name="_finish" value="Save"/></td>
<td><input type="submit" name="_cancel" value="Cancel"/></td>
</tr>
</table>
</form:form>
insert.jsp页面具有三个标记库声明。 您已经熟悉其中的两个,jstl标记库和portlet标记库。 此外,我们还定义了表单标签库。 从2.0版开始,Spring Framework提供了表单标签库,Web MVC Framework或Portlet MVC Framework应用程序代码中包含的JSP文件可以使用该表单标签库。
表单标签库与Spring Portlet MVC Framework集成在一起,并提供对命令对象的访问。 在insert.jsp文件中,<form:form>是用于在最终标记中生成HTML <form>标记的第一个标记。 此标记还读取命令对象,并将其设置在JSP文件的pageContext中。 在示例代码中,commandName是联系人,因此<form>标记的commandName属性的值是contact。
表单标签库中的所有其他标签都是<form>标签的嵌套标签。 我们使用的下一个表单标签库标签是<input>,它用于在最终标记中生成<input type =“ text”>元素。 在示例代码中,我们希望将contactId输入绑定到Contact对象中的contactId属性,为此,我们添加了一个路径属性,其值等于contactId。 注意,这是insert.jsp的部分代码清单。 完整的清单在本文的“ 下载”部分列出的示例代码中。
开发SpringContactManagPortlet-portlet.xml
下一步是在SpringContactManagPortlet-portlet.xml中添加几个bean定义,如清单7所示。
清单7. SpringContactManagPortlet-portlet.xml中更改的代码清单
<bean id="parameterMappingInterceptor"
class= "org.springframework.web.portlet.handler.ParameterMappingInterceptor" />
<bean id="portletModeParameterHandlerMapping"
class= "org.springframework.web.portlet.handler.PortletModeParameterHandlerMapping">
<property name="interceptors">
<list>
<ref bean="parameterMappingInterceptor" />
</list>
</property>
<property name="portletModeParameterMap">
<map>
<entry key="view">
<map>
<entry key="select">
<ref bean="selectController" />
</entry>
<entry key="insert">
<ref bean="insertController" />
</entry>
</map>
</entry>
</map>
</property>
</bean>
如您所见,我们在SpringContactManagPortlet-portlet.xml文件中定义了另外两个bean定义。
- parameterMappingInterceptor。 parameterMappingInterceptor用于将动作请求参数的值从ActionRequest转发到RenderRequest,以便Spring Portlet MVC Framework使用相同的控制器来处理动作请求和呈现请求。 我们将在本文后面详细讨论。
- portletModeParameterHandlerMapping。 portletModeParameterHandlerMapping是HandlerMapping的高级实现,它使用portlet模式以及动作请求参数的值来解析请求的处理程序。 在示例代码中,我们在View模式下有多个处理程序,因此我们使用portletModeParameterHandlerMapping。 portletModeParameterHandlerMapping bean具有portletModeParameterMap属性,该属性是您的portlet支持的所有portlet模式的映射。 该映射将键值对与Portlet模式的名称作为键。 值是另一个映射,它以操作请求参数的值作为键,并以处理其请求的控制器的引用作为其值。
在示例代码中,您只希望支持“查看”模式,以便顶层地图只有一个条目。 但是,第二级映射具有一个等于insert的键,其值引用了insertController bean。 结果,当此Portlet在View模式下获得操作参数等于insert的请求时,它将控制权传递给InsertController进行处理。
构建此示例代码的最后一步是从本文的示例代码中复制其他文件SelectController.java,UpdateController.java,DeleteController.java以及相应的JSP文件list.jsp和update.jsp。 从示例代码复制了所有必需的文件之后,执行mvn package命令来构建portlet应用程序。 准备好WAR文件后,可以在IBM WebSphere Portal上对其进行测试。
动作处理如何工作
Spring Portlet MVC框架在后台执行许多操作。 从长远来看,在您熟悉该软件之后,这可能是一个很大的优势。 从短期来看,这种方法可能很难理解。 在本节中,我们讨论当DispatcherPortlet收到一个动作请求时,后台会发生什么。 见图1。
图1. DispatcherPortlet收到动作请求时发生的事件序列的序列图
当DispatcherPortlet获取插入操作请求时,将发生以下事件序列。
- DispatcherPortlet检查是否为您的Portlet启用了多部分请求处理。 如果已启用,则DispatcherPortlet将检查请求是否为多部分; 也就是说,HTML <form>元素的enctype属性设置为multipart / form-data。 如果是,则将该请求包装在MultipartActionRequest对象中。
- DispatcherPortlet遍历您的Portlet应用程序中配置的所有HandlerMappings的列表,调用其getHandler()方法来获取当前请求的适当处理程序。 在示例代码中,我们仅配置了一个HandlerMapping实现类PortletModeParameterHandlerMapping。 当获得控制权时,它将检查当前请求的Portlet模式(在这种情况下为View模式)以及动作请求参数的值(即insert)。 现在,在SpringContactManagPortlet-portlet.xml中定义portletModeParameterHandlerMapping bean时,插入键被映射到InsertController,因此它返回InsertController的对象。
- 在DispatcherPortlet知道InsertController负责处理此请求之后,它将开始遍历此处理程序的所有拦截器,并调用其preHandleAction()方法。 在示例代码中,我们仅配置了一个拦截器类ParameterMappingInterceptor,因此将调用其preHandleAction()方法。
- ParameterMappingInterceptor用于将动作请求参数的值从ActionRequest转发到RenderRequest,因此它将渲染参数设置为名称等于action且值等于insert。
Spring Portlet MVC Framework在操作阶段和呈现阶段都尝试为当前请求解析Handler。 如果希望Spring Portlet MVC Framework在操作和呈现阶段使用相同的Handler,则可以使用ParameterMappingInterceptor。 - DispatcherPortlet调用InsertController的handleActionRequest()方法; 这是实际动作处理发生的地方。 在示例代码中,InsertController扩展了SimpleFormController,使其遵循SimpleFormController定义的操作请求生命周期。
- SimpleFormController首先读取为InsertController配置的commandClass的名称,即com.ibm.developerworks.contact.domain.Contact,并创建其对象。
- 之后,SimpleFormController创建PortletRequestDataBinder的对象,该对象负责使用用户在表单上提交的值填充Contact对象。 例如,它将读取firstName请求参数的值,并将其设置为Contact对象中firstName属性的值。
- 如果希望Spring Portlet MVC Framework在将请求参数值转换为命令类属性值时使用某些自定义逻辑,则可以在类中重写initBinder()方法。 例如,您可以指定如何将String转换为Date或将MultiPartFile转换为byte []数组。 我们将在本文后面的示例中显示此示例。
- 在使用用户提交的值填充Contact bean之后,SimpleFormController遍历应用程序中的所有Validator,并调用其validate()方法。 在这里,您可以编写服务器端验证逻辑。 例如,您可以检查联系人ID是否为必填字段。 我们将在本文后面详细讨论。
- 执行验证后,SimpleFormController检查是否发生任何验证错误。 如果没有发生错误,它将在InsertController中调用onSubmitAction()/ doSubmitAction()。 通常,我们建议您在Controller类中重写doSubmitAction()。 但是,在示例代码中,我们希望在呈现阶段将控件重定向到SelectController,因此我们将覆盖onSubmitAction()阶段。
操作阶段完成后,DispatcherPortlet将检查是否发生任何验证错误。 如果确实发生错误,它将读取formView的值并将用户重定向到该页面。 如果没有验证错误,则它将用户重定向到successView页面。
结论
在本文中,我们介绍了如何使用Spring Portlet MVC Framework开发示例联系人管理应用程序。 我们讨论了如何创建一个覆盖SimpleFormController的类,该类在获得GET请求时将控制转发给formView属性定义的值,并在它获得POST请求并成功执行动作时将控制转发给successView属性定义的值。 我们还讨论了您需要创建的所有配置文件,并介绍了如何使用Spring Portlet MVC Framework定义的自定义标签库。
在本系列的最后一部分中,我们将讨论Spring Portlet MVC Framework中的一些高级主题,例如处理表单验证,优雅地处理异常和国际化。 我们还将演示如何将Apache Tiles框架与Spring Portlet MVC框架集成。
翻译自: https://www.ibm.com/developerworks/websphere/library/techarticles/0802_patil-pt2/0802_patil-pt2.html
表格提交