参考:https://docs.spring.io/spring/docs/4.3.20.RELEASE/spring-framework-reference/htmlsingle/#mvc-coc
对于很多项目来说,坚持已建立的约定并且具有合理的默认值正是他们(项目)所需要的,而Spring Web MVC现在明确支持约定优于配置。这意味着如果你建立了一组命名约定等,你可以大大减少设置处理器映射、视图解析器、ModelAndView实例等所需的配置量。这对于快速原型设计是一个很大的好处,如果选择将其推向生产阶段,还可以在代码库中提供一定程度的(始终是良好的)一致性。
Convention-over-configuration(约定优于配置)的支持解决了MVC的三个核心领域:模型,视图和控制器。
1. 控制器ControllerClassNameHandlerMapping
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查找在其应用程序上下文中定义的所有各种处理器(或Controller)bean,并从名称中删除Controller以定义其处理器映射。因此,ViewShoppingCartController映射到 /viewshoppingcart* 请求URL。
示例: (注意URL中的所有小写,与驼峰式的Controller类名相反。)
- WelcomeController:映射到/welcome*请求URL
- HomeController:映射到/home*请求URL
- IndexController:映射到/index*请求URL
- RegisterController:映射到/register*请求URL
对于MultiActionController处理器类,生成的映射稍微复杂一些。以下示例中的Controller名称假定为MultiActionController实现:
- AdminController:映射到/admin/*请求URL
- CatalogController:映射到/catalog/*请求URL
如果遵循将Controller实现命名为xxxController的约定,ControllerClassNameHandlerMapping将节省定义和维护潜在的looooong SimpleUrlHandlerMapping(或类似)的繁琐。
ControllerClassNameHandlerMapping类扩展了AbstractHandlerMapping基类,因此可以像许多其他HandlerMapping实现一样定义HandlerInterceptor实例和其他所有实例。
2. 模型 ModelMap(ModelAndView)
ModelMap类本质上是一个Map,可以使添加要在View中(或在View上)显示的对象遵循通用的命名约定。考虑以下Controller实现;请注意,在未指定任何关联名称的情况下将对象添加到ModelAndView。
public class DisplayShoppingCartController implements Controller {
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) {
List cartItems = // 获取CartItem对象列表
User user = // 获取当前购物的用户
ModelAndView mav = new ModelAndView("displayShoppingCart"); <-- 逻辑视图名称
mav.addObject(cartItems); <-- 没有名称,只有对象
mav.addObject(user); <-- 同上
return mav;
}
}
ModelAndView类使用ModelMap类,该类是一个自定义Map实现,在对象添加到对象时自动生成对象的key。确定添加对象的名称的策略是,在诸如User之类的纯量对象的情况下,使用对象类的短类名。以下示例是为放入ModelMap实例的纯量对象生成的名称。
- x.y.User实例:生成名称为 user 的key
- x.y.Registration实例:生成名称为 registration 的key
- x.y.Foo实例:生成名称为 foo 的key
- java.util.HashMap实例:生成名称为 hashMap 的key。在这种情况下,可能希望明确名称,因为hashMap不够直观。
- null:将导致抛出IllegalArgumentException。如果要添加的对象(或多个对象)可能为null,那么还需要明确该名称。
注:
什么,没有自动复数?
Spring Web MVC的约定配置支持不支持自动为数组、集合生成key。也就是说,不能将Person对象列表添加到ModelAndView并将生成的名称设置为 people。
这个决定是在经过一番辩论之后作出的,最终的"Principle of Least Surprise"获胜。
添加Set或List后生成名称的策略是查看集合,获取集合中第一个对象的短类名,并使用List附加到名称。这同样适用于数组,尽管使用数组时不必查看数组内容。示例:
- x.y.User[]数组:0或多个元素,生成名称为 userList 的key
- x.y.Foo[]数组:0或多个元素,生成名称为 fooList 的key
- java.util.ArrayList:一个或多个x.y.User元素,生成名称为 userList 的key
- java.util.HashSet:一个或多个x.y.Foo元素,生成名称为 fooList 的key
- java.util.ArrayList:空集合,addObject(...) 调用是无操作的,即根本不会被添加
3. 默认视图名称
当没有显式提供此类逻辑视图名称时,RequestToViewNameTranslator接口确定逻辑视图名称。它只有一个实现,DefaultRequestToViewNameTranslator类。
DefaultRequestToViewNameTranslator将请求URL映射到逻辑视图名称,如下例所示:
public class RegistrationController implements Controller {
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) {
// 处理请求...
ModelAndView mav = new ModelAndView();
// 必要时添加数据至model...
return mav;
// 提示,没有View或设置逻辑视图名称
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- this bean with the well known name generates view names for us -->
<bean id="viewNameTranslator"
class="org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator"/>
<bean class="x.y.RegistrationController">
<!-- inject dependencies as necessary -->
</bean>
<!-- maps request URLs to Controller names -->
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
请注意,在handleRequest(...)方法的实现中,如何在返回的ModelAndView上设置View或逻辑视图名称。 DefaultRequestToViewNameTranslator的任务是从请求的URL生成逻辑视图名称。在上述RegistrationController(与ControllerClassNameHandlerMapping一起使用)的情况下,http://localhost/registration.html的请求URL导致由DefaultRequestToViewNameTranslator生成的注册的逻辑视图名称。然后,InternalResourceViewResolver bean将此逻辑视图名称解析为/WEB-INF/jsp/registration.jsp视图。
提示:
不需要显式定义DefaultRequestToViewNameTranslator bean。如果喜欢DefaultRequestToViewNameTranslator的默认设置,则可以依赖Spring Web MVC DispatcherServlet来实例化此类的实例(如果未明确配置该实例)。
当然,如果需要更改默认设置,则需要显式配置自己的DefaultRequestToViewNameTranslator bean。有关可配置的各种属性的详细信息,参考DefaultRequestToViewNameTranslator javadoc文档。