Spring — это слабосвязанная среда Java, означающая, что все объекты не зависят друг от друга и могут легко управляться и модифицироваться. Базовая архитектура в каждом весеннем проекте включает использование контроллеров или REST-контроллеров, любого инструмента сборки, такого как maven, gradle или Groove, СУБД, сервисные уровни и т. д. Кто-то, кто только начинает работать с Spring Framework, скорее всего, столкнется с неоднозначным сопоставлением. При сопоставлении запроса с конкретным контроллером есть вероятность возникновения двусмысленности, если не позаботиться о некоторых вещах.
1. Использование «имя =» вместо «значение =»
Этот тип ошибки очень распространен, поскольку терминология звучит так, как будто он выполняет ту же задачу. Однако, когда вы используете это:
Java
@RequestMapping
(name =
"/getName"
, method = GET)
Свойство name аннотации @RequestMapping только предоставляет имя для вашего сопоставления, но фактически не определяет сопоставление, что приводит к неоднозначному сопоставлению. Решением этой проблемы было бы просто заменить атрибут «имя» атрибутом «значение» следующим образом:
Java
// Changed 'name' to 'value'
@RequestMapping
(value =
"/getName"
, method = GET)
Давайте рассмотрим другой распространенный вариант использования, когда возникает эта двусмысленность:
2. Нет родительского сопоставления в Rest Controller
Допустим, в вашем проекте есть 2 контроллера, а именно: HomeController и TaskController. А как мы знаем, под каждый контроллер мы определили различные маппинги для каждого запроса, полученного от клиента. Теперь, возможно, вы забыли добавить сопоставление поверх вашего контроллера или в сопоставлении поверх вашего контроллера есть опечатка.
Потому что, когда у нас есть более 1 контроллера, лучше добавить родительское сопоставление к контроллеру, и причина этого может заключаться в том, что вы определили так много @RequestMappings внутри контроллера, и некоторые или один из них одинаковы в оба контроллера, тогда это создаст двусмысленность в структуре Spring, потому что он не сможет решить, какому контроллеру следует делегировать вызов! Отсюда снова возникает двусмысленность.
Давайте посмотрим на это с точки зрения кода, чтобы лучше понять, давайте рассмотрим предыдущий пример, где мы сказали, что у нас есть 2 контроллера: HomeController и TaskController.
HomeController.java:
Java
package
com.geeksforgeeks.ambiguousmapping.controller;
import
org.springframework.web.bind.annotation.RequestMapping;
import
org.springframework.web.bind.annotation.RestController;
@RestController
public
class
HomeController {
@RequestMapping
(value =
"/writeArticles"
)
public
String writeArticles() {
return
"5 Articles written on GeeksForGeeks !"
;
}
@RequestMapping
(value =
"/doLaundry"
)
public
String doLaundry() {
return
"Laundry done at 5 PM !"
;
}
TaskController.java:
Java
package
com.geeksforgeeks.ambiguousmapping.controller;
import
org.springframework.web.bind.annotation.RequestMapping;
import
org.springframework.web.bind.annotation.RestController;
@RestController
public
class
TaskController {
@RequestMapping
(value =
"/writeArticles"
)
public
String writetwoMoreArticles() {
return
"2 More Articles written !"
;
}
@RequestMapping
(value =
"/doLaundry"
)
public
String checkLaundry() {
return
"Laundry already done @5 PM !"
;
}
}
Объяснение кода:
Мы создали 2 класса и пометили их аннотацией @Controller, поэтому Spring будет рассматривать их как контроллеры, а @Controller наследуется от @Component, поэтому этот класс будет сканироваться во время сканирования компонентов. И внутри обоих контроллеров мы определили разные методы, имеющие одно и то же сопоставление запросов, что может вызвать двусмысленность. (Имена методов не имеют значения, здесь вызов решается с помощью @RequestMappings). Теперь, когда мы запускаем наше приложение Spring Boot, оно будет вызывать следующие исключения:
1. BeanCreationException
2. Вызвано: IllegalStateException
См. выделенный текст на следующем изображении: «Неоднозначное сопоставление. Не удается сопоставить метод «taskController»
Решение неоднозначного сопоставления
Давайте сделаем это шаг за шагом:
Шаг 1. Перейдите в Spring Initilizr и создайте проект, используя зависимость — Spring WEB.
Шаг 2. Создайте новый пакет. Убедитесь, что имя пакета такое же, как у предыдущего пакета, и добавьте в конце.controller. Потому что Spring рекурсивно сканирует все пакеты.
Шаг 3. Затем создайте 2 новых класса внутри этого пакета, а именно: HomeController и TaskController, и соответственно вставьте приведенный ниже код.
В приведенном ниже коде мы преодолели проблему неоднозначного сопоставления, просто предоставив еще один RequestMapping поверх нашего класса контроллера, который помог бы провести различие между двумя контроллерами и сопоставить запрос с соответствующим контроллером REST. Вам просто нужно добавить еще один @RequestMapping поверх вашего RestController. Давайте посмотрим на код:
HomeController.java:
Java
package
com.geeksforgeeks.ambiguousmapping.controller;
import
org.springframework.web.bind.annotation.RequestMapping;
import
org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping
(value =
"/home"
)
public
class
HomeController {
@RequestMapping
(value =
"/writeArticles"
)
public
String writeArticles() {
return
"5 Articles written on GeeksForGeeks !"
;
}
@RequestMapping
(value =
"/doLaundry"
)
public
String doLaundry() {
return
"Laundry done at 5 PM !"
;
}
}
TaskController.java:
Java
package
com.geeksforgeeks.ambiguousmapping.controller;
import
org.springframework.web.bind.annotation.RequestMapping;
import
org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping
(value =
"/task"
)
public
class
TaskController {
@RequestMapping
(value =
"/writeArticles"
)
public
String writetwoMoreArticles() {
return
"2 More Articles written !"
;
}
@RequestMapping
(value =
"/doLaundry"
)
public
String checkLaundry() {
return
"Laundry already done @5 PM !"
;
}
}
Как видите, мы добавили еще один @RequestMapping поверх обоих наших остальных контроллеров, которые теперь будут вызываться соответствующим образом, и не будет никакой двусмысленности. Итак, теперь давайте перейдем к браузеру и проверим это. Перейдите по адресу: localhost:8080/home/writeArticles и localhost:8080/task/writeArticles (или на настроенный вами порт вместо 8080)
Вы получите следующий вывод:
URL: localhost:8090/home/writeArticles (я настроил свой порт на 8090, поэтому я использую 8090)
URL: localhost:8090/task/writeArticles (я настроил свой порт на 8090, поэтому я использую 8090)