Skip to content
springside edited this page Mar 30, 2012 · 15 revisions

##OverView SpringMVC回归MVC本质,简简单单的Restful式函数,没有任何基类之后,应该是传统Request-Response框架中最好用的了。

##Tips

1.事务失效的惨案

Spring MVC最打击新人的事情,你必须保证spring-mvc.xml的context:component-scan只扫描Controller,而 applicationContext.xml里的不包含Controller. 否则你定义在applicationContext.xml里的事务就要失效了。方法如下:

spring-mvc.xml:

	<context:component-scan base-package="com.mycompany.myproject" use-default-filters="false">
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
	</context:component-scan>

applicationContext.xml:

	<context:component-scan base-package="org.springside.examples.miniweb">
		<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
	</context:component-scan>

另外,定义在spring-mvc.xml里的东西,在applicationContext*.xml中是不可见的,想共享的东西最好放在applicationContext.xml那边。
而applicationContext*.xml里的一些BeanPostProccesor,也不会作用到spring-mvc.xml定义/扫描到的Bean上,如果有必要就在spring-mvc.xml里重新定义一次,像Shiro的AOP校验权限。

2.Struts2式的Preparable接口

Struts2有一个很实用的Preparable二次绑定功能: 表单提交时,先绑定一个ID,使用这个ID从数据库里找出对象来,再把表单的其他属性绑定到这个对象上,对于那些表单中的输入框数量比业务对象的实际属性数量少的情况很实用。

其实Spring MVC也有相同的能力,见mini-web中的GroupDetailController.

先用@ModelAttribute标注如下函数。SpringMVC会在执行任何实际处理函数之前,执行该函数并将返回值存为属性"group"

	@ModelAttribute("group")
	public Group getGroup(@PathVariable("id") Long id) {
		return accountManager.getGroup(id);
	}

再在save函数里,以@ModelAttribute标注表单处理函数的参数。SpringMVC就会按名称取出前面的对象,进行真正的Binding

	@RequestMapping(value = "save/{id}")
	public String save(@ModelAttribute("group") Group group, RedirectAttributes redirectAttributes) {
		accountManager.saveGroup(group);
	}

有个小可惜的地方是因为这个getGroup()会在任意函数都执行,但是有些函数(如create/list)不需要或者没有id在路径中,所有只好把这个Edit&Save的动作独立到另一个Controller了,于是便有了GroupController和GroupDetailController两个Controller。

3.Struts2式的FlashAttribute

为了防止用户刷新重复提交,save操作之后一般会redirect到另一个页面,同时带点操作成功的提示信息。因为是Redirect,Request里的attribute不会传递过去,如果放在session中,则需要在显示后及时清理,不然下面每一页都带着这个信息也不对。Spring在3.1才提供了这个能力。

	public String save(@ModelAttribute("group") Group group, RedirectAttributes redirectAttributes) {
		accountManager.saveGroup(group);
		redirectAttributes.addFlashAttribute("message", "修改权限组成功");
		return "redirect:/account/group/";
	}

4.PropertyEditor 与 CheckBox绑定

在采用ORM的应用中,如果绑定子对象到页面上,以及在表单提交时如何把checkbox的内容重新绑回父对象是一个头痛的问题。

在mini-web示例中,User-Group组合中的Group是一个复杂对象,而Group-Permission组合中的Permission是一个简单的枚举。

对于简单的枚举,什么都不用做,直接用checkboxes的taglib就可以了。

	<form:checkboxes path="permissionList" items="${allPermissions}" itemLabel="displayName" itemValue="value" />

而Group这种对象就没这么好彩了,需要写一个针对gorupList的Editor,详见mini-web示例。不过虽然麻烦点,但最终出去回来都自动绑定的效果还挺不错的。

5.输出跨域Ajax所需的JsonP

网上说什么扩展JsonView什么的太复杂了,自己拿Jackson生成一个JsonP的字符串返回就好了。 更多JSONP信息见Ajax章节.

6.Spring MVC与Hibernate Validator的结合

Validation章节,一般情况下使用JQuery Validation Plugin的客户端认证。为了防止恶意用户的攻击,可以再加上Spring MVC与Hibernate Validator的服务端认证。 Springside在showcase中用户修改页面做了演示,共4步:

  1. 在spring-mvc.xml中,加入hibernate validator的定义
  2. 在User.java的相关属性加入@NotBlank定义
  3. 在UserDetailController的save方法,加入@Valid定义和BindingResult参数,并对BindingResult参数进行判断。
  4. 在userForm.jsp的每个可能出错的input框后面补一句 <form:errors path="loginName" cssClass="error"/>

返回参考手册

Clone this wiki locally