慣例優先原則(convention over configuration)


13.11. 慣例優先原則(convention over configuration) - Spring Framework reference 2.0.5 參考手冊中文版

13.11. 慣例優先原則(convention over configuration)

對於很多項目來說,遵從已有的慣例和使用合理的缺省選項大概是最合情合理的做法。現在Spring Web MVC框架也明確支持這種慣例優先的配置。具體來說,如果你在項目中遵守一定的慣例(比如命名規範),你可以顯著地減少系統需要的配置(比如處理器映射,視圖解析器配置,ModelAndView的聲明,等等)。這對快速系統建模(rapid prototyping)是非常有利的。如果你打算進一步把模型完成爲可以工作的系統,這樣寫出的代碼也具有很好的一致性。

提示

Spring的開發包中有一個web應用的範例。這個範例演示了這一節提到的慣例優先原則。你可以在samples/showcases/mvc-convention目錄中找到這個範例。

This convention over configuration support address the three core areas of MVC - namely, the models, views, and controllers.

Spring對慣例優先原則的支持體現在MVC的3個核心領域:模型、視圖和控制器。

13.11.1. 對控制器的支持: ControllerClassNameHandlerMapping

ControllerClassNameHandlerMappingHandlerMapping接口的一個實現。它檢查請求的URL,然後通過慣例來決定與之相對應的控制器。

比如,下面有個非常簡單的控制器實現,請特別注意這個類的名字

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。把這些bean的名字中Controller後綴去掉,就得到了每個控制器所能處理的URL。

讓我們通過舉例來進一步解釋這個映射的工作原理。

  • WelcomeController映射到'/welcome*'這個URL

  • HomeController映射到'/home*'這個URL

  • IndexController映射到'/index*'這個URL

  • RegisterController映射到'/register*'這個URL

  • DisplayShoppingCartController映射到'/displayshoppingcart*'這個URL

    (注意字母的大小寫。URL全部都用小寫,但在Java類名中每個單詞的第一個字母要大寫。)

當控制器是MultiActionController的子類時,自動生成的映射就稍有點複雜,但應該還是比較好理解的。下面例子中這幾個控制器都是MultiActionController

  • AdminController映射到 '/admin/*' 這個URL。

  • CatalogController映射到 '/catalog/*' 這個URL。

如果你的控制器類遵守這些命名規範(xxxController),ControllerClassNameHandlerMapping可以自動生成映射,這樣你就不必費勁的定義和維護一長串SimpleUrlHandlerMapping(或者類似的映射策略)。

ControllerClassNameHandlerMappingAbstractHandlerMapping的子類,所以你仍舊可以像對待其他HandlerMapping實例一樣來定義HandlerInterceptor的實例。

13.11.2. 對模型的支持:ModelMap (ModelAndView)

ModelMap是一個加強版的Map實現。在這個Map裏,每個對象的鍵都遵守一個命名規範,然後這些對象就可以顯示在視圖中。這個類的使用其實很簡單,不需要長篇大論。下面讓我們看幾個例子,然後我們結合例子進行講解。

下面是一個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 shoppingModelAndView mav = new ModelAndView("displayShoppingCart"); <-- the logical view name mav.addObject(cartItems); <-- look ma, no name, just the object mav.addObject(user); <-- and a'ain ma! return mav; }}

ModelAndView在內部使用了ModelMapModelMap是一個自定義的Map實現,它可以爲加入其中的每個對象自動生成一個鍵名。這些鍵名是有規律的。當存入的對象是存儲單值的對象(scalar object),比如User,生成的鍵名就是對象的類名(不包括包的名字)。下面幾個例子詳細解釋了這個命名規範:

  • x.y.User這個類的實例對應user

  • x.y.Registration 這個類的實例對應registration

  • x.y.Foo這個類的實例對應foo

  • java.util.HashMap的實例對應hashMap(在這種情況下你最好還是自己聲明鍵名,hashMap這個名字不那麼直觀)

  • 當你視圖把null這個值加入Map時,你會得到IllegalArgumentException。 所以如果你的某個對象可能爲null,你最好也自己聲明鍵名。

當你加入ModelAndView中的對象是SetList或者數組時,Spring會檢查這個集合,取出這個集合中的第一個對象,然後用它的類名,加上List後綴,就是最終生成的名字。下面幾個例子進一步解釋了這個命名規則:

  • 一個由 x.y.User組成的數組對應userList這個名字。

  • 一個由x.y.Foo組成的數組對應fooList這個名字。

  • 一個由x.y.User組成的java.util.ArrayList對應userList這個名字。

  • 一個由x.y.Foo組成的java.util.HashSet對應fooList這個名字。

  • 一個空的java.util.ArrayList根本不可能被加到這個Map中。(在這種情況下,adObject(..)其實什麼都沒做)。

13.11.3. 對視圖的支持: RequestToViewNameTranslator

RequestToViewNameTranslator這個接口的功能是自動尋找請求所對應的視圖名(當某個視圖名沒有明確配置的時候)。這個接口目前只有一個實現,類名爲DefaultRequestToViewNameTranslator

爲了解釋DefaultRequestToViewNameTranslator是如何將請求的URL映射到視圖名,最好的方法就是舉例說明。下面是一個Controller的實現,和它對應的配置文件。

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 }}<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd"><beans> <!-- 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.RegistrationControllerController"> <!-- 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的實例或者邏輯視圖名。真正從請求的URL中分析出邏輯視圖名(logical view name)的是DefaultRequestToViewNameTranslator。在上面這個例子中,RegistrationControllerControllerControllerClassNameHandlerMapping在一起使用,所有對http://localhost/registration.html這個URL的請求將會對應registration這個邏輯視圖名。這個視圖名正是由DefaultRequestToViewNameTranslator解析出來的。然後,InternalResourceViewResolver這個bean會將這個邏輯視圖名進一步解析成/WEB-INF/jsp/registration.jsp這個視圖。

提示

你甚至不需要配置類型爲DefaultRequestToViewNameTranslator的bean。如果DefaultRequestToViewNameTranslator的缺省行爲已經符合你的要求,你就可以使用這個類。當你沒有明確聲明時,Spring Web MVC 中DispatcherServlet這個類會自動生成一個DefaultRequestToViewNameTranslator的實例。

當然,如果你有自己特殊的要求,你就需要配置DefaultRequestToViewNameTranslator bean。如果你需要知道這個類有哪些可以設置的參數,請參閱DefaultRequestToViewNameTranslator的Javadoc。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章