1. 引言
在 Spring MVC 中,<em >DispatcherServlet</em> 作為前置控制器,負責接收所有傳入的 HTTP 請求並進行處理。
簡單來説,處理是通過將請求傳遞給相關的組件,藉助 <em >handler mappings</em> 實現的。
<em >HandlerMapping</em> 是一個定義請求與處理器對象之間映射關係的接口。雖然 Spring MVC 框架提供了一些現成的實現,但開發者也可以通過實現該接口來提供自定義的映射策略。
本文將討論 Spring MVC 提供的幾種實現,包括 <em >BeanNameUrlHandlerMapping</em>、<em >SimpleUrlHandlerMapping</em>、<em >ControllerClassNameHandlerMapping</em>,以及它們的配置以及它們之間的差異。
2.BeanNameUrlHandlerMapping
BeanNameUrlHandlerMapping 是默認的 HandlerMapping 實現。 BeanNameUrlHandlerMapping 將請求 URL 映射到具有相同名稱的 Bean。
此映射實現支持直接名稱匹配,以及使用“*”模式的模式匹配。
例如,傳入的 URL “/foo” 映射到名為 “/foo” 的 Bean。 模式匹配的示例是,將請求映射到 “/foo*” 到以 “/foo” 開頭的 Bean,例如 “/foo2/” 或 “/fooOne/”。
讓我們在這裏配置此示例並註冊一個處理請求到 “/beanNameUrl” 的 Bean 容器:
@Configuration
public class BeanNameUrlHandlerMappingConfig {
@Bean
BeanNameUrlHandlerMapping beanNameUrlHandlerMapping() {
return new BeanNameUrlHandlerMapping();
}
@Bean("/beanNameUrl")
public WelcomeController welcome() {
return new WelcomeController();
}
}這是基於 Java 的配置的 XML 對應內容:
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
<bean name="/beanNameUrl" class="com.baeldung.WelcomeController" />
需要注意的是,在兩種配置中,定義 BeanNameUrlHandlerMapping 的 Bean 並不會產生影響,因為 Spring MVC 已經提供了它。刪除此 Bean 定義不會導致任何問題,請求仍然會被映射到已註冊的 Handler Bean。
現在,所有指向 “/beanNameUrl” 的請求都將由 DispatcherServlet 轉發到 WelcomeController。 WelcomeController 返回一個名為 “welcome” 的視圖。
以下代碼測試了此配置,並確保返回正確的視圖名稱:
public class BeanNameMappingConfigTest {
// ...
@Test
public void whenBeanNameMapping_thenMappedOK() {
mockMvc.perform(get("/beanNameUrl"))
.andExpect(status().isOk())
.andExpect(view().name("welcome"));
}
}3. SimpleUrlHandlerMapping
下一階段,SimpleUrlHandlerMapping 是最靈活的 HandlerMapping 實現。它允許直接且聲明式地將 Bean 實例與 URL 或 Bean 名稱與 URL 之間建立映射關係。
讓我們將請求 “/simpleUrlWelcome” 和 “/*/simpleUrlWelcome” 映射到 “welcome” Bean:
@Configuration
public class SimpleUrlHandlerMappingConfig {
@Bean
public SimpleUrlHandlerMapping simpleUrlHandlerMapping() {
SimpleUrlHandlerMapping simpleUrlHandlerMapping
= new SimpleUrlHandlerMapping();
Map<String, Object> urlMap = new HashMap<>();
urlMap.put("/simpleUrlWelcome", welcome());
simpleUrlHandlerMapping.setUrlMap(urlMap);
return simpleUrlHandlerMapping;
}
@Bean
public WelcomeController welcome() {
return new WelcomeController();
}
}或者,以下是其等效的 XML 配置:
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<value>
/simpleUrlWelcome=welcome
/*/simpleUrlWelcome=welcome
</value>
</property>
</bean>
<bean id="welcome" class="com.baeldung.WelcomeController" />需要注意的是,在 XML 配置中,必須建立一個與 “<value>” 標籤的映射,該映射必須符合 java.util.Properties 類的接受格式,並且應遵循以下語法:path= Handler_Bean_Name。
URL 通常應包含前導斜槓,但是如果路徑沒有以斜槓開頭,Spring MVC 會自動添加它。
配置上述示例的另一種方式是在 XML 中使用 “props” 屬性而不是 “value” 屬性。Props 具有一個 “prop” 標籤列表,其中每個標籤定義一個映射,其中 “key” 指向映射的 URL,該標籤的值是 bean 的名稱。
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/simpleUrlWelcome">welcome</prop>
<prop key="/*/simpleUrlWelcome">welcome</prop>
</props>
</property>
</bean>以下測試用例確保對“/simpleUrlWelcome”請求由“WelcomeController”處理,並返回一個名為“welcome”的視圖:
public class SimpleUrlMappingConfigTest {
// ...
@Test
public void whenSimpleUrlMapping_thenMappedOK() {
mockMvc.perform(get("/simpleUrlWelcome"))
.andExpect(status().isOk())
.andExpect(view().name("welcome"));
}
}4. ControllerClassNameHandlerMapping (Spring 5 中已移除)
該 ControllerClassNameHandlerMapping 將 URL 映射到已註冊的控制器 Bean(或帶有 @Controller 註解的控制器)上,該控制器名稱與其相同或以其名稱開頭。
它在許多場景下都非常方便,尤其是在處理單個請求類型的簡單控制器實現時。 Spring MVC 中使用的約定是使用類名並刪除 “Controller” 後綴,然後將名稱轉換為小寫,並將其作為以 “/” 開頭的映射返回。
例如,“WelcomeController” 將映射到 “/welcome*”,即以 “welcome” 開頭的任何 URL。
讓我們配置 ControllerClassNameHandlerMapping:
@Configuration
public class ControllerClassNameHandlerMappingConfig {
@Bean
public ControllerClassNameHandlerMapping controllerClassNameHandlerMapping() {
return new ControllerClassNameHandlerMapping();
}
@Bean
public WelcomeController welcome() {
return new WelcomeController();
}
}請注意,ControllerClassNameHandlerMapping 從 Spring 4.3 版本開始已棄用,取而代之的是註解驅動的處理器方法。
另一個重要的注意事項是,控制器名稱始終以小寫形式返回(不包括“Controller”後綴)。因此,如果我們的控制器名為 “WelcomeBaeldungController” ,它只會處理請求到 “/welcomebaeldung” ,而不會處理 “/welcomeBaeldung”。
在以下 Java 配置和 XML 配置中,我們定義了 ControllerClassNameHandlerMapping Bean 並註冊了用於處理請求的控制器 Bean。我們還註冊了一個類型為 “WelcomeController” 的 Bean,該 Bean 將處理所有以 “/welcome” 開頭的請求。
以下是等效的 XML 配置:
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping" />
<bean class="com.baeldung.WelcomeController" />
使用上述配置時,對“/welcome”的請求將被“WelcomeController”處理。
以下代碼確保對“/welcome*”請求,例如“/welcometest”的處理由“WelcomeController”完成,該“WelcomeController”返回一個名為“welcome”的視圖。
public class ControllerClassNameHandlerMappingTest {
// ...
@Test
public void whenControllerClassNameMapping_thenMappedOK() {
mockMvc.perform(get("/welcometest"))
.andExpect(status().isOk())
.andExpect(view().name("welcome"));
}
}5. 配置優先級
Spring MVC 框架允許同時使用多個 HandlerMapping 接口的實現。
讓我們創建一個配置並註冊兩個控制器,它們都映射到 URL “/welcome”,僅使用不同的映射和返回不同的視圖名稱:
@Configuration
public class HandlerMappingDefaultConfig {
@Bean("/welcome")
public BeanNameHandlerMappingController beanNameHandlerMapping() {
return new BeanNameHandlerMappingController();
}
@Bean
public WelcomeController welcome() {
return new WelcomeController();
}
}在未註冊任何顯式映射器的情況下,將使用默認的 BeanNameHandlerMapping。 讓我們通過測試來驗證這種行為:
@Test
public void whenConfiguringPriorities_thenMappedOK() {
mockMvc.perform(get("/welcome"))
.andExpect(status().isOk())
.andExpect(view().name("bean-name-handler-mapping"));
}
如果明確註冊了不同的映射器,則默認映射器將被覆蓋。然而,當明確註冊兩個映射器時,會產生有趣的結果:
@Configuration
public class HandlerMappingPrioritiesConfig {
@Bean
BeanNameUrlHandlerMapping beanNameUrlHandlerMapping() {
BeanNameUrlHandlerMapping beanNameUrlHandlerMapping
= new BeanNameUrlHandlerMapping();
return beanNameUrlHandlerMapping;
}
@Bean
public SimpleUrlHandlerMapping simpleUrlHandlerMapping() {
SimpleUrlHandlerMapping simpleUrlHandlerMapping
= new SimpleUrlHandlerMapping();
Map<String, Object> urlMap = new HashMap<>();
urlMap.put("/welcome", simpleUrlMapping());
simpleUrlHandlerMapping.setUrlMap(urlMap);
return simpleUrlHandlerMapping;
}
@Bean
public SimpleUrlMappingController simpleUrlMapping() {
return new SimpleUrlMappingController();
}
@Bean("/welcome")
public BeanNameHandlerMappingController beanNameHandlerMapping() {
return new BeanNameHandlerMappingController();
}
}為了控制使用的映射,通過setOrder(int order)方法設置優先級。此方法接受一個 int參數,其中較低的值表示較高的優先級。
在XML配置中,可以通過名為 “order”的屬性來配置優先級。
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
<property name="order" value="2" />
</bean>
讓我們為處理映射 Bean 添加 order 屬性,通過使用 beanNameUrlHandlerMapping.setOrder(1) 和 simpleUrlHandlerMapping.setOrder(0) 。較低的 order 屬性值表示更高的優先級。讓我們通過測試來驗證新的行為:
@Test
public void whenConfiguringPriorities_thenMappedOK() {
mockMvc.perform(get("/welcome"))
.andExpect(status().isOk())
.andExpect(view().name("simple-url-handler-mapping"));
}在對上述配置進行測試時,您會發現請求到 “/welcome” 會被 SimpleUrlHandlerMapping bean 處理,該 bean 調用 SimpleUrlHandlerController 並返回 simple-url-handler-mapping view。 我們可以通過調整 BeanNameHandlerMapping 的 order 屬性值來使其優先級更高。
6. 結論
在本文中,我們探討了 Spring MVC 框架中 URL 映射的處理方式,通過研究框架中不同的實現來深入瞭解。