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 .