Перейти к основному содержимому

Руководство по сопоставлению обработчиков Spring

· 6 мин. чтения

1. Введение

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

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

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

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

2. Отображение URL-адреса-обработчика-компонента

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

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

Например, входящий URL -адрес «/foo» сопоставляется с bean-компонентом с именем «/foo» . Примером сопоставления шаблонов является сопоставление запросов к «/foo*» с bean-компонентами с именами, начинающимися с «/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.foreach.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. Сопоставление SimpleUrlHandler

Далее, 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.foreach.WelcomeController" />

Важно отметить, что в конфигурации XML сопоставление между тегом «<value>» должно быть выполнено в форме, принятой классом 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 (удален в 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 в пользу методов обработчика, управляемых аннотациями.

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

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

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

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

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

Следующий код гарантирует, что запросы к «/ welcome *», такие как «/ welcometest », обрабатываются «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-адресом «/ приветствие», только используя разные сопоставления и возвращая разные имена представлений:

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

В конфигурации 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-mapping . Мы можем легко настроить BeanNameHandlerMapping так , чтобы он имел приоритет, соответствующим образом изменив значения свойства order .

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

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

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