重头开始学 Spring JPetStore 4 里面的Controller似乎太简单了一些。
让我们看一个更复杂一些的请求
看看注册页面
引用
http://localhost:8080/jpetstore/shop/newAccount.do
这里有一个表单需要提交。沿着这个请求 /shop/newAccount.do 找到对应的bean
引用
<bean name="/shop/newAccount.do" class="org.springframework.samples.jpetstore.web.spring.AccountFormController">
<property name="petStore" ref="petStore"/>
<property name="validator" ref="accountValidator"/>
<property name="successView" value="index"/>
</bean>
我们可以看到这个控制器变成了AccountFormController。继续SimpleFormController 看看文档我们发现这个控制器就是一个为表单而生的。另外注入了一个petStore ,看看代码,我们就知道这是一个真实的业务层处理的类。还有一个验证器 :accountValidator
但是这个验证在哪?在另一个文件 applicationContext.xml 里。
引用
<!-- Generic validator for Account objects, to be used for example by the Spring web tier -->
<bean id="accountValidator" class="org.springframework.samples.jpetstore.domain.logic.AccountValidator"/>
以及 successView 一个view页面指向。
看看这个控制器最主要的方法吧。
引用
protected ModelAndView onSubmit(
HttpServletRequest request, HttpServletResponse response, Object command, BindException errors)
throws Exception {
AccountForm accountForm = (AccountForm) command;
try {
if (accountForm.isNewAccount()) {
this.petStore.insertAccount(accountForm.getAccount());
}
else {
this.petStore.updateAccount(accountForm.getAccount());
}
}
catch (DataIntegrityViolationException ex) {
errors.rejectValue("account.username", "USER_ID_ALREADY_EXISTS",
"User ID already exists: choose a different ID.");
return showForm(request, response, errors);
}
UserSession userSession = new UserSession(this.petStore.getAccount(accountForm.getAccount().getUsername()));
PagedListHolder myList = new PagedListHolder(
this.petStore.getProductListByCategory(accountForm.getAccount().getFavouriteCategoryId()));
myList.setPageSize(4);
userSession.setMyList(myList);
request.getSession().setAttribute("userSession", userSession);
return super.onSubmit(request, response, command, errors);
}
有一点让人愉快的是这里的AccountForm似乎不需要像在struts中一样特别麻烦的配置一次。只要一句话就可以了,省力了。
引用
AccountForm accountForm = (AccountForm) command;
引用
if (accountForm.isNewAccount()) {
this.petStore.insertAccount(accountForm.getAccount());
}
else {
this.petStore.updateAccount(accountForm.getAccount());
}
保存的业务逻辑被petStore包起来了,所以直接调用就结束了。关键是异常处理
引用
catch (DataIntegrityViolationException ex) {
errors.rejectValue("account.username", "USER_ID_ALREADY_EXISTS",
"User ID already exists: choose a different ID.");
return showForm(request, response, errors);
}
这里处理了一个重复用户名的异常。errors. rejectValue方法带了好几个参数。应该是国际化资源文件里的key,以及默认提示信息等内容,然后呢,然后调用了
引用
showForm(request, response, errors);
这个方法应该是一个父类的方法。自动回填了表单的提交的数据?自己试试提交一个重复的用户名,页面是不是回填了?我试验了一下是这样的,完成的还算不错。
看看它是怎么实现这个工作的呢?这个页面到底在哪?让我们看看。
引用
public AccountFormController() {
setSessionForm(true);
setValidateOnBinding(false);
setCommandName("accountForm");
setFormView("EditAccountForm");
}
其中有一个 setFormView("EditAccountForm"); 知道了吧。就是EditAccountForm.jsp了。
找到它,看看。
页面开始就是这么一段。似乎是所有的accountForm的属性都被绑定到status这个变量了现在我还不确定。先留下这个问号?接下去再看看。起码我们通过el标签可以看出来status有个属性 errorMessages 。这个集合保存了所有的错误。JSTL c:forEach标签遍历了显示了所有的错误。
引用
<!-- Support for Spring errors object -->
<spring:bind path="accountForm.*">
<c:forEach var="error" items="${status.errorMessages}">
<B><FONT color=RED> <BR> <c:out value="${error}" /> </FONT>
</B>
</c:forEach>
</spring:bind>
随便看一个input
引用
<tr bgcolor="#FFFF88">
<td>
New password:
</td>
<td>
<spring:bind path="accountForm.account.password">
<input type="password"
name="<c:out value="${status.expression}"/>"
value="<c:out value="${status.value}"/>" />
</spring:bind>
</td>
</tr>
总是被一个<spring:bind 标签包裹着。name属性也被${status.expression}动态生成了。这样做似乎于前面提到的一个方便有关。
不需要像在struts中一样特别麻烦的配置一次AccountForm。只要一句话就可以了 猜得不错的话,应该是与这个很大的关系。
看到这个页面不怎么好。满页面的<spring:bind ,每个input都要。还真是烦。
这个控制器里面有一个方法 referenceData
Create a reference data map for the given request.
在AbstractFormController里面可以找到这个方法如何被调用的。合并到一个MAP里面了。
接下来干嘛了?
引用
UserSession userSession = new UserSession(this.petStore.getAccount(accountForm.getAccount().getUsername()));
PagedListHolder myList = new PagedListHolder(
this.petStore.getProductListByCategory(accountForm.getAccount().getFavouriteCategoryId()));
myList.setPageSize(4);
userSession.setMyList(myList);
request.getSession().setAttribute("userSession", userSession);
return super.onSubmit(request, response, command, errors);
引用
PagedListHolder
似乎是一个分页的类。没看到这个list在index.jsp页面有调用。他在哪里?不知道。
经过这个请求的跟踪,我发现spring mvc真的太灵活。以至于我都不知道什么地方该写哪些?没人告诉我必须实现
引用
public AccountFormController() {
setSessionForm(true);
setValidateOnBinding(false);
setCommandName("accountForm");
setFormView("EditAccountForm");
}
里面的4个方法。如果我没写setFormView("EditAccountForm");应该不行。但是没有一个机制让我知道哪些是必须的。在这样的系统上写代码是不是有点走钢丝的感觉?
从代码的角度来看,它这样的实现无可厚非,但似乎对使用者不够友好 。