1. 简介

在 Spring MVC 中,DispatcherServlet 充当前端控制器角色——接收所有 HTTP 请求并进行处理。

简单来说,处理过程是通过处理器映射(Handler Mappings) 将请求转发给相应的组件来完成的。

HandlerMapping 是一个接口,用于定义请求与处理器对象之间的映射关系。虽然 Spring MVC 框架提供了一些现成的实现,但开发者也可以实现该接口来提供自定义的映射策略。

本文将讨论 Spring MVC 提供的几种实现:BeanNameUrlHandlerMappingSimpleUrlHandlerMappingControllerClassNameHandlerMapping(Spring 5 已移除),包括它们的配置方式以及之间的差异。

2. BeanNameUrlHandlerMapping

BeanNameUrlHandlerMapping 是默认的 HandlerMapping 实现。它将请求 URL 映射到同名的 Bean。

这种映射支持直接名称匹配,也支持使用 * 模式的通配符匹配。例如:

  • 直接匹配:URL /foo 映射到名为 /foo 的 Bean
  • 通配符匹配:URL /foo* 映射到以 /foo 开头的 Bean(如 /foo2//fooOne/

下面配置一个处理 /beanNameUrl 请求的控制器:

@Configuration
public class BeanNameUrlHandlerMappingConfig {
    @Bean
    BeanNameUrlHandlerMapping beanNameUrlHandlerMapping() {
        return new BeanNameUrlHandlerMapping();
    }

    @Bean("/beanNameUrl")
    public WelcomeController welcome() {
        return new WelcomeController();
    }
}

对应的 XML 配置:

<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
<bean name="/beanNameUrl" class="com.baeldung.WelcomeController" />

⚠️ 注意:两种配置中显式定义 BeanNameUrlHandlerMapping Bean 并非必需,因为 Spring MVC 默认已提供。移除该定义不会影响功能,请求仍会映射到注册的处理器 Bean。

现在所有 /beanNameUrl 请求将由 DispatcherServlet 转发给 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 会自动添加。

另一种 XML 配置方式(使用 props):

<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 请求:

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 注解的类),要求控制器名称与 URL 相同或以 URL 开头。

在简单场景下特别方便,尤其是处理单一请求类型的控制器。Spring MVC 的约定是:

  1. 移除类名中的 Controller 后缀
  2. 转换为小写
  3. 添加前导 / 作为映射

例如 WelcomeController 会映射到 /welcome*,即所有以 welcome 开头的 URL。

配置示例:

@Configuration
public class ControllerClassNameHandlerMappingConfig {

    @Bean
    public ControllerClassNameHandlerMapping controllerClassNameHandlerMapping() {
        return new ControllerClassNameHandlerMapping();
    }

    @Bean
    public WelcomeController welcome() {
        return new WelcomeController();
    }
}

⚠️ 注意

  • 该类在 Spring 4.3 已废弃,推荐使用注解驱动的处理器方法
  • 控制器名称始终返回小写(移除 Controller 后缀)。例如 WelcomeBaeldungController 只能处理 /welcomebaeldung 而非 /welcomeBaeldung

对应的 XML 配置:

<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping" />
<bean class="com.baeldung.WelcomeController" />

测试代码验证 /welcome* 请求(如 /welcometest):

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) 方法控制优先级(数值越小优先级越高):

// Java 配置
beanNameUrlHandlerMapping.setOrder(1);
simpleUrlHandlerMapping.setOrder(0);

XML 配置方式:

<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
    <property name="order" value="2" />
</bean>

测试验证优先级(order=0SimpleUrlHandlerMapping 优先):

@Test
public void whenConfiguringPriorities_thenMappedOK() {
    mockMvc.perform(get("/welcome"))
      .andExpect(status().isOk())
      .andExpect(view().name("simple-url-handler-mapping"));
}

关键点:调整 order 值可轻松切换优先级映射器。

6. 总结

本文探讨了 Spring MVC 框架中 URL 映射的处理机制,重点分析了框架提供的几种 HandlerMapping 实现:

  1. BeanNameUrlHandlerMapping

    • 默认实现,通过 Bean 名称直接映射 URL
    • 支持通配符匹配(如 /foo*
  2. SimpleUrlHandlerMapping

    • 最灵活的实现
    • 支持声明式映射(Bean 实例/名称与 URL)
  3. ControllerClassNameHandlerMapping

    • 基于控制器类名生成映射(Spring 5 已移除)
    • 约定优于配置的典型应用
  4. 优先级配置

    • 多映射器共存时通过 order 属性控制优先级
    • 数值越小优先级越高

合理选择映射器能显著提升 Spring MVC 应用的灵活性与可维护性。实际开发中,推荐优先使用注解驱动的 @RequestMapping(现代 Spring 应用的主流方案),但在特定场景下(如遗留系统迁移),这些传统映射器仍有实用价值。


原始标题:Guide to Spring Handler Mappings | Baeldung