Guide to Spring Handler Mappings – Spring处理程序映射指南

最后修改: 2017年 1月 20日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.介绍

In Spring MVC, the DispatcherServlet acts as front controller – receiving all incoming HTTP requests and processing them.

在Spring MVC中,DispatcherServlet充当前置控制器–接收所有传入的HTTP请求并处理它们。

Simply put, the processing occurs by passing the requests to the relevant component with the help of handler mappings.

简单地说,处理是通过将请求传递给相关的组件来进行的借助于处理程序映射.

HandlerMapping is an interface that defines a mapping between requests and handler objects. While Spring MVC framework provides some ready-made implementations, the interface can be implemented by developers to provide customized mapping strategy.

HandlerMapping是一个接口,它定义了请求和处理程序对象之间的映射。虽然Spring MVC框架提供了一些现成的实现,但该接口可由开发人员实现,以提供自定义的映射策略。

This article discusses some of the implementations provided by Spring MVC namely BeanNameUrlHandlerMapping, SimpleUrlHandlerMapping, ControllerClassNameHandlerMapping, their configuration, and the differences between them.

本文讨论了Spring MVC提供的一些实现,即BeanNameUrlHandlerMappingSimpleUrlHandlerMappingControllerClassNameHandlerMapping,以及它们的配置和区别。

2. BeanNameUrlHandlerMapping

2.BeanNameUrlHandlerMapping

BeanNameUrlHandlerMapping is the default HandlerMapping implementation. BeanNameUrlHandlerMapping maps request URLs to beans with the same name.

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

This particular mapping has support for direct name matching and also for pattern matching using the “*” pattern.

这个特殊的映射支持直接的名称匹配,也支持使用 “*”模式进行模式匹配。

For example, an incoming URL “/foo” maps to a bean called “/foo”. An example of pattern mapping is mapping requests to “/foo*” to beans with names starting with “/foo” like “/foo2/” or “/fooOne/”.

例如,一个传入的URL “/foo” 映射到一个叫做“/foo”的bean。模式映射的一个例子是将对“/foo*”/em>的请求映射到名称以“/foo”/em>开头的bean,如“/foo2/”/em>或“/fooOne/”/em>。

Let’s configure this example here and register a bean controller that handles requests to “/beanNameUrl”:

让我们在这里配置这个例子,注册一个Bean控制器,处理对“/beanNameUrl”的请求。

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

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

This is the XML equivalent of the above Java based config:

这是上述基于Java的配置的XML等价物。

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

It’s important to note that in both of these configurations, defining a bean for BeanNameUrlHandlerMapping is not required as it is provided by Spring MVC. Removing this bean definition will cause no problems and requests will still be mapped to their registered handler beans.

需要注意的是,在这两种配置中,BeanNameUrlHandlerMapping定义一个bean是不需要的,因为它是由Spring MVC提供。删除这个Bean定义不会导致任何问题,请求仍然会被映射到他们注册的处理程序Bean。

Now all requests to “/beanNameUrl” will be forwarded by DispatcherServlet to “WelcomeController“. WelcomeController returns a view name called “welcome“.

现在所有对“/beanNameUrl”的请求将被DispatcherServlet转发到”WelcomeController“。WelcomeController返回一个名为”welcome“的视图名称。

The following code tests this configuration and makes sure that the correct view name is returned:

下面的代码测试了这个配置并确保返回正确的视图名称。

public class BeanNameMappingConfigTest {
    // ...

    @Test
    public void whenBeanNameMapping_thenMappedOK() {
        mockMvc.perform(get("/beanNameUrl"))
          .andExpect(status().isOk())
          .andExpect(view().name("welcome"));
    }
}

3. SimpleUrlHandlerMapping

3.SimpleUrlHandlerMapping

Next, the SimpleUrlHandlerMapping is the most flexible HandlerMapping implementation. It allows for direct and declarative mapping between either bean instances and URLs or between bean names and URLs.

接下来,SimpleUrlHandlerMapping是最灵活的HandlerMapping实现。它允许在Bean实例和URL之间或Bean名称和URL之间进行直接和声明式的映射。

Let’s map requests “/simpleUrlWelcome” and “/*/simpleUrlWelcome” to the “welcome” bean:

让我们把请求“/simpleUrlWelcome”“/*/simpleUrlWelcome”映射到“欢迎”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();
    }
}

Alternatively, here’s the equivalent XML configuration:

或者,这里有同等的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" />

It’s important to note that in the XML configuration, a mapping between “<value>” tag must be done in a form accepted by java.util.Properties class and it should follow the syntax: path= Handler_Bean_Name.

需要注意的是,在XML配置中,“<value>”标签之间的映射必须以java.util.Properties类接受的形式进行,它应该遵循以下语法。path= Handler_Bean_Name

The URL should normally be with a leading slash, however, if the path doesn’t begin with one, Spring MVC adds it automatically.

URL通常应该有一个前导斜杠,然而,如果路径不是以斜杠开头,Spring MVC会自动添加它。

A different way to configure the above example in XML is to use the “props” property instead of “value”. Props have a list of “prop” tag where each defines a mapping where “key” referred to the mapped URL and the value of the tag is the name of the bean.

在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>

The following test case makes sure that requests to “/simpleUrlWelcome” is handled by “WelcomeController” which returns a view name called “welcome” :

下面的测试案例确保对”/simpleUrlWelcome“的请求被”WelcomeController”处理,它返回一个名为“welcome”的视图。

public class SimpleUrlMappingConfigTest {
    // ...

    @Test
    public void whenSimpleUrlMapping_thenMappedOK() {
        mockMvc.perform(get("/simpleUrlWelcome"))
          .andExpect(status().isOk())
          .andExpect(view().name("welcome"));
    }
}

4. ControllerClassNameHandlerMapping (removed in Spring 5)

4.ControllerClassNameHandlerMapping(在Spring 5中删除)

The ControllerClassNameHandlerMapping maps URL to a registered controller bean (or a controller annotated with the @Controller annotation) that has, or starts with, the same name.

ControllerClassNameHandlerMapping将URL映射到注册的控制器Bean(或用@Controller注解的控制器),该控制器具有相同的名字,或以相同的名字开始。

It can be more convenient in many scenarios especially for simple controller implementations that handle a single request type. The convention used by Spring MVC is to use the name of the class and remove the “Controller” suffix, then change the name to a lower case and return it as the mapping with a leading “/”.

在很多情况下,特别是对于处理单一请求类型的简单控制器实现来说,它可能更方便。Spring MVC使用的惯例是使用类的名称,去掉“Controller”后缀,然后将名称改为小写,并将其作为映射返回,前面加一个“/”

For example “WelcomeController” would return as mapping to “/welcome*”, i.e. to any URL that starts with “welcome”.

例如,“WelcomeController”将返回对“/welcome*”的映射,即任何以“welcome”开头的URL。

Let’s configure ControllerClassNameHandlerMapping:

让我们来配置ControllerClassNameHandlerMapping

@Configuration
public class ControllerClassNameHandlerMappingConfig {

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

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

Note that ControllerClassNameHandlerMapping is deprecated from Spring 4.3 in favor of annotation driven handler methods.

请注意,ControllerClassNameHandlerMapping已从Spring 4.3中废弃,转而采用注解驱动的处理方法。

Another important note is that controller names will always be returned in lowercase (minus the “Controller” suffix). So if we have a controller called “WelcomeBaeldungController“, it will only handle requests to “/welcomebaeldung” and not to “/welcomeBaeldung”.

另一个重要的注意点是,控制器名称将总是以小写返回(减去 “Controller “后缀)。因此,如果我们有一个名为”WelcomeBaeldungController“的控制器,它将只处理对“/welcomebaeldung”的请求,而不是对“/welcomeBaeldung”

In both Java config and XML config below, we define ControllerClassNameHandlerMapping bean and register beans for the controllers that we will use to handle requests. We also register a bean of type “WelcomeController” and that bean will handle all requests that start with “/welcome”.

在下面的Java config和XML config中,我们定义了ControllerClassNameHandlerMapping Bean,并为我们将用来处理请求的控制器注册了bean。我们还注册了一个“WelcomeController”类型的bean,该bean将处理所有以“/welcome”开头的请求。

Here’s the equivalent XML configuration:

下面是等效的XML配置。

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

When using the above configuration, requests to “/welcome” will be handled by the “WelcomeController“.

当使用上述配置时,对”/welcome“的请求将由”WelcomeController“处理。

The following code will make sure that requests to “/welcome*” such as “/welcometest” is handled by “WelcomeController” which returns a view name called “welcome“:

下面的代码将确保对”/welcome*”的请求,如”/welcometest“被 “WelcomeController “处理,它返回一个名为”welcome“的视图名称。

public class ControllerClassNameHandlerMappingTest {
    // ...

    @Test
    public void whenControllerClassNameMapping_thenMappedOK() {
        mockMvc.perform(get("/welcometest"))
          .andExpect(status().isOk())
          .andExpect(view().name("welcome"));
    }
}

5. Configuring Priorities

5.配置优先权

Spring MVC framework allows more than one implementation of HandlerMapping interface at the same time.

Spring MVC框架允许同时实现一个以上的HandlerMapping接口。

Let us create a configuration and register two controllers, both mapped to URL “/welcome”, only using different mapping and returning different view names:

让我们创建一个配置并注册两个控制器,都映射到URL”/welcome”,只是使用不同的映射并返回不同的视图名称。

@Configuration
public class HandlerMappingDefaultConfig {

    @Bean("/welcome")
    public BeanNameHandlerMappingController beanNameHandlerMapping() {
        return new BeanNameHandlerMappingController();
    }

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

With no explicit handler mapper registered, a default BeanNameHandlerMapping will be used. Let us assert this behaviour with the test:

在没有明确注册处理程序映射的情况下,将使用一个默认的BeanNameHandlerMapping。让我们通过测试来断言这一行为。

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

If we explicitly register a different handler mapper, the default mapper will be overridden. However, it is interesting to see what happens when two mappers are explicitly registered:

如果我们明确地注册一个不同的处理程序映射器,默认的映射器将被覆盖。然而,有趣的是,当两个映射器被明确注册时,会发生什么。

@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();
    }
}

To get the control over which mapping is used, the priorities are set using setOrder(int order) method. This method takes one int parameter where lower value mean higher priority.

为了获得对使用哪种映射的控制,使用 setOrder(int order)方法设置优先级。这个方法需要一个int参数,数值越低意味着优先级越高。

In XML configuration you can configure priorities by using a property called “order”:

在XML配置中,你可以通过使用一个叫做“order”的属性来配置优先级。

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

Let us add order properties to handler mapping beans, via following beanNameUrlHandlerMapping.setOrder(1) and simpleUrlHandlerMapping.setOrder(0). The lower value of the order property reflects higher precedence. Let us assert new behaviour with the test:

让我们为处理程序映射Bean添加order属性,通过下面的beanNameUrlHandlerMapping.setOrder(1)simpleUrlHandlerMapping.setOrder(0)。顺序属性的低值反映了更高的优先权。让我们用测试来断言新的行为。

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

When testing the above configuration, you see that requests to “/welcome” will be handled by SimpleUrlHandlerMapping bean which calls a SimpleUrlHandlerController and returns simple-url-handler-mapping view. We can easily configure the BeanNameHandlerMapping to take precedence by adjusting accordingly the values of order property.

在测试上述配置时,你会看到对“/welcome”的请求将由SimpleUrlHandlerMapping bean处理,它调用SimpleUrlHandlerController并返回simple-url-handler-mapping视图。我们可以通过相应地调整order属性的值,轻松地配置BeanNameHandlerMapping以获得优先权。

6. Conclusion

6.结论

In this article we discussed how URL mapping are handled in Spring MVC framework by exploring the different implementations in the framework.

在这篇文章中,我们通过探索Spring MVC框架中的不同实现来讨论URL映射是如何处理的。

The code accompanying this article can be found over on GitHub.

本文所附的代码可以在GitHub上找到over