对于很多项目,坚持既定的约定,并设置合理的默认值是他们(项目)所需要的东西。并且Spring WEB MVC已经明确的支持约定优于配置(or Convention over configuration)。这意味着如果你建立一组命名约定等,你可以充分的减少大多数配置。其中必要的设置包含handler mappings,view resolvers, ModelAndView
实例等等。对于快速原型这是一个被视为很好的,提供一定程度的(总是好)一致性代码库。你应该选择把它推行到生产环境中。
约定优于配置支持定位MVC三个基本的地方: models, view 和controllers.
ControllerClassNameHandlerMapping
类是HandlerMapping
的一个实现.它使用约定去决定requeset URLS与处理这些请求的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">
bean>
ControllerClassNameHandlerMapping
会找到所有不同的定义在它的应用context中的handler(or Controller
)beans并且剥离Controller
中定义的handler mappings。从而,ViewShoppingCartController
映射/viewshoppingcart*
到请求URL。
让我们看一下一些其它的例子,所以,主要的想法立即变得熟悉。(注意所有URLs中的小名字母,与Controller
类名的骆峰式形成对照)。
WelcomeController
maps to the /welcome*
request URLHomeController
maps to the /home*
request URLIndexController
maps to the /index*
request URLRegisterController
maps to the /register*
request URL在情况下MultiActionController
这个handler类,他映射生成更复杂。在下面的例子中,这个Controller
名称假定认为是MultiActionController
的实现。
AdminController
maps to the /admin/*
request URLCatalogController
maps to the /catalog/*
request URL如果你按照约定的命名你的Controller
实现为xxxController
,ControllerClassNameHandlerMapping
会按昭上面保存你的定义并且维持一个的SimpleUrlHandlerMapping
(or suchlike).
ControllerClassNameHandlerMapping
继承于基类AbstractHandlerMapping
,因此你可以定义HandlerInterceptor
实例并且你想定义的其它HandlerMapping
实现。
ModelMap
类本质上是一个增强的Map
,它是一个常见的命名约定,用来添加对应View
用来展示的对象。思考一下下面的Controller
实现;注意:被添加到ModelAndView
的对象并没有指定关联的名称。
public class DisplayShoppingCartController implements Controller {
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) {
List cartItems = // get a List of CartItem objects
User user = // get the User doing the shopping
ModelAndView mav = new ModelAndView("displayShoppingCart"); <-- the logical view name
mav.addObject(cartItems); <-- look ma, no name, just the object
mav.addObject(user); <-- and again ma!
return mav;
}
}
ModelAndView
类使用一个自定义的Map
实现类 – ModelMap
。它一个对象被添加到它的时候,它可以自动的为一个对象生成一个key值。决定这个被添加对象的名称的策略,我们以一个标准的User
对象来说明。它会使用这个对象类的短类名.下面的例子就是放`进ModelMap
实例的标准对象的名称。
x.y.User
实例被添加会生成user
名称.x.y.Registration
实例被添加会生成registration
名称.x.y.Foo
实例被添加会生成foo
名称.java.util.HashMap
实例被添加会生成hashMap
名称.你可能想要明确这种情况下的名称,因为hashMap
显得并不直观。null
对象被添加会导致抛出IllegalArgumentException
异常。如果你想添加的object(or objects)为null
,你需要明确的指明它的名称。What, no automatic pluralization?
Spring Web MVC的约定优于配置并不自动支持泛型。也就是说
也就是说,你不能够添加Person
的List
对象到ModelAndView
中并且生成名称为people
.
这个决定会引起后面很多争论,”Principle of Least Surprise”最后赢了。
当添加一个Set
或者List
对象的时候,它们生成名称的策略可以看一看collection.首先获取集合中第一个对象的类的短名称,然后再这个名称的后面添加"List"
.对于数组也是一样的。下面的几个例子将会清楚的展现集合名称生成的语法:
x.y.User[]
如果有一个或者多个x.y.User
元素将生成添加userList
名称.x.y.Foo[]
如果有一个或者多个 x.y.User
元素将生成添加fooList
名称.java.util.ArrayList
如果有一个或者多个 x.y.User
元素将生成添加userList
名称.java.util.HashSet
如果有一个或者多个 x.y.Foo
元素将生成添加fooList
名称.java.util.ArrayList
不会添加元素 (事额头上, the addObject(..)
将不会被调用).当没有明确提出相应的逻辑view名称,RequestToViewNameTranslator
会确定一个逻辑上的View
名称。它仅有一个实现,就是DefaultRequestToViewNameTranslator
。
DefaultRequestToViewNameTranslator
会映射requeset URL到一个逻辑的页面名称,正如下面的这个例子:
public class RegistrationController implements Controller {
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) {
// process the request...
ModelAndView mav = new ModelAndView();
// add data as necessary to the model...
return mav;
// notice that no View or logical view name has been set
}
}
<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">
<bean id="viewNameTranslator"
class="org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator"/>
<bean class="x.y.RegistrationController">
bean>
<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
会负责对request的URL生成一个逻辑的页面名称.RegistrationController
这种情况下,ControllerClassNameHandlerMapping
结合使用的是哪一个,DefaultRequestToViewNameTranslator
会生成一个registration
逻辑名称用来关联http://localhost/registration.html
这个request URL。这个逻辑页面名称然后通过InternalResourceViewResolver
这个bean来解析成/WEB-INF/jsp/registration.jsp
。
注意 :
你不需要明确的定义一个DefaultRequestToViewNameTranslator
bean。如果你喜欢DefaultRequestToViewNameTranslator
的默认设置,如果你没有明确配置的话,你可以依赖Spring Web MVC中的DispatcerServlet
来实例化这个类的实例。
当然,如果你需要改变默认的设置,你可以明确的配置你自己的DefaultRequestToViewNameTranslator bean
。可以查看DefaultRequestToViewNameTranslator
的javadocs了解关于它可以配置的各种properties的详情。
因为水平有限,翻译不足之处还望见谅。
原文地址:spring-framework-reference-4.2.6.RELEASE