«1. Введение

В Spring MVC DispatcherServlet действует как передний контроллер — получает все входящие HTTP-запросы и обрабатывает их.

Проще говоря, обработка происходит путем передачи запросов соответствующему компоненту с помощью сопоставлений обработчиков.

HandlerMapping — это интерфейс, определяющий сопоставление между запросами и объектами обработчика. В то время как среда Spring MVC предоставляет некоторые готовые реализации, интерфейс может быть реализован разработчиками для обеспечения индивидуальной стратегии сопоставления.

В этой статье обсуждаются некоторые реализации, предоставляемые Spring MVC, а именно BeanNameUrlHandlerMapping, SimpleUrlHandlerMapping, ControllerClassNameHandlerMapping, их конфигурация и различия между ними.

2. BeanNameUrlHandlerMapping

BeanNameUrlHandlerMapping — это реализация HandlerMapping по умолчанию. BeanNameUrlHandlerMapping сопоставляет URL-адреса запроса с bean-компонентами с тем же именем.

Это конкретное сопоставление поддерживает прямое сопоставление имен, а также сопоставление с шаблоном с использованием шаблона «*».

Например, входящий URL-адрес «/foo» сопоставляется с bean-компонентом с именем «/foo». Примером сопоставления шаблонов является сопоставление запросов к «/foo*» бинам с именами, начинающимися с «/foo», например «/foo2/» или «/fooOne/».

Давайте настроим этот пример здесь и зарегистрируем контроллер компонента, который обрабатывает запросы к «/beanNameUrl»:

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

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

Это XML-эквивалент приведенной выше конфигурации на основе Java:

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

Это важно отметить, что в обеих этих конфигурациях определение bean-компонента для BeanNameUrlHandlerMapping не требуется, поскольку оно предоставляется Spring MVC. Удаление этого определения bean-компонента не вызовет проблем, и запросы по-прежнему будут сопоставляться с их зарегистрированными bean-компонентами-обработчиками.

Теперь все запросы к «/beanNameUrl» будут перенаправлены DispatcherServlet на «WelcomeController». WelcomeController возвращает имя представления, которое называется «добро пожаловать».

Следующий код проверяет эту конфигурацию и убеждается, что возвращается правильное имя представления:

public class BeanNameMappingConfigTest {
    // ...

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

3. SimpleUrlHandlerMapping

Далее, SimpleUrlHandlerMapping является наиболее гибкой реализацией HandlerMapping. Он позволяет осуществлять прямое и декларативное сопоставление либо между экземплярами компонентов и URL-адресами, либо между именами компонентов и URL-адресами.

Давайте сопоставим запросы «/simpleUrlWelcome» и «/*/simpleUrlWelcome» с bean-компонентом «welcome»:

@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 сопоставление между тегом «\u003cvalue\u003e» должно быть выполнено в форме, принятой классом java.util.Properties, и должно соответствовать синтаксису: path= Handler_Bean_Name.

Обычно URL-адрес должен начинаться с косой черты, однако, если путь не начинается с косой черты, Spring MVC добавляет ее автоматически.

Другой способ настроить приведенный выше пример в XML — использовать свойство «props» вместо «value». У свойств есть список тегов «prop», каждый из которых определяет сопоставление, где «ключ» относится к сопоставленному URL-адресу, а значением тега является имя компонента.

<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 (удален in Spring 5)

ControllerClassNameHandlerMapping сопоставляет URL-адрес с зарегистрированным bean-компонентом контроллера (или контроллером, аннотированным аннотацией @Controller), который имеет или начинается с того же имени.

Это может быть удобнее во многих сценариях, особенно для простых реализаций контроллера, обрабатывающих один тип запроса. Соглашение, используемое Spring MVC, состоит в том, чтобы использовать имя класса и удалить суффикс «Контроллер», затем изменить имя на нижний регистр и вернуть его как сопоставление с ведущим «/».

Например, «WelcomeController» вернется как сопоставление с «/welcome*», то есть с любым URL-адресом, начинающимся с «welcome».

Давайте настроим ControllerClassNameHandlerMapping:

@Configuration
public class ControllerClassNameHandlerMappingConfig {

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

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

«

«Обратите внимание, что ControllerClassNameHandlerMapping устарел из Spring 4.3 в пользу методов обработчика, управляемых аннотациями.

Еще одно важное замечание: имена контроллеров всегда будут возвращаться в нижнем регистре (без суффикса «Контроллер»). Итак, если у нас есть контроллер с именем «WelcomeBaeldungController», он будет обрабатывать запросы только к «/welcomeBaeldung», а не к «/welcomeBaeldung».

Как в конфигурации Java, так и в конфигурации XML ниже мы определяем bean-компонент ControllerClassNameHandlerMapping и регистрируем bean-компоненты для контроллеров, которые мы будем использовать для обработки запросов. Мы также регистрируем bean-компонент типа «WelcomeController», и этот bean-компонент будет обрабатывать все запросы, начинающиеся с «/welcome».

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

Вот эквивалентная конфигурация XML:

При использовании приведенной выше конфигурации запросы к «/welcome» будут обрабатываться «WelcomeController».

public class ControllerClassNameHandlerMappingTest {
    // ...

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

Следующий код гарантирует, что запросы к «/welcome*», такие как «/welcometest», обрабатываются «WelcomeController», который возвращает имя представления «welcome»:

5. Настройка приоритетов

Среда Spring MVC позволяет использовать более одной реализации интерфейса HandlerMapping одновременно.

@Configuration
public class HandlerMappingDefaultConfig {

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

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

Давайте создадим конфигурацию и зарегистрируем два контроллера, оба сопоставлены с URL-адресом «/welcome», только используя разные сопоставления и возвращая разные имена представлений: будет использоваться. Давайте проверим это поведение с помощью теста:

@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). Этот метод принимает один целочисленный параметр, где более низкое значение означает более высокий приоритет.

В конфигурации XML вы можете настроить приоритеты, используя свойство под названием «порядок»:

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

Давайте добавим свойства порядка к bean-компонентам сопоставления обработчиков, используя следующие 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"));
}

При тестировании приведенной выше конфигурации вы видите, что запросы к «/welcome» будут обрабатываться bean-компонентом SimpleUrlHandlerMapping, который вызывает SimpleUrlHandlerController и возвращает simple-url-handler- картографический вид. Мы можем легко настроить BeanNameHandlerMapping так, чтобы он имел приоритет, соответствующим образом изменив значения свойства order.

6. Заключение

В этой статье мы обсудили, как сопоставление URL-адресов обрабатывается в среде Spring MVC, исследуя различные реализации в среде.

Код, сопровождающий эту статью, можно найти на GitHub.