1. 程式人生 > >SpringMVC(16) - 約定優於配置支援

SpringMVC(16) - 約定優於配置支援

參考: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文件。