©
This document uses PHP Chinese website manual Release
对于很多项目来说,严格遵从已有惯例和使用合理的缺省选项大概是这些项目需要的……现在Spring Web MVC明确的支持了这种惯例优先原则的主旨。
这意味着,如果建立了一套命名规范,诸如此类,就可以显著地减少系统所需配置项目的数量,
来建立处理器映射、视图解析器、ModelAndView
实例,等等。
这为快速原型开发提供了很大方便。同时提供了一定程度的(通常是好事情)代码库的一致性,进而可以从中选择并发展为成型产品。
Spring分发版本包含了一个展现了惯例优先原则支持的Web应用程序,我们将在这一节描述这一原则。
这个应用程序可以在samples/showcases/mvc-convention
目录中找到。
惯例优先原则支持体现在MVC的三个核心领域:模型、视图和控制器。
ControllerClassNameHandlerMapping
类是HandlerMapping
接口的一个实现。
它使用惯例来确定请求的URL和用于处理它们的Controller
实例间的映射关系。
举个例子,考虑下面的(直观的)Controller
实现,
请特别注意这个类的名称。
public class ViewShoppingCartController implements Controller { public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) { // the implementation is not hugely important for this example... } }
下面是与之伴随的Spring Web MVC配置文件的一个片段:
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/> <bean id="viewShoppingCart" class="x.y.z.ViewShoppingCartController"> <!-- inject dependencies as required... --> </bean>
ControllerClassNameHandlerMapping
在它的应用上下文中找出所有不同的处理器(handler)(或Controller
)bean,
并去掉名称中的Controller
,来定义它的处理器映射。
让我们看更多的例子,这样其中的中心思想就马上就清楚了。
WelcomeController
映射到“/welcome*
”请求URL
HomeController
映射到“/home*
”请求URL
IndexController
映射到“/index*
”请求URL
RegisterController
映射到“/register*
”请求URL
DisplayShoppingCartController
映射到“/displayshoppingcart*
请求URL
(注意大小写――全部小写――对于驼峰式大小写(第一个词的首字母小写,随后的每个词首字母大写)的Controller
类名。)
当控制器是MultiActionController
处理器类时,生成的映射就(有一点点)更为复杂,但幸而没有更难理解。
下面例子中的几个Controller
名字假设都是MultiActionController
的实现。
使用 请求和/或响应对象(Servlet API或者Portlet API)。
可以选择任何特定的请求/响应类型,例如,ServletRequest/HttpServletRequest或者PortletRequest/ActionRequest/RenderRequest。
注意那个Portlet的例子里,一个被显式声明了的action/render参数被用于映射特定的请求类型到一个处理方法(在没有提供其他信息来区分action和render requests的情况下)。 会话对象(Servlet API或者Portlet API):不管是HttpSession还是PortletSession。
一个此种类型的参数将会保证出现一个对应的会话。这样就造成,这样一个参数永远也不可以是 以 绑定参数到的命令/表单对象:带有自定义的类型转换的bean属性或者域,依赖于AdminController
映射到“/admin
@RequestMapping("/welcome.do")
public void welcomeHandler() {
}
@RequestMapping("/vets.do")
public ModelMap vetsHandler() {
return new ModelMap(this.clinic.getVets());
}
@RequestMapping("/owner.do")
public ModelMap ownerHandler(@RequestParam("ownerId") int ownerId) {
return new ModelMap(this.clinic.loadOwner(ownerId));
}
}
@RequestMapping
注解的处理器方法允许具有非常灵活的外观。
它们可以拥有下面类型的参数,在任意的顺序下(除非是对于验证结果,它需要紧跟在对应的命令对象后面,如果需要):
null
。
注意会话访问可以并不是线程安全的,特别是在Servlet环境中:如果允许多个请求同时访问一个会话,就考虑把AnnotationMethodHandlerAdapter
的“synchronizeOnSession”旗标置为“true”org.springframework.web.context.request.WebRequest
或org.springframework.web.context.request.NativeWebRequest
。
允许像访问请求/会话属性一样的访问一般的请求参数,而不是锁定在原生的Servlet/Portlet API上。java.util.Locale
用于当前请求区域属性(由可用的最接近的区域属性解析器决定,也就是,
在Servlet环境中配置好的LocaleResolver
以及在Portlet环境中的portal locale)。java.io.InputStream
/java.io.Reader
用于访问请求的内容。
这将是Servlet/Portlet API暴露出的天然的InputStream/Reader。java.io.OutputStream
/java.io.Writer
用于生成响应的内容。
这将是Servlet/Portlet API暴露出的天然的OutputStream/Writer。@RequestParam
注解的参数用于访问特定的Servlet/Portlet请求参数。
参数的值将被转换为声明的方法参数类型。java.util.Map
/org.springframework.ui.Model
/org.springframework.ui.ModelMap
用于充实将被暴露到Web视图的隐含模型。@InitBinder
方法和/或HandlerAdapter配置――参见AnnotationMethodHandlerAdapter
的“webBindingInitializer
”属性。
这样的命令对象,包括它们的验证结果,将会暴露为模型属性,默认的会在属性注解中使用非限定的命令类名(例如,对于类型“mypackage.OrderAddress”使用“orderAddress”)。
为声明一个特定的模型属性名称指定一个参数级别的ModelAttribute
注解。org.springframework.validation.Errors
/org.springframework.validation.BindingResult
验证结果用于前面的一个命令/表单对象(前面紧接的参数)。org.springframework.web.bind.support.SessionStatus
状态处理用于把表单处理过程标记为已完成(触发会话属性的清理,这些会话属性是在句柄类型级别由@SessionAttributes
注解指示出的)。
@RequestParam
注解是用于在控制器中绑定请求参数到方法参数。
下面取自PetClinic实例程序的代码片段说明了这种用法:
@Controller @RequestMapping("/editPet.do") @SessionAttributes("pet") public class EditPetForm { // ... @RequestMapping(method = RequestMethod.GET) public String setupForm(@RequestParam("petId") int petId, ModelMap model) { Pet pet = this.clinic.loadPet(petId); model.addAttribute("pet", pet); return "petForm"; } // ...
使用这个注解的参数默认是必需的,但是可以把@RequestParam
的required
属性置为false
从而让这个参数可选(例如,@RequestParam(value="id", required="false")
)。
@ModelAttribute
在控制器中有两种使用场景。
当作为一个方法参数时,@ModelAttribute
用于映射一个模型属性到特定的注解的方法参数(见下面的processSubmit()
方法)。
这是控制器获得持有表单数据的对象引用的方法。另外,这个参数也可以被声明为特定类型的表单支持对象,而不是一般的java.lang.Object
,这就增加了类型安全性。
@ModelAttribute
也用于在方法级别为模型提供引用数据(见下面的populatePetTypes()
方法)。
在这种用法中,方法编写可以包含与上面描述的@RequestMapping
注解相同的类型。
注意:使用@ModelAttribute
注解的方法将会在选定的使用@RequestMapping
注解的方法之前执行。
它们有效的使用特定的属性预先填充隐含的模型,这些属性常常来自一个数据库。
这样一个属性也就可以通过在选定的方法中使用@ModelAttribute
注解的句柄方法参数来访问了,潜在的可以应用绑定和验证。
下面的代码片段展示了此注解的这两种用法:
@Controller @RequestMapping("/editPet.do") @SessionAttributes("pet") public class EditPetForm { // ... @ModelAttribute("types") public Collection<PetType> populatePetTypes() { return this.clinic.getPetTypes(); } @RequestMapping(method = RequestMethod.POST) public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result, SessionStatus status) { new PetValidator().validate(pet, result); if (result.hasErrors()) { return "petForm"; } else { this.clinic.storePet(pet); status.setComplete(); return "redirect:owner.do?ownerId=" + pet.getOwner().getId(); } } }
类型级别的@SessionAttributes
注解使用一个特定的句柄声明会话属性。
这通常会列出模型属性的名称,这些属性应被透明的保存在会话或者对话存储中,用于在后续的请求之间作为表单支持beans。
下面的代码片段展示了此注解的这种用法:
@Controller @RequestMapping("/editPet.do") @SessionAttributes("pet") public class EditPetForm { // ... }
为了通过Spring的WebDataBinder
使用PropertyEditors等自定义请求参数绑定,可以或者使用@InitBinder
――在控制器之内的注解的方法,
或者通过提供一个定制的WebBindingInitializer
把配置具体化。
使用@InitBinder
注解控制器方法,可以在控制器类内部直接配置Web数据绑定。
@InitBinder
指定初始化WebDataBinder
的方法,
后者被用于填充注解的句柄方法的命令和表单对象参数。
这个init-binder方法支持@RequestMapping
支持的全部参数,除了命令/表单对象和对应的验证结果对象。
Init-binder方法必须没有返回值。因此,它们常被声明为void
。
典型的参数,包括 WebDataBinder
以及WebRequest
或者java.util.Locale
,允许代码注册上下文特定的编辑器。
下面的例子说明了@InitBinder
的用法,为所有的java.util.Date
表单属性配置一个CustomDateEditor
。
@Controller public class MyFormController { @InitBinder public void initBinder(WebDataBinder binder) { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); dateFormat.setLenient(false); binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false)); } // ... }
为了外化数据绑定初始化的过程,可以提供一个WebBindingInitializer
接口的自定义实现。
通过为一个AnnotationMethodHandlerAdapter
提供一个定制的bean配置可以使它启用,这样就覆盖了默认配置。
下面取自PetClinic应用的例子展示了一个使用WebBindingInitializer
接口的自定义实现的配置――org.springframework.samples.petclinic.web.ClinicBindingInitializer
,
完成多个PetClinic控制器都需要的PropertyEditors的配置。
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="cacheSeconds" value="0" /> <property name="webBindingInitializer"> <bean class="org.springframework.samples.petclinic.web.ClinicBindingInitializer" /> </property> </bean>