Ответ 1
Ваше стандартное приложение Spring MVC будет обслуживать все запросы через DispatcherServlet
, который вы зарегистрировали в своем контейнере сервлетов.
DispatcherServlet
просматривает свой ApplicationContext
и, если доступно, ApplicationContext
, зарегистрированный в ContextLoaderListener
для специальных бинов, он должен установить свою логику обслуживания запросов. Эти компоненты описаны в документации.
Возможно, самое важное, бобы типа HandlerMapping
map
входящие запросы к обработчикам и список pre- и постпроцессоров (обработчики-перехватчики) на основе некоторых критериев, детали которых варьируется в зависимости от реализации
HandlerMapping
. Самая популярная реализация поддерживает аннотированные контроллеры, но другие реализации существуют как хорошо.
Javadoc из HandlerMapping
далее описывает, как должны вести себя реализации.
DispatcherServlet
находит все бины этого типа и регистрирует их в некотором порядке (можно настроить). Во время обслуживания запроса DispatcherServlet
проходит через эти объекты HandlerMapping
и тестирует каждый из них с помощью getHandler
, чтобы найти тот, который может обработать входящий запрос, представленный в виде стандарта HttpServletRequest
. Начиная с 4.3.x, если он не находит, он регистрирует предупреждение о том, что вы видите
Не найдено сопоставление для HTTP-запроса с URI
[/some/path]
вDispatcherServlet
с именем SomeName
и либо выдает NoHandlerFoundException
, либо сразу же отправляет ответ с кодом состояния 404 "Не найден".
Почему DispatcherServlet
не нашел HandlerMapping
, который мог бы обработать мой запрос?
Наиболее распространенной реализацией HandlerMapping
является RequestMappingHandlerMapping
, которая обрабатывает регистрацию bean-компонентов @Controller
в качестве обработчиков (на самом деле их аннотированные методы @RequestMapping
). Вы можете либо объявить bean-компонент этого типа самостоятельно (с помощью @Bean
или <bean>
, либо с помощью другого механизма), либо использовать встроенные параметры. Это:
- Аннотируйте свой класс
@Configuration
с помощью@EnableWebMvc
. - Объявите элемент
<mvc:annotation-driven />
в вашей конфигурации XML.
Как описано в приведенной выше ссылке, оба из них зарегистрируют bean-компонент RequestMappingHandlerMapping
(и множество других вещей). Однако HandlerMapping
не очень полезен без обработчика. RequestMappingHandlerMapping
ожидает некоторые bean-компоненты @Controller
, поэтому вам нужно также объявить их с помощью методов @Bean
в конфигурации Java или объявлений <bean>
в конфигурации XML или путем сканирования компонентов аннотированных классов @Controller
в обоих. Убедитесь, что эти бобы присутствуют.
Если вы получаете предупреждающее сообщение и номер 404 и правильно настроили все вышеперечисленное, вы отправляете запрос на неправильный URI, который не обрабатывается обнаруженным @RequestMapping
аннотированный метод обработчика.
Библиотека spring-webmvc
предлагает другие встроенные реализации HandlerMapping
. Например, BeanNameUrlHandlerMapping
карты
от URL до бобов с именами, начинающимися с косой черты ("/")
и вы всегда можете написать свой. Очевидно, вы должны убедиться, что отправляемый вами запрос соответствует хотя бы одному из зарегистрированных обработчиков объектов HandlerMapping
.
Если вы неявно или явно не регистрируете какие-либо bean-компоненты HandlerMapping
(или если detectAllHandlerMappings
равно true
), DispatcherServlet
регистрирует некоторые значения по умолчанию. Они определены в DispatcherServlet.properties
в том же пакете, что и класс DispatcherServlet
. Это BeanNameUrlHandlerMapping
и DefaultAnnotationHandlerMapping
(что аналогично RequestMappingHandlerMapping
, но не рекомендуется).
Отладка
Spring MVC будет регистрировать обработчики, зарегистрированные через RequestMappingHandlerMapping
. Например, @Controller
как
@Controller
public class ExampleController {
@RequestMapping(path = "/example", method = RequestMethod.GET, headers = "X-Custom")
public String example() {
return "example-view-name";
}
}
будет регистрировать следующее на уровне INFO
Mapped "{[/example],methods=[GET],headers=[X-Custom]}" onto public java.lang.String com.spring.servlet.ExampleController.example()
Это описание зарегистрированного отображения. Когда вы увидите предупреждение о том, что обработчик не найден, сравните URI в сообщении с отображением, указанным здесь. Все ограничения, указанные в @RequestMapping
, должны соответствовать Spring MVC для выбора обработчика.
Другие реализации HandlerMapping
регистрируют свои собственные операторы, которые должны указывать на их отображения и соответствующие обработчики.
Аналогично, включите ведение журнала Spring на уровне DEBUG, чтобы увидеть, какие bean-компоненты регистрируются Spring. Он должен сообщить, какие аннотированные классы он находит, какие пакеты он сканирует и какие компоненты он инициализирует. Если ожидаемых вами нет, просмотрите конфигурацию ApplicationContext
.
Другие распространенные ошибки
DispatcherServlet
- это просто типичный Java EE Servlet
. Вы регистрируете его в своих типичных декларациях <web.xml>
<servlet-class>
и <servlet-mapping>
, либо напрямую через ServletContext#addServlet
в WebApplicationInitializer
, либо с помощью любого другого механизма, используемого Spring boot. Таким образом, вы должны полагаться на логику отображения URL, указанную в спецификации сервлета, см. главу 12. См. также
Учитывая это, распространенной ошибкой является регистрация DispatcherServlet
с отображением URL-адреса /*
, возвращением имени представления из метода-обработчика @RequestMapping
и ожиданием визуализации JSP. Например, рассмотрим метод-обработчик, например
@RequestMapping(path = "/example", method = RequestMethod.GET)
public String example() {
return "example-view-name";
}
с InternalResourceViewResolver
@Bean
public InternalResourceViewResolver resolver() {
InternalResourceViewResolver vr = new InternalResourceViewResolver();
vr.setPrefix("/WEB-INF/jsps/");
vr.setSuffix(".jsp");
return vr;
}
вы можете ожидать, что запрос будет перенаправлен ресурсу JSP по пути /WEB-INF/jsps/example-view-name.jsp
. Этого не произойдет. Вместо этого, принимая имя контекста Example
, DisaptcherServlet
сообщит
Не найдено сопоставление для HTTP-запроса с URI
[/Example/WEB-INF/jsps/example-view-name.jsp]
вDispatcherServlet
с именем "диспетчер"
Поскольку DispatcherServlet
сопоставлен с /*
и /*
соответствует всему (кроме точных совпадений, которые имеют более высокий приоритет), будет выбран DispatcherServlet
для обработки forward
из JstlView
(возвращается InternalResourceViewResolver
). Почти в каждом случае DispatcherServlet
не будет настроен для обработки такого запроса.
Вместо этого в этом упрощенном случае вы должны зарегистрировать DispatcherServlet
в /
, отметив его как сервлет по умолчанию. Сервлет по умолчанию - это последнее совпадение для запроса. Это позволит вашему типичному сервлет-контейнеру выбрать внутреннюю реализацию сервлета, сопоставленную с *.jsp
, для обработки ресурса JSP (например, Tomcat имеет JspServlet
), прежде чем пытаться использовать сервлет по умолчанию.
Это то, что вы видите в своем примере.